mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
Merge branch 'hovudstraum' into add-polish-translation
Signed-off-by: ed <s@ocv.me>
This commit is contained in:
commit
3a3556dca7
50
README.md
50
README.md
|
@ -147,6 +147,8 @@ made in Norway 🇳🇴
|
|||
|
||||
just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
||||
|
||||
> ℹ️ the sfx is a [self-extractor](https://github.com/9001/copyparty/issues/270) which unpacks an embedded `tar.gz` into `$TEMP` -- if this looks too scary, you can use the [zipapp](#zipapp) which has slightly worse performance
|
||||
|
||||
* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
|
||||
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
||||
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
||||
|
@ -435,6 +437,7 @@ upgrade notes
|
|||
|
||||
* can I link someone to a password-protected volume/file by including the password in the URL?
|
||||
* yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
|
||||
* if you have enabled `--usernames` then do `?pw=username:password` instead
|
||||
|
||||
* how do I stop `.hist` folders from appearing everywhere on my HDD?
|
||||
* by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)
|
||||
|
@ -513,12 +516,17 @@ anyone trying to bruteforce a password gets banned according to `--ban-pw`; defa
|
|||
|
||||
and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
|
||||
|
||||
* you can also `PRTY_CONFIG=foobar.conf python copyparty-sfx.py` (convenient in docker etc)
|
||||
|
||||
```yaml
|
||||
[accounts]
|
||||
u1: p1 # create account "u1" with password "p1"
|
||||
u2: p2 # (note that comments must have
|
||||
u3: p3 # two spaces before the # sign)
|
||||
|
||||
[groups]
|
||||
g1: u1, u2 # create a group
|
||||
|
||||
[/] # this URL will be mapped to...
|
||||
/srv # ...this folder on the server filesystem
|
||||
accs:
|
||||
|
@ -528,6 +536,7 @@ and if you want to use config files instead of commandline args (good!) then her
|
|||
/mnt/music # which is mapped to this folder
|
||||
accs:
|
||||
r: u1, u2 # only these accounts can read,
|
||||
r: @g1 # (exactly the same, just with a group instead)
|
||||
rw: u3 # and only u3 can read-write
|
||||
|
||||
[/inc]
|
||||
|
@ -1008,6 +1017,7 @@ a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3
|
|||
url parameters:
|
||||
|
||||
* `pw=hunter2` for password auth
|
||||
* if you enabled `--usernames` then do `pw=username:password` instead
|
||||
* `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)
|
||||
|
@ -1084,6 +1094,9 @@ open the `[🎺]` media-player-settings tab to configure it,
|
|||
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
||||
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the most part
|
||||
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
||||
* `[flac]` -- lossless but compressed, for LAN and/or fiber playback on electrostatic headphones
|
||||
* `[wav]` -- lossless and uncompressed, for LAN and/or fiber playback on electrostatic headphones connected to very old equipment
|
||||
* `flac` and `wav` must be enabled with `--allow-flac` / `--allow-wav` to allow spending the disk space
|
||||
* "tint" reduces the contrast of the playback bar
|
||||
|
||||
|
||||
|
@ -1218,7 +1231,7 @@ using arguments or config files, or a mix of both:
|
|||
|
||||
**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with `--help` to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in `--help-flags` can be used in volumes as volflags.
|
||||
* if running in docker/podman, try this: `docker run --rm -it copyparty/ac --help`
|
||||
* or see this (probably outdated): https://ocv.me/copyparty/helptext.html
|
||||
* or see this: https://ocv.me/copyparty/helptext.html
|
||||
* or if you prefer plaintext, https://ocv.me/copyparty/helptext.txt
|
||||
|
||||
|
||||
|
@ -1290,6 +1303,7 @@ an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit T
|
|||
* if you enable both `ftp` and `ftps`, the port-range will be divided in half
|
||||
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
||||
* login with any username + your password, or put your password in the username field
|
||||
* unless you enabled `--usernames`
|
||||
|
||||
some recommended FTP / FTPS clients; `wark` = example password:
|
||||
* https://winscp.net/eng/download.php
|
||||
|
@ -1307,6 +1321,7 @@ click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to se
|
|||
|
||||
general usage:
|
||||
* login with any username + your password, or put your password in the username field (password field can be empty/whatever)
|
||||
* unless you enabled `--usernames`
|
||||
|
||||
on macos, connect from finder:
|
||||
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
||||
|
@ -1322,6 +1337,7 @@ using the GUI (winXP or later):
|
|||
* rightclick [my computer] -> [map network drive] -> Folder: `http://192.168.123.1:3923/`
|
||||
* on winXP only, click the `Sign up for online storage` hyperlink instead and put the URL there
|
||||
* providing your password as the username is recommended; the password field can be anything or empty
|
||||
* unless you enabled `--usernames`
|
||||
|
||||
the webdav client that's built into windows has the following list of bugs; you can avoid all of these by connecting with rclone instead:
|
||||
* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password
|
||||
|
@ -1379,6 +1395,7 @@ some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
|
|||
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
|
||||
* account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
|
||||
* [shadowing](#shadowing) probably works as expected but no guarantees
|
||||
* not compatible with pw-hashing or `--usernames`
|
||||
|
||||
and some minor issues,
|
||||
* clients only see the first ~400 files in big folders;
|
||||
|
@ -1425,6 +1442,8 @@ note that this disables hotlinking because the opengraph spec demands it; to sne
|
|||
|
||||
you can also hotlink files regardless by appending `?raw` to the url
|
||||
|
||||
> WARNING: if you plan to use WebDAV, then `--og-ua` / `og_ua` must be configured
|
||||
|
||||
if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
|
||||
|
||||
|
||||
|
@ -1881,6 +1900,8 @@ you can disable the built-in password-based login system, and instead replace it
|
|||
|
||||
* the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
|
||||
|
||||
* if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party
|
||||
|
||||
some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
|
||||
|
||||
there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)
|
||||
|
@ -2043,7 +2064,11 @@ you can either:
|
|||
* or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
|
||||
* if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
|
||||
|
||||
when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP. Look out for red and yellow log messages which explain how to do this. But basically, set `--xff-hdr` to the name of the http header to read the IP from (usually `x-forwarded-for`, but cloudflare uses `cf-connecting-ip`), and then `--xff-src` to the IP of the reverse-proxy so copyparty will trust the xff-hdr. Note that `--rp-loc` in particular will not work at all unless you do this
|
||||
when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP. The best/safest approach is to configure your reverse-proxy so it gives copyparty a header which only contains the client's true/real IP-address, and then setting `--xff-hdr theHeaderName --rproxy 1` but alternatively, if you want/need to let copyparty handle this, look out for red and yellow log messages which explain how to do that. Basically, the log will say this:
|
||||
|
||||
> set `--xff-hdr` to the name of the http-header to read the IP from (usually `x-forwarded-for`, but cloudflare uses `cf-connecting-ip`), and then `--xff-src` to the IP of the reverse-proxy so copyparty will trust the xff-hdr. You will also need to configure `--rproxy` to `1` if the header only contains one IP (the correct one) or to a *negative value* if it contains multiple; `-1` being the rightmost and most trusted IP (the nearest proxy, so usually not the correct one), `-2` being the second-closest hop, and so on
|
||||
|
||||
Note that `--rp-loc` in particular will not work at all unless you configure the above correctly
|
||||
|
||||
some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which *could* be a nice speed boost, depending on a lot of factors
|
||||
* **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
|
||||
|
@ -2262,11 +2287,9 @@ if your distro/OS is not mentioned below, there might be some hints in the [«on
|
|||
|
||||
`pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
|
||||
|
||||
it comes with a [systemd service](./contrib/package/arch/copyparty.service) and expects to find one or more [config files](./docs/example.conf) in `/etc/copyparty.d/`
|
||||
it comes with a [systemd service](./contrib/systemd/copyparty@.service) as well as a [user service](./contrib/systemd/copyparty-user.service), and expects to find a [config file](./contrib/systemd/copyparty.example.conf) in `/etc/copyparty/copyparty.conf` or `~/.config/copyparty/copyparty.conf`
|
||||
|
||||
after installing it, you may want to `cp /usr/lib/systemd/system/copyparty.service /etc/systemd/system/` and then `vim /etc/systemd/system/copyparty.service` to change what user/group it is running as (you only need to do this once)
|
||||
|
||||
NOTE: there used to be an aur package; this evaporated when copyparty was adopted by the official archlinux repos. If you're still using the aur package, please move
|
||||
after installing, start either the system service or the user service and navigate to http://127.0.0.1:3923 for further instructions (unless you already edited the config files, in which case you are good to go, probably)
|
||||
|
||||
|
||||
## fedora package
|
||||
|
@ -2429,6 +2452,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|||
| **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
|
||||
| **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
|
||||
| **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
|
||||
| **Nintendo Wii (Opera 9.0 "Internet Channel")** | can browse, can't upload or download (no local storage), can view images - works best with `?b=u`, default view broken |
|
||||
|
||||
<p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
|
||||
|
||||
|
@ -2488,6 +2512,8 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
|
|||
|
||||
> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)
|
||||
|
||||
* unless you've enabled `--usernames`, then it's `PW: usr:pwd`, cookie `cppwd=usr:pwd`, url-param `?pw=usr:pwd`
|
||||
|
||||
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
||||
|
||||
|
||||
|
@ -2599,7 +2625,7 @@ there is a [discord server](https://discord.gg/25J8CdTT6G) with an `@everyone`
|
|||
|
||||
some notes on hardening
|
||||
|
||||
* set `--rproxy 0` if your copyparty is directly facing the internet (not through a reverse-proxy)
|
||||
* set `--rproxy 0` *if and only if* your copyparty is directly facing the internet (not through a reverse-proxy)
|
||||
* cors doesn't work right otherwise
|
||||
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
|
||||
* this returns html documents as plaintext, and also disables markdown rendering
|
||||
|
@ -2699,6 +2725,12 @@ optionally also specify `--ah-cli` to enter an interactive mode where it will ha
|
|||
|
||||
the default configs take about 0.4 sec and 256 MiB RAM to process a new password on a decent laptop
|
||||
|
||||
when generating hashes using `--ah-cli` for docker or systemd services, make sure it is using the same `--ah-salt` by:
|
||||
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
||||
* setting the same `--ah-salt` in both environments
|
||||
|
||||
> ⚠️ if you have enabled `--usernames` then provide the password as `username:password` when hashing it, for example `ed:hunter2`
|
||||
|
||||
|
||||
## https
|
||||
|
||||
|
@ -2816,6 +2848,8 @@ these are standalone programs and will never be imported / evaluated by copypart
|
|||
|
||||
the self-contained "binary" (recommended!) [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
|
||||
|
||||
if you only need english, [copyparty-en.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.py) is the same thing but smaller
|
||||
|
||||
you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
|
||||
|
||||
|
||||
|
@ -2843,7 +2877,7 @@ then again, if you are already into downloading shady binaries from the internet
|
|||
|
||||
## zipapp
|
||||
|
||||
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
||||
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but, unlike the sfx, it is a completely normal zipfile which does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
||||
|
||||
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ if PY2:
|
|||
|
||||
sys.dont_write_bytecode = True
|
||||
bytes = str
|
||||
files_decoder = lambda s: unicode(s, 'utf8')
|
||||
files_decoder = lambda s: unicode(s, "utf8")
|
||||
else:
|
||||
from urllib.parse import quote_from_bytes as quote
|
||||
from urllib.parse import unquote_to_bytes as unquote
|
||||
|
|
|
@ -1,57 +1,48 @@
|
|||
# Maintainer: icxes <dev.null@need.moe>
|
||||
# Contributor: Morgan Adamiec <morganamilo@archlinux.org>
|
||||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.18.9"
|
||||
pkgver="1.19.0"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
url="https://github.com/9001/${pkgname}"
|
||||
license=('MIT')
|
||||
depends=("python" "lsof" "python-jinja")
|
||||
depends=("bash" "python" "lsof" "python-jinja")
|
||||
makedepends=("python-wheel" "python-setuptools" "python-build" "python-installer" "make" "pigz")
|
||||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"cfssl: generate TLS certificates on startup (pointless when reverse-proxied)"
|
||||
"python-mutagen: music tags (alternative)"
|
||||
"python-pillow: thumbnails for images"
|
||||
"python-pyvips: thumbnails for images (higher quality, faster, uses more ram)"
|
||||
"libkeyfinder-git: detection of musical keys"
|
||||
"qm-vamp-plugins: BPM detection"
|
||||
"python-pyopenssl: ftps functionality"
|
||||
"python-pyzmq: send zeromq messages from event-hooks"
|
||||
"python-argon2-cffi: hashed passwords in config"
|
||||
"python-impacket-git: smb support (bad idea)"
|
||||
"cfssl: generate TLS certificates on startup"
|
||||
"python-mutagen: music tags (alternative)"
|
||||
"python-pillow: thumbnails for images"
|
||||
"python-pyvips: thumbnails for images (higher quality, faster, uses more ram)"
|
||||
"libkeyfinder: detection of musical keys"
|
||||
"python-pyopenssl: ftps functionality"
|
||||
"python-pyzmq: send zeromq messages from event-hooks"
|
||||
"python-argon2-cffi: hashed passwords in config"
|
||||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}.d/init" )
|
||||
sha256sums=("d5d33b50d6717e52427956beb1687061a6f28b467997506505151e3ae18c58e5")
|
||||
backup=("etc/${pkgname}/copyparty.conf" )
|
||||
sha256sums=("179b027d51e4fe7ebdab2b18c07475d52c57e2ce69256292b157a8efacd82118")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
make
|
||||
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
|
||||
pushd copyparty/web
|
||||
make -j$(nproc)
|
||||
rm Makefile
|
||||
popd
|
||||
|
||||
python3 -m build -wn
|
||||
python -m build --wheel --no-isolation
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
python3 -m installer -d "$pkgdir" dist/*.whl
|
||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
||||
|
||||
install -dm755 "${pkgdir}/etc/${pkgname}.d"
|
||||
install -dm755 "${pkgdir}/etc/${pkgname}"
|
||||
install -Dm755 "bin/prisonparty.sh" "${pkgdir}/usr/bin/prisonparty"
|
||||
install -Dm644 "contrib/package/arch/${pkgname}.conf" "${pkgdir}/etc/${pkgname}.d/init"
|
||||
install -Dm644 "contrib/package/arch/${pkgname}.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}.service"
|
||||
install -Dm644 "contrib/package/arch/prisonparty.service" "${pkgdir}/usr/lib/systemd/system/prisonparty.service"
|
||||
install -Dm644 "contrib/package/arch/index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm644 "contrib/systemd/${pkgname}.conf" "${pkgdir}/etc/${pkgname}/copyparty.conf"
|
||||
install -Dm644 "contrib/systemd/${pkgname}@.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}@.service"
|
||||
install -Dm644 "contrib/systemd/${pkgname}-user.service" "${pkgdir}/usr/lib/systemd/user/${pkgname}.service"
|
||||
install -Dm644 "contrib/systemd/prisonparty@.service" "${pkgdir}/usr/lib/systemd/system/prisonparty@.service"
|
||||
install -Dm644 "contrib/systemd/index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
|
||||
find /etc/${pkgname}.d -iname '*.conf' 2>/dev/null | grep -qE . && return
|
||||
echo "┏━━━━━━━━━━━━━━━──-"
|
||||
echo "┃ Configure ${pkgname} by adding .conf files into /etc/${pkgname}.d/"
|
||||
echo "┃ and maybe copy+edit one of the following to /etc/systemd/system/:"
|
||||
echo "┣━♦ /usr/lib/systemd/system/${pkgname}.service (standard)"
|
||||
echo "┣━♦ /usr/lib/systemd/system/prisonparty.service (chroot)"
|
||||
echo "┗━━━━━━━━━━━━━━━──-"
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
## import all *.conf files from the current folder (/etc/copyparty.d)
|
||||
% ./
|
||||
|
||||
# add additional .conf files to this folder;
|
||||
# see example config files for reference:
|
||||
# https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf
|
||||
# https://github.com/9001/copyparty/tree/hovudstraum/docs/copyparty.d
|
|
@ -1,32 +0,0 @@
|
|||
# this will start `/usr/bin/copyparty-sfx.py`
|
||||
# and read config from `/etc/copyparty.d/*.conf`
|
||||
#
|
||||
# you probably want to:
|
||||
# change "User=cpp" and "/home/cpp/" to another user
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
SyslogIdentifier=copyparty
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# user to run as + where the TLS certificate is (if any)
|
||||
User=cpp
|
||||
Environment=XDG_CONFIG_HOME=/home/cpp/.config
|
||||
|
||||
# stop systemd-tmpfiles-clean.timer from deleting copyparty while it's running
|
||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/usr/bin/python3 /usr/bin/copyparty -c /etc/copyparty.d/init
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,3 +0,0 @@
|
|||
this is `/var/lib/copyparty-jail`, the fallback webroot when copyparty has not yet been configured
|
||||
|
||||
please add some `*.conf` files to `/etc/copyparty.d/`
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.18.9
|
||||
pkgver=1.19.0
|
||||
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=("d5d33b50d6717e52427956beb1687061a6f28b467997506505151e3ae18c58e5")
|
||||
sha256sums=("179b027d51e4fe7ebdab2b18c07475d52c57e2ce69256292b157a8efacd82118")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.18.9/copyparty-sfx.py",
|
||||
"version": "1.18.9",
|
||||
"hash": "sha256-R1OVx4f8GERAG80ZcHAIP6HK2TlBbKJZpvnJmJbGPRY="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.19.0/copyparty-sfx.py",
|
||||
"version": "1.19.0",
|
||||
"hash": "sha256-9A+zPtkVtUuGHB/JJV3fhVtJderLUGxHqvuJQz0/1+Q="
|
||||
}
|
26
contrib/systemd/copyparty-user.service
Normal file
26
contrib/systemd/copyparty-user.service
Normal file
|
@ -0,0 +1,26 @@
|
|||
# this will start `/usr/bin/copyparty`
|
||||
# and read config from `$HOME/.config/copyparty.conf`
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
SyslogIdentifier=copyparty
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
Environment=PRTY_CONFIG=%h/.config/copyparty/copyparty.conf
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# ensure there is a config
|
||||
ExecStartPre=/bin/bash -c 'if [[ ! -f %h/.config/copyparty/copyparty.conf ]]; then mkdir -p %h/.config/copyparty; cp /etc/copyparty/copyparty.conf %h/.config/copyparty/copyparty.conf; fi'
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/usr/bin/python3 /usr/bin/copyparty
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
|
@ -1,42 +1,13 @@
|
|||
# not actually YAML but lets pretend:
|
||||
# -*- mode: yaml -*-
|
||||
# vim: ft=yaml:
|
||||
|
||||
|
||||
# put this file in /etc/
|
||||
|
||||
|
||||
[global]
|
||||
e2dsa # enable file indexing and filesystem scanning
|
||||
e2ts # and enable multimedia indexing
|
||||
ansi # and colors in log messages
|
||||
|
||||
# disable logging to stdout/journalctl and log to a file instead;
|
||||
# $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
|
||||
|
||||
i: 127.0.0.1
|
||||
|
||||
[accounts]
|
||||
ed: wark # username: password
|
||||
user: password
|
||||
|
||||
|
||||
[/] # create a volume at "/" (the webroot), which will
|
||||
/mnt # share the contents of the "/mnt" folder
|
||||
[/]
|
||||
/var/lib/copyparty-jail
|
||||
accs:
|
||||
rw: * # everyone gets read-write access, but
|
||||
rwmda: ed # the user "ed" gets read-write-move-delete-admin
|
||||
r: *
|
||||
rwdma: user
|
||||
flags:
|
||||
grid
|
42
contrib/systemd/copyparty.example.conf
Normal file
42
contrib/systemd/copyparty.example.conf
Normal file
|
@ -0,0 +1,42 @@
|
|||
# not actually YAML but lets pretend:
|
||||
# -*- mode: yaml -*-
|
||||
# vim: ft=yaml:
|
||||
|
||||
|
||||
# put this file in /etc/
|
||||
|
||||
|
||||
[global]
|
||||
e2dsa # enable file indexing and filesystem scanning
|
||||
e2ts # and enable multimedia indexing
|
||||
ansi # and colors in log messages
|
||||
|
||||
# disable logging to stdout/journalctl and log to a file instead;
|
||||
# $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
|
||||
/mnt # share the contents of the "/mnt" folder
|
||||
accs:
|
||||
rw: * # everyone gets read-write access, but
|
||||
rwmda: ed # the user "ed" gets read-write-move-delete-admin
|
30
contrib/systemd/copyparty@.service
Normal file
30
contrib/systemd/copyparty@.service
Normal file
|
@ -0,0 +1,30 @@
|
|||
# this will start `/usr/bin/copyparty`
|
||||
# and read config from `/etc/copyparty/copyparty.conf`
|
||||
#
|
||||
# the %i refers to whatever you put after the copyparty@
|
||||
# so with copyparty@foo.service, %i == foo
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
SyslogIdentifier=copyparty
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
Environment=PRTY_CONFIG=/etc/copyparty/copyparty.conf
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# user to run as + where the TLS certificate is (if any)
|
||||
User=%i
|
||||
Environment=XDG_CONFIG_HOME=/home/%i/.config
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/usr/bin/python3 /usr/bin/copyparty
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
contrib/systemd/index.md
Normal file
10
contrib/systemd/index.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
this is `/var/lib/copyparty-jail`, the fallback webroot when copyparty has not yet been configured
|
||||
|
||||
please edit `/etc/copyparty/copyparty.conf` (if running as a system service)
|
||||
or `$HOME/.config/copyparty/copyparty.conf` if running as a user service
|
||||
|
||||
a basic configuration example is available at https://github.com/9001/copyparty/blob/hovudstraum/contrib/systemd/copyparty.example.conf
|
||||
a configuration example that explains most flags is available at https://github.com/9001/copyparty/blob/hovudstraum/docs/chungus.conf
|
||||
|
||||
the full list of configuration options can be seen at https://ocv.me/copyparty/helptext.html
|
||||
or by running `copyparty --help`
|
|
@ -1,11 +1,13 @@
|
|||
# this will start `/usr/bin/copyparty-sfx.py`
|
||||
# this will start `/usr/bin/copyparty`
|
||||
# in a chroot, preventing accidental access elsewhere,
|
||||
# and read copyparty config from `/etc/copyparty.d/*.conf`
|
||||
# and read copyparty config from `/etc/copyparty/copyparty.conf`
|
||||
#
|
||||
# expose additional filesystem locations to copyparty
|
||||
# by listing them between the last `cpp` and `--`
|
||||
# by listing them between the last `%i` and `--`
|
||||
#
|
||||
# `cpp cpp` = user/group to run copyparty as; can be IDs (1000 1000)
|
||||
# `%i %i` = user/group to run copyparty as; can be IDs (1000 1000)
|
||||
# the %i refers to whatever you put after the prisonparty@
|
||||
# so with prisonparty@foo.service, %i == foo
|
||||
#
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
|
@ -15,19 +17,22 @@
|
|||
Description=copyparty file server
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
SyslogIdentifier=prisonparty
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
WorkingDirectory=/var/lib/copyparty-jail
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
Environment=PRTY_CONFIG=/etc/copyparty/copyparty.conf
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
|
||||
# stop systemd-tmpfiles-clean.timer from deleting copyparty while it's running
|
||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
# user to run as + where the TLS certificate is (if any)
|
||||
User=%i
|
||||
Environment=XDG_CONFIG_HOME=/home/%i/.config
|
||||
|
||||
# run copyparty
|
||||
ExecStart=/bin/bash /usr/bin/prisonparty /var/lib/copyparty-jail cpp cpp \
|
||||
/etc/copyparty.d \
|
||||
ExecStart=/bin/bash /usr/bin/prisonparty /var/lib/copyparty-jail %i %i \
|
||||
/etc/copyparty \
|
||||
-- \
|
||||
/usr/bin/python3 /usr/bin/copyparty -c /etc/copyparty.d/init
|
||||
/usr/bin/python3 /usr/bin/copyparty
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -63,10 +63,6 @@ web/browser.js
|
|||
web/browser2.html
|
||||
web/cf.html
|
||||
web/copyparty.gif
|
||||
web/dd/2.png
|
||||
web/dd/3.png
|
||||
web/dd/4.png
|
||||
web/dd/5.png
|
||||
web/deps/busy.mp3
|
||||
web/deps/easymde.css
|
||||
web/deps/easymde.js
|
||||
|
|
|
@ -536,7 +536,7 @@ def get_sects():
|
|||
dedent(
|
||||
"""
|
||||
\033[33m-i\033[0m takes a comma-separated list of interfaces to listen on;
|
||||
IP-addresses and/or unix-sockets (Unix Domain Sockets)
|
||||
IP-addresses, unix-sockets, and/or open file descriptors
|
||||
|
||||
the default (\033[32m-i ::\033[0m) means all IPv4 and IPv6 addresses
|
||||
|
||||
|
@ -562,7 +562,9 @@ def get_sects():
|
|||
\033[32m-i unix:\033[33m/dev/shm/party.sock\033[0m keeps umask-defined permission
|
||||
(usually \033[33m0600\033[0m) and the same user/group as copyparty
|
||||
|
||||
\033[33m-p\033[0m (tcp ports) is ignored for unix sockets
|
||||
\033[32m-i fd:\033[33m3\033[0m uses the socket passed to copyparty on file descriptor 3
|
||||
|
||||
\033[33m-p\033[0m (tcp ports) is ignored for unix-sockets and FDs
|
||||
"""
|
||||
),
|
||||
],
|
||||
|
@ -578,7 +580,7 @@ def get_sects():
|
|||
|
||||
--grp takes groupname:username1,username2,...
|
||||
and groupnames can be used instead of usernames in -v
|
||||
by prefixing the groupname with %
|
||||
by prefixing the groupname with @
|
||||
|
||||
list of permissions:
|
||||
"r" (read): list folder contents, download files
|
||||
|
@ -916,6 +918,9 @@ def get_sects():
|
|||
copyparty will also hash and print any passwords that are non-hashed
|
||||
(password which do not start with '+') and then terminate afterwards
|
||||
|
||||
if you have enabled --usernames then the password
|
||||
must be provided as username:password for hashing
|
||||
|
||||
\033[36m--ah-alg\033[0m specifies the hashing algorithm and a
|
||||
list of optional comma-separated arguments:
|
||||
|
||||
|
@ -993,18 +998,19 @@ def build_flags_desc():
|
|||
|
||||
|
||||
def add_general(ap, nc, srvname):
|
||||
ap2 = ap.add_argument_group('general options')
|
||||
ap2.add_argument("-c", metavar="PATH", type=u, default=CFG_DEF, action="append", help="add config file")
|
||||
ap2 = ap.add_argument_group("general options")
|
||||
ap2.add_argument("-c", metavar="PATH", type=u, default=CFG_DEF, action="append", help="\033[34mREPEATABLE:\033[0m add config file")
|
||||
ap2.add_argument("-nc", metavar="NUM", type=int, default=nc, help="max num clients")
|
||||
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all")
|
||||
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
|
||||
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
|
||||
ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
|
||||
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
|
||||
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
|
||||
ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
|
||||
ap2.add_argument("--usernames", action="store_true", help="require username and password for login; default is just password")
|
||||
ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files (volflag=dots)")
|
||||
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("--mime", metavar="EXT=MIME", type=u, action="append", help="map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
|
||||
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)")
|
||||
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
||||
|
@ -1012,7 +1018,7 @@ def add_general(ap, nc, srvname):
|
|||
|
||||
|
||||
def add_qr(ap, tty):
|
||||
ap2 = ap.add_argument_group('qr options')
|
||||
ap2 = ap.add_argument_group("qr options")
|
||||
ap2.add_argument("--qr", action="store_true", help="show http:// QR-code on startup")
|
||||
ap2.add_argument("--qrs", action="store_true", help="show https:// QR-code on startup")
|
||||
ap2.add_argument("--qrl", metavar="PATH", type=u, default="", help="location to include in the url, for example [\033[32mpriv/?pw=hunter2\033[0m]")
|
||||
|
@ -1034,7 +1040,7 @@ def add_fs(ap):
|
|||
|
||||
def add_share(ap):
|
||||
db_path = os.path.join(E.cfg, "shares.db")
|
||||
ap2 = ap.add_argument_group('share-url options')
|
||||
ap2 = ap.add_argument_group("share-url options")
|
||||
ap2.add_argument("--shr", metavar="DIR", type=u, default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
|
||||
ap2.add_argument("--shr-db", metavar="FILE", type=u, default=db_path, help="database to store shares in")
|
||||
ap2.add_argument("--shr-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to view/delete any share")
|
||||
|
@ -1043,7 +1049,7 @@ def add_share(ap):
|
|||
|
||||
|
||||
def add_upload(ap):
|
||||
ap2 = ap.add_argument_group('upload options')
|
||||
ap2 = ap.add_argument_group("upload options")
|
||||
ap2.add_argument("--dotpart", action="store_true", help="dotfile incomplete uploads, hiding them from clients unless \033[33m-ed\033[0m")
|
||||
ap2.add_argument("--plain-ip", action="store_true", help="when avoiding filename collisions by appending the uploader's ip to the filename: append the plaintext ip instead of salting and hashing the ip")
|
||||
ap2.add_argument("--put-name", metavar="TXT", type=u, default="put-{now.6f}-{cip}.bin", help="filename for nameless uploads (when uploader doesn't provide a name); default is [\033[32mput-UNIXTIME-IP.bin\033[0m] (the \033[32m.6f\033[0m means six decimal places) (volflag=put_name)")
|
||||
|
@ -1085,14 +1091,14 @@ def add_upload(ap):
|
|||
|
||||
|
||||
def add_network(ap):
|
||||
ap2 = ap.add_argument_group('network options')
|
||||
ap2.add_argument("-i", metavar="IP", type=u, default="::", help="IPs and/or unix-sockets to listen on (see \033[33m--help-bind\033[0m). Default: all IPv4 and IPv6")
|
||||
ap2 = ap.add_argument_group("network options")
|
||||
ap2.add_argument("-i", metavar="IP", type=u, default="::", help="IPs and/or unix-sockets to listen on (comma-separated list; see \033[33m--help-bind\033[0m). Default: all IPv4 and IPv6")
|
||||
ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to listen on (comma/range); ignored for unix-sockets")
|
||||
ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 in mDNS replies, even if the NIC has routable IPs (breaks some mDNS clients)")
|
||||
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m2\033[0m]=outermost-proxy, [\033[32m3\033[0m]=second-proxy, [\033[32m-1\033[0m]=closest-proxy")
|
||||
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=9999999, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m-1\033[0m]=closest-proxy, [\033[32m-2\033[0m]=second-hop, [\033[32m-3\033[0m]=third-hop")
|
||||
ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from")
|
||||
ap2.add_argument("--xff-src", metavar="CIDR", type=u, default="127.0.0.0/8, ::1/128", help="comma-separated list of trusted reverse-proxy CIDRs; only accept the real-ip header (\033[33m--xff-hdr\033[0m) and IdP headers if the incoming connection is from an IP within either of these subnets. Specify [\033[32mlan\033[0m] to allow all LAN / private / non-internet IPs. Can be disabled with [\033[32many\033[0m] if you are behind cloudflare (or similar) and are using \033[32m--xff-hdr=cf-connecting-ip\033[0m (or similar)")
|
||||
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m; examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--xff-src", metavar="CIDR", type=u, default="127.0.0.0/8, ::1/128", help="list of trusted reverse-proxy CIDRs (comma-separated); only accept the real-ip header (\033[33m--xff-hdr\033[0m) and IdP headers if the incoming connection is from an IP within either of these subnets. Specify [\033[32mlan\033[0m] to allow all LAN / private / non-internet IPs. Can be disabled with [\033[32many\033[0m] if you are behind cloudflare (or similar) and are using \033[32m--xff-hdr=cf-connecting-ip\033[0m (or similar)")
|
||||
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--rp-loc", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated domain/subdomain, provide the base location here; example: [\033[32m/foo/bar\033[0m]")
|
||||
if ANYWIN:
|
||||
ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances")
|
||||
|
@ -1110,7 +1116,7 @@ def add_network(ap):
|
|||
|
||||
|
||||
def add_tls(ap, cert_path):
|
||||
ap2 = ap.add_argument_group('SSL/TLS options')
|
||||
ap2 = ap.add_argument_group("SSL/TLS options")
|
||||
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
||||
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
||||
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to file containing a concatenation of TLS key and certificate chain")
|
||||
|
@ -1122,7 +1128,7 @@ def add_tls(ap, cert_path):
|
|||
|
||||
def add_cert(ap, cert_path):
|
||||
cert_dir = os.path.dirname(cert_path)
|
||||
ap2 = ap.add_argument_group('TLS certificate generator options')
|
||||
ap2 = ap.add_argument_group("TLS certificate generator options")
|
||||
ap2.add_argument("--no-crt", action="store_true", help="disable automatic certificate creation")
|
||||
ap2.add_argument("--crt-ns", metavar="N,N", type=u, default="", help="comma-separated list of FQDNs (domains) to add into the certificate")
|
||||
ap2.add_argument("--crt-exact", action="store_true", help="do not add wildcard entries for each \033[33m--crt-ns\033[0m")
|
||||
|
@ -1142,7 +1148,7 @@ def add_cert(ap, cert_path):
|
|||
def add_auth(ap):
|
||||
idp_db = os.path.join(E.cfg, "idp.db")
|
||||
ses_db = os.path.join(E.cfg, "sessions.db")
|
||||
ap2 = ap.add_argument_group('IdP / identity provider / user authentication options')
|
||||
ap2 = ap.add_argument_group("IdP / identity provider / user authentication options")
|
||||
ap2.add_argument("--idp-h-usr", metavar="HN", type=u, default="", help="bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy")
|
||||
ap2.add_argument("--idp-h-grp", metavar="HN", type=u, default="", help="assume the request-header \033[33mHN\033[0m contains the groupname of the requesting user; can be referenced in config files for group-based access control")
|
||||
ap2.add_argument("--idp-h-key", metavar="HN", type=u, default="", help="optional but recommended safeguard; your reverse-proxy will insert a secret header named \033[33mHN\033[0m into all requests, and the other IdP headers will be ignored if this header is not present")
|
||||
|
@ -1150,19 +1156,20 @@ def add_auth(ap):
|
|||
ap2.add_argument("--idp-db", metavar="PATH", type=u, default=idp_db, help="where to store the known IdP users/groups (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP")
|
||||
ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)")
|
||||
ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n └─note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)")
|
||||
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
|
||||
ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins")
|
||||
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
|
||||
ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
|
||||
ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
|
||||
ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
|
||||
|
||||
|
||||
def add_chpw(ap):
|
||||
db_path = os.path.join(E.cfg, "chpw.json")
|
||||
ap2 = ap.add_argument_group('user-changeable passwords options')
|
||||
ap2 = ap.add_argument_group("user-changeable passwords options")
|
||||
ap2.add_argument("--chpw", action="store_true", help="allow users to change their own passwords")
|
||||
ap2.add_argument("--chpw-no", metavar="U,U,U", type=u, action="append", help="do not allow password-changes for this comma-separated list of usernames")
|
||||
ap2.add_argument("--chpw-no", metavar="U,U,U", type=u, action="append", help="\033[34mREPEATABLE:\033[0m do not allow password-changes for this comma-separated list of usernames")
|
||||
ap2.add_argument("--chpw-db", metavar="PATH", type=u, default=db_path, help="where to store the passwords database (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
ap2.add_argument("--chpw-len", metavar="N", type=int, default=8, help="minimum password length")
|
||||
ap2.add_argument("--chpw-v", metavar="LVL", type=int, default=2, help="verbosity of summary on config load [\033[32m0\033[0m] = nothing at all, [\033[32m1\033[0m] = number of users, [\033[32m2\033[0m] = list users with default-pw, [\033[32m3\033[0m] = list all users")
|
||||
|
@ -1211,12 +1218,12 @@ def add_zc_ssdp(ap):
|
|||
|
||||
|
||||
def add_ftp(ap):
|
||||
ap2 = ap.add_argument_group('FTP options (TCP only)')
|
||||
ap2 = ap.add_argument_group("FTP options (TCP only)")
|
||||
ap2.add_argument("--ftp", metavar="PORT", type=int, default=0, help="enable FTP server on \033[33mPORT\033[0m, for example \033[32m3921")
|
||||
ap2.add_argument("--ftps", metavar="PORT", type=int, default=0, help="enable FTPS server on \033[33mPORT\033[0m, for example \033[32m3990")
|
||||
ap2.add_argument("--ftpv", action="store_true", help="verbose")
|
||||
ap2.add_argument("--ftp4", action="store_true", help="only listen on IPv4")
|
||||
ap2.add_argument("--ftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m; specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--ftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--ftp-no-ow", action="store_true", help="if target file exists, reject upload instead of overwrite")
|
||||
ap2.add_argument("--ftp-wt", metavar="SEC", type=int, default=7, help="grace period for resuming interrupted uploads (any client can write to any file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
||||
ap2.add_argument("--ftp-nat", metavar="ADDR", type=u, default="", help="the NAT address to use for passive connections")
|
||||
|
@ -1224,7 +1231,7 @@ def add_ftp(ap):
|
|||
|
||||
|
||||
def add_webdav(ap):
|
||||
ap2 = ap.add_argument_group('WebDAV options')
|
||||
ap2 = ap.add_argument_group("WebDAV options")
|
||||
ap2.add_argument("--daw", action="store_true", help="enable full write support, even if client may not be webdav. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
|
||||
ap2.add_argument("--dav-inf", action="store_true", help="allow depth:infinite requests (recursive file listing); extremely server-heavy but required for spec compliance -- luckily few clients rely on this")
|
||||
ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
|
||||
|
@ -1234,7 +1241,7 @@ def add_webdav(ap):
|
|||
|
||||
|
||||
def add_tftp(ap):
|
||||
ap2 = ap.add_argument_group('TFTP options (UDP only)')
|
||||
ap2 = ap.add_argument_group("TFTP options (UDP only)")
|
||||
ap2.add_argument("--tftp", metavar="PORT", type=int, default=0, help="enable TFTP server on \033[33mPORT\033[0m, for example \033[32m69 \033[0mor \033[32m3969")
|
||||
ap2.add_argument("--tftp4", action="store_true", help="only listen on IPv4")
|
||||
ap2.add_argument("--tftpv", action="store_true", help="verbose")
|
||||
|
@ -1242,12 +1249,12 @@ def add_tftp(ap):
|
|||
ap2.add_argument("--tftp-no-fast", action="store_true", help="debug: disable optimizations")
|
||||
ap2.add_argument("--tftp-lsf", metavar="PTN", type=u, default="\\.?(dir|ls)(\\.txt)?", help="return a directory listing if a file with this name is requested and it does not exist; defaults matches .ls, dir, .dir.txt, ls.txt, ...")
|
||||
ap2.add_argument("--tftp-nols", action="store_true", help="if someone tries to download a directory, return an error instead of showing its directory listing")
|
||||
ap2.add_argument("--tftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m; specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--tftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--tftp-pr", metavar="P-P", type=u, default="", help="the range of UDP ports to use for data transfer, for example \033[32m12000-13000")
|
||||
|
||||
|
||||
def add_smb(ap):
|
||||
ap2 = ap.add_argument_group('SMB/CIFS options')
|
||||
ap2 = ap.add_argument_group("SMB/CIFS options")
|
||||
ap2.add_argument("--smb", action="store_true", help="enable smb (read-only) -- this requires running copyparty as root on linux and macos unless \033[33m--smb-port\033[0m is set above 1024 and your OS does port-forwarding from 445 to that.\n\033[1;31mWARNING:\033[0m this protocol is DANGEROUS and buggy! Never expose to the internet!")
|
||||
ap2.add_argument("--smbw", action="store_true", help="enable write support (please dont)")
|
||||
ap2.add_argument("--smb1", action="store_true", help="disable SMBv2, only enable SMBv1 (CIFS)")
|
||||
|
@ -1261,30 +1268,30 @@ def add_smb(ap):
|
|||
|
||||
|
||||
def add_handlers(ap):
|
||||
ap2 = ap.add_argument_group('handlers (see --help-handlers)')
|
||||
ap2.add_argument("--on404", metavar="PY", type=u, action="append", help="handle 404s by executing \033[33mPY\033[0m file")
|
||||
ap2.add_argument("--on403", metavar="PY", type=u, action="append", help="handle 403s by executing \033[33mPY\033[0m file")
|
||||
ap2 = ap.add_argument_group("handlers (see --help-handlers)")
|
||||
ap2.add_argument("--on404", metavar="PY", type=u, action="append", help="\033[34mREPEATABLE:\033[0m handle 404s by executing \033[33mPY\033[0m file")
|
||||
ap2.add_argument("--on403", metavar="PY", type=u, action="append", help="\033[34mREPEATABLE:\033[0m handle 403s by executing \033[33mPY\033[0m file")
|
||||
ap2.add_argument("--hot-handlers", action="store_true", help="recompile handlers on each request -- expensive but convenient when hacking on stuff")
|
||||
|
||||
|
||||
def add_hooks(ap):
|
||||
ap2 = ap.add_argument_group('event hooks (see --help-hooks)')
|
||||
ap2.add_argument("--xbu", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file upload starts")
|
||||
ap2.add_argument("--xau", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file upload finishes")
|
||||
ap2.add_argument("--xiu", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after all uploads finish and volume is idle")
|
||||
ap2.add_argument("--xbc", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file copy")
|
||||
ap2.add_argument("--xac", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file copy")
|
||||
ap2.add_argument("--xbr", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file move/rename")
|
||||
ap2.add_argument("--xar", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file move/rename")
|
||||
ap2.add_argument("--xbd", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file delete")
|
||||
ap2.add_argument("--xad", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file delete")
|
||||
ap2.add_argument("--xm", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m on message")
|
||||
ap2.add_argument("--xban", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m if someone gets banned (pw/404/403/url)")
|
||||
ap2 = ap.add_argument_group("event hooks (see --help-hooks)")
|
||||
ap2.add_argument("--xbu", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m before a file upload starts")
|
||||
ap2.add_argument("--xau", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m after a file upload finishes")
|
||||
ap2.add_argument("--xiu", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m after all uploads finish and volume is idle")
|
||||
ap2.add_argument("--xbc", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m before a file copy")
|
||||
ap2.add_argument("--xac", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m after a file copy")
|
||||
ap2.add_argument("--xbr", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m before a file move/rename")
|
||||
ap2.add_argument("--xar", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m after a file move/rename")
|
||||
ap2.add_argument("--xbd", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m before a file delete")
|
||||
ap2.add_argument("--xad", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m after a file delete")
|
||||
ap2.add_argument("--xm", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m on message")
|
||||
ap2.add_argument("--xban", metavar="CMD", type=u, action="append", help="\033[34mREPEATABLE:\033[0m execute \033[33mCMD\033[0m if someone gets banned (pw/404/403/url)")
|
||||
ap2.add_argument("--hook-v", action="store_true", help="verbose hooks")
|
||||
|
||||
|
||||
def add_stats(ap):
|
||||
ap2 = ap.add_argument_group('grafana/prometheus metrics endpoint')
|
||||
ap2 = ap.add_argument_group("grafana/prometheus metrics endpoint")
|
||||
ap2.add_argument("--stats", action="store_true", help="enable openmetrics at /.cpr/metrics for admin accounts")
|
||||
ap2.add_argument("--nos-hdd", action="store_true", help="disable disk-space metrics (used/free space)")
|
||||
ap2.add_argument("--nos-vol", action="store_true", help="disable volume size metrics (num files, total bytes, vmaxb/vmaxn)")
|
||||
|
@ -1294,15 +1301,16 @@ def add_stats(ap):
|
|||
|
||||
|
||||
def add_yolo(ap):
|
||||
ap2 = ap.add_argument_group('yolo options')
|
||||
ap2 = ap.add_argument_group("yolo options")
|
||||
ap2.add_argument("--allow-csrf", action="store_true", help="disable csrf protections; let other domains/sites impersonate you through cross-site requests")
|
||||
ap2.add_argument("--cookie-lax", action="store_true", help="allow cookies from other domains (if you follow a link from another website into your server, you will arrive logged-in); this reduces protection against CSRF")
|
||||
ap2.add_argument("--no-fnugg", action="store_true", help="disable the smoketest for caching-related issues in the web-UI")
|
||||
ap2.add_argument("--getmod", action="store_true", help="permit ?move=[...] and ?delete as GET")
|
||||
ap2.add_argument("--wo-up-readme", action="store_true", help="allow users with write-only access to upload logues and readmes without adding the _wo_ filename prefix (volflag=wo_up_readme)")
|
||||
|
||||
|
||||
def add_optouts(ap):
|
||||
ap2 = ap.add_argument_group('opt-outs')
|
||||
ap2 = ap.add_argument_group("opt-outs")
|
||||
ap2.add_argument("-nw", action="store_true", help="never write anything to disk (debug/benchmark)")
|
||||
ap2.add_argument("--keep-qem", action="store_true", help="do not disable quick-edit-mode on windows (it is disabled to avoid accidental text selection in the terminal window, as this would pause execution)")
|
||||
ap2.add_argument("--no-dav", action="store_true", help="disable webdav support")
|
||||
|
@ -1328,7 +1336,7 @@ def add_optouts(ap):
|
|||
|
||||
|
||||
def add_safety(ap):
|
||||
ap2 = ap.add_argument_group('safety options')
|
||||
ap2 = ap.add_argument_group("safety options")
|
||||
ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js")
|
||||
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --vague-403 -nih")
|
||||
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
||||
|
@ -1359,7 +1367,7 @@ def add_safety(ap):
|
|||
|
||||
|
||||
def add_salt(ap, fk_salt, dk_salt, ah_salt):
|
||||
ap2 = ap.add_argument_group('salting options')
|
||||
ap2 = ap.add_argument_group("salting options")
|
||||
ap2.add_argument("--ah-alg", metavar="ALG", type=u, default="none", help="account-pw hashing algorithm; one of these, best to worst: \033[32margon2 scrypt sha2 none\033[0m (each optionally followed by alg-specific comma-sep. config)")
|
||||
ap2.add_argument("--ah-salt", metavar="SALT", type=u, default=ah_salt, help="account-pw salt; ignored if \033[33m--ah-alg\033[0m is none (default)")
|
||||
ap2.add_argument("--ah-gen", metavar="PW", type=u, default="", help="generate hashed password for \033[33mPW\033[0m, or read passwords from STDIN if \033[33mPW\033[0m is [\033[32m-\033[0m]")
|
||||
|
@ -1373,14 +1381,14 @@ def add_salt(ap, fk_salt, dk_salt, ah_salt):
|
|||
|
||||
|
||||
def add_shutdown(ap):
|
||||
ap2 = ap.add_argument_group('shutdown options')
|
||||
ap2 = ap.add_argument_group("shutdown options")
|
||||
ap2.add_argument("--ign-ebind", action="store_true", help="continue running even if it's impossible to listen on some of the requested endpoints")
|
||||
ap2.add_argument("--ign-ebind-all", action="store_true", help="continue running even if it's impossible to receive connections at all")
|
||||
ap2.add_argument("--exit", metavar="WHEN", type=u, default="", help="shutdown after \033[33mWHEN\033[0m has finished; [\033[32mcfg\033[0m] config parsing, [\033[32midx\033[0m] volscan + multimedia indexing")
|
||||
|
||||
|
||||
def add_logging(ap):
|
||||
ap2 = ap.add_argument_group('logging options')
|
||||
ap2 = ap.add_argument_group("logging options")
|
||||
ap2.add_argument("-q", action="store_true", help="quiet; disable most STDOUT messages")
|
||||
ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile, example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)")
|
||||
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
||||
|
@ -1389,7 +1397,7 @@ def add_logging(ap):
|
|||
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
||||
ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
|
||||
ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
|
||||
ap2.add_argument("--log-badpwd", metavar="N", type=int, default=1, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
|
||||
ap2.add_argument("--log-badpwd", metavar="N", type=int, default=2, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
|
||||
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
||||
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
||||
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||
|
@ -1398,7 +1406,7 @@ def add_logging(ap):
|
|||
|
||||
|
||||
def add_admin(ap):
|
||||
ap2 = ap.add_argument_group('admin panel options')
|
||||
ap2 = ap.add_argument_group("admin panel options")
|
||||
ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
|
||||
ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
|
||||
ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
|
||||
|
@ -1412,7 +1420,7 @@ def add_admin(ap):
|
|||
def add_thumbnail(ap):
|
||||
th_ram = (RAM_AVAIL or RAM_TOTAL or 9) * 0.6
|
||||
th_ram = int(max(min(th_ram, 6), 0.3) * 10) / 10
|
||||
ap2 = ap.add_argument_group('thumbnail options')
|
||||
ap2 = ap.add_argument_group("thumbnail options")
|
||||
ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails (volflag=dthumb)")
|
||||
ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails (volflag=dvthumb)")
|
||||
ap2.add_argument("--no-athumb", action="store_true", help="disable audio thumbnails (spectrograms) (volflag=dathumb)")
|
||||
|
@ -1444,9 +1452,11 @@ def add_thumbnail(ap):
|
|||
|
||||
|
||||
def add_transcoding(ap):
|
||||
ap2 = ap.add_argument_group('transcoding options')
|
||||
ap2 = ap.add_argument_group("transcoding options")
|
||||
ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
|
||||
ap2.add_argument("--q-mp3", metavar="QUALITY", type=u, default="q2", help="target quality for transcoding to mp3, for example [\033[32m192k\033[0m] (CBR) or [\033[32mq0\033[0m] (CQ/CRF, q0=maxquality, q9=smallest); set 0 to disable")
|
||||
ap2.add_argument("--allow-wav", action="store_true", help="allow transcoding to wav (lossless, uncompressed)")
|
||||
ap2.add_argument("--allow-flac", action="store_true", help="allow transcoding to flac (lossless, compressed)")
|
||||
ap2.add_argument("--no-caf", action="store_true", help="disable transcoding to caf-opus (affects iOS v12~v17), will use mp3 instead")
|
||||
ap2.add_argument("--no-owa", action="store_true", help="disable transcoding to webm-opus (iOS v18 and later), will use mp3 instead")
|
||||
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
||||
|
@ -1455,7 +1465,7 @@ def add_transcoding(ap):
|
|||
|
||||
|
||||
def add_tail(ap):
|
||||
ap2 = ap.add_argument_group('tailing options (realtime streaming of a growing file)')
|
||||
ap2 = ap.add_argument_group("tailing options (realtime streaming of a growing file)")
|
||||
ap2.add_argument("--tail-who", metavar="LVL", type=int, default=2, help="who can tail? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=authenticated-with-read-access, [\033[32m3\033[0m]=everyone-with-read-access (volflag=tail_who)")
|
||||
ap2.add_argument("--tail-cmax", metavar="N", type=int, default=64, help="do not allow starting a new tail if more than \033[33mN\033[0m active downloads")
|
||||
ap2.add_argument("--tail-tmax", metavar="SEC", type=float, default=0, help="terminate connection after \033[33mSEC\033[0m seconds; [\033[32m0\033[0m]=never (volflag=tail_tmax)")
|
||||
|
@ -1465,7 +1475,7 @@ def add_tail(ap):
|
|||
|
||||
|
||||
def add_rss(ap):
|
||||
ap2 = ap.add_argument_group('RSS options')
|
||||
ap2 = ap.add_argument_group("RSS options")
|
||||
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental) (volflag=rss)")
|
||||
ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
|
||||
ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
|
||||
|
@ -1474,7 +1484,7 @@ def add_rss(ap):
|
|||
|
||||
def add_db_general(ap, hcores):
|
||||
noidx = APPLESAN_TXT if MACOS else ""
|
||||
ap2 = ap.add_argument_group('general db options')
|
||||
ap2 = ap.add_argument_group("general db options")
|
||||
ap2.add_argument("-e2d", action="store_true", help="enable up2k database; this enables file search, upload-undo, improves deduplication")
|
||||
ap2.add_argument("-e2ds", action="store_true", help="scan writable folders for new files on startup; sets \033[33m-e2d\033[0m")
|
||||
ap2.add_argument("-e2dsa", action="store_true", help="scans all folders on startup; sets \033[33m-e2ds\033[0m")
|
||||
|
@ -1503,7 +1513,7 @@ def add_db_general(ap, hcores):
|
|||
|
||||
|
||||
def add_db_metadata(ap):
|
||||
ap2 = ap.add_argument_group('metadata db options')
|
||||
ap2 = ap.add_argument_group("metadata db options")
|
||||
ap2.add_argument("-e2t", action="store_true", help="enable metadata indexing; makes it possible to search for artist/title/codec/resolution/...")
|
||||
ap2.add_argument("-e2ts", action="store_true", help="scan newly discovered files for metadata on startup; sets \033[33m-e2t\033[0m")
|
||||
ap2.add_argument("-e2tsr", action="store_true", help="delete all metadata from DB and do a full rescan; sets \033[33m-e2ts\033[0m")
|
||||
|
@ -1513,15 +1523,16 @@ def add_db_metadata(ap):
|
|||
ap2.add_argument("--mtag-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for tag scanning")
|
||||
ap2.add_argument("--mtag-v", action="store_true", help="verbose tag scanning; print errors from mtp subprocesses and such")
|
||||
ap2.add_argument("--mtag-vv", action="store_true", help="debug mtp settings and mutagen/FFprobe parsers")
|
||||
ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="add/replace metadata mapping")
|
||||
ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add/replace metadata mapping")
|
||||
ap2.add_argument("-mte", metavar="M,M,M", type=u, help="tags to index/display (comma-sep.); either an entire replacement list, or add/remove stuff on the default-list with +foo or /bar", default=DEF_MTE)
|
||||
ap2.add_argument("-mth", metavar="M,M,M", type=u, help="tags to hide by default (comma-sep.); assign/add/remove same as \033[33m-mte\033[0m", default=DEF_MTH)
|
||||
ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="read tag \033[33mM\033[0m using program \033[33mBIN\033[0m to parse the file")
|
||||
ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="\033[34mREPEATABLE:\033[0m read tag \033[33mM\033[0m using program \033[33mBIN\033[0m to parse the file")
|
||||
|
||||
|
||||
def add_txt(ap):
|
||||
ap2 = ap.add_argument_group('textfile options')
|
||||
ap2 = ap.add_argument_group("textfile options")
|
||||
ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of markdown files; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)")
|
||||
ap2.add_argument("--txt-eol", metavar="TYPE", type=u, default="", help="enable EOL conversion when writing documents; supported: CRLF, LF (volflag=txt_eol)")
|
||||
ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="the textfile editor will check for serverside changes every \033[33mSEC\033[0m seconds")
|
||||
ap2.add_argument("-emp", action="store_true", help="enable markdown plugins -- neat but dangerous, big XSS risk")
|
||||
ap2.add_argument("--exp", action="store_true", help="enable textfile expansion -- replace {{self.ip}} and such; see \033[33m--help-exp\033[0m (volflag=exp)")
|
||||
|
@ -1531,7 +1542,7 @@ def add_txt(ap):
|
|||
|
||||
|
||||
def add_og(ap):
|
||||
ap2 = ap.add_argument_group('og / open graph / discord-embed options')
|
||||
ap2 = ap.add_argument_group("og / open graph / discord-embed options")
|
||||
ap2.add_argument("--og", action="store_true", help="disable hotlinking and return an html document instead; this is required by open-graph, but can also be useful on its own (volflag=og)")
|
||||
ap2.add_argument("--og-ua", metavar="RE", type=u, default="", help="only disable hotlinking / engage OG behavior if the useragent matches regex \033[33mRE\033[0m (volflag=og_ua)")
|
||||
ap2.add_argument("--og-tpl", metavar="PATH", type=u, default="", help="do not return the regular copyparty html, but instead load the jinja2 template at \033[33mPATH\033[0m (if path contains 'EXT' then EXT will be replaced with the requested file's extension) (volflag=og_tpl)")
|
||||
|
@ -1549,7 +1560,7 @@ def add_og(ap):
|
|||
|
||||
|
||||
def add_ui(ap, retry):
|
||||
ap2 = ap.add_argument_group('ui options')
|
||||
ap2 = ap.add_argument_group("ui options")
|
||||
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
||||
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
||||
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
||||
|
@ -1564,8 +1575,8 @@ def add_ui(ap, retry):
|
|||
ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
|
||||
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
||||
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
||||
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
||||
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
||||
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="\033[34mREPEATABLE:\033[0m use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
||||
ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS)
|
||||
ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
|
||||
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
|
||||
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
||||
|
@ -1580,6 +1591,7 @@ def add_ui(ap, retry):
|
|||
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
|
||||
ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
||||
ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
||||
ap2.add_argument("--ctl-re", metavar="SEC", type=int, default=1, help="the controlpanel Refresh-button will autorefresh every SEC; [\033[32m0\033[0m] = just once")
|
||||
ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox")
|
||||
ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for prologue/epilogue docs (volflag=lg_sbf)")
|
||||
ap2.add_argument("--md-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for README.md docs, for example [\033[32mfullscreen\033[0m] (volflag=md_sba)")
|
||||
|
@ -1590,7 +1602,7 @@ def add_ui(ap, retry):
|
|||
|
||||
|
||||
def add_debug(ap):
|
||||
ap2 = ap.add_argument_group('debug options')
|
||||
ap2 = ap.add_argument_group("debug options")
|
||||
ap2.add_argument("--vc", action="store_true", help="verbose config file parser (explain config)")
|
||||
ap2.add_argument("--cgen", action="store_true", help="generate config file from current config (best-effort; probably buggy)")
|
||||
ap2.add_argument("--deps", action="store_true", help="list information about detected optional dependencies")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 18, 9)
|
||||
CODENAME = "logtail"
|
||||
BUILD_DT = (2025, 8, 1)
|
||||
VERSION = (1, 19, 0)
|
||||
CODENAME = "usernames"
|
||||
BUILD_DT = (2025, 8, 7)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
@ -1700,6 +1700,7 @@ class AuthSrv(object):
|
|||
if not mount and not self.args.idp_h_usr:
|
||||
# -h says our defaults are CWD at root and read/write for everyone
|
||||
axs = AXS(["*"], ["*"], None, None)
|
||||
ehint = ""
|
||||
if self.is_lxc:
|
||||
t = "Read-access has been disabled due to failsafe: Docker detected, but %s. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to all of /w/ by adding the following arguments to the docker container: -v .::rw"
|
||||
if len(cfg_files_loaded) == 1:
|
||||
|
@ -1709,11 +1710,23 @@ class AuthSrv(object):
|
|||
else:
|
||||
self.log(t % ("the config does not define any volumes",), 1)
|
||||
axs = AXS()
|
||||
ehint = "; please try moving them up one level, into the parent folder:"
|
||||
elif self.args.c:
|
||||
t = "Read-access has been disabled due to failsafe: No volumes were defined by the config-file. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to the working-directory by adding the following arguments: -v .::rw"
|
||||
self.log(t, 1)
|
||||
axs = AXS()
|
||||
vfs = VFS(self.log_func, absreal("."), "", "", axs, self.vf0())
|
||||
ehint = ":"
|
||||
if ehint:
|
||||
try:
|
||||
files = os.listdir(E.cfg)
|
||||
except:
|
||||
files = []
|
||||
hits = [x for x in files if x.lower().endswith(".conf")]
|
||||
if hits:
|
||||
t = "Hint: Found some config files in [%s], but these were not automatically loaded because they are in the wrong place%s %s\n"
|
||||
self.log(t % (E.cfg, ehint, ", ".join(hits)), 3)
|
||||
zvf = {"tcolor": self.args.tcolor}
|
||||
vfs = VFS(self.log_func, absreal("."), "", "", axs, zvf)
|
||||
if not axs.uread:
|
||||
self.badcfg1 = True
|
||||
elif "" not in mount:
|
||||
|
@ -2629,6 +2642,8 @@ class AuthSrv(object):
|
|||
self.re_pwd = None
|
||||
pwds = [re.escape(x) for x in self.iacct.keys()]
|
||||
pwds.extend(list(self.sesa))
|
||||
if self.args.usernames:
|
||||
pwds.extend([x.split(":", 1)[1] for x in pwds if ":" in x])
|
||||
if pwds:
|
||||
if self.ah.on:
|
||||
zs = r"(\[H\] pw:.*|[?&]pw=)([^&]+)"
|
||||
|
@ -2751,6 +2766,8 @@ class AuthSrv(object):
|
|||
"s_name": self.args.bname,
|
||||
"have_up2k_idx": "e2d" in vf,
|
||||
"have_acode": not self.args.no_acode,
|
||||
"have_c2flac": self.args.allow_flac,
|
||||
"have_c2wav": self.args.allow_wav,
|
||||
"have_shr": self.args.shr,
|
||||
"have_zip": not self.args.no_zip,
|
||||
"have_mv": not self.args.no_mv,
|
||||
|
@ -2866,7 +2883,10 @@ class AuthSrv(object):
|
|||
|
||||
n = []
|
||||
q = "insert into us values (?,?,?)"
|
||||
for uname in self.acct:
|
||||
accs = list(self.acct)
|
||||
if self.args.idp_h_usr and self.args.idp_cookie:
|
||||
accs.extend(self.idp_accs.keys())
|
||||
for uname in accs:
|
||||
if uname not in ases:
|
||||
sid = ub64enc(os.urandom(blen)).decode("ascii")
|
||||
cur.execute(q, (uname, sid, int(time.time())))
|
||||
|
@ -2924,6 +2944,9 @@ class AuthSrv(object):
|
|||
t = "minimum password length: %d characters"
|
||||
return False, t % (self.args.chpw_len,)
|
||||
|
||||
if self.args.usernames:
|
||||
pw = "%s:%s" % (uname, pw)
|
||||
|
||||
hpw = self.ah.hash(pw) if self.ah.on else pw
|
||||
|
||||
if hpw == self.acct[uname]:
|
||||
|
@ -3015,6 +3038,12 @@ class AuthSrv(object):
|
|||
self.log("chpw: " + msg, 6)
|
||||
|
||||
def setup_pwhash(self, acct: dict[str, str]) -> None:
|
||||
if self.args.usernames:
|
||||
for uname, pw in list(acct.items())[:]:
|
||||
if pw.startswith("+") and len(pw) == 33:
|
||||
continue
|
||||
acct[uname] = "%s:%s" % (uname, pw)
|
||||
|
||||
self.ah = PWHash(self.args)
|
||||
if not self.ah.on:
|
||||
if self.args.ah_cli or self.args.ah_gen:
|
||||
|
|
|
@ -111,6 +111,7 @@ def vf_vmap() -> dict[str, str]:
|
|||
"tail_tmax",
|
||||
"tail_who",
|
||||
"tcolor",
|
||||
"txt_eol",
|
||||
"unlist",
|
||||
"u2abort",
|
||||
"u2ts",
|
||||
|
@ -322,6 +323,7 @@ flagcats = {
|
|||
"exp": "enable textfile expansion; see --help-exp",
|
||||
"exp_md": "placeholders to expand in markdown files; see --help",
|
||||
"exp_lg": "placeholders to expand in prologue/epilogue; see --help",
|
||||
"txt_eol=lf": "enable EOL conversion when writing docs (LF or CRLF)",
|
||||
},
|
||||
"tailing": {
|
||||
"notail": "disable ?tail (download a growing file continuously)",
|
||||
|
|
|
@ -83,7 +83,12 @@ class FtpAuth(DummyAuthorizer):
|
|||
uname = "*"
|
||||
if username != "anonymous":
|
||||
uname = ""
|
||||
for zs in (password, username):
|
||||
if args.usernames:
|
||||
alts = ["%s:%s" % (username, password)]
|
||||
else:
|
||||
alts = password, username
|
||||
|
||||
for zs in alts:
|
||||
zs = asrv.iacct.get(asrv.ah.hash(zs), "")
|
||||
if zs:
|
||||
uname = zs
|
||||
|
@ -607,7 +612,7 @@ class Ftpd(object):
|
|||
if "::" in ips:
|
||||
ips.append("0.0.0.0")
|
||||
|
||||
ips = [x for x in ips if "unix:" not in x]
|
||||
ips = [x for x in ips if not x.startswith(("unix:", "fd:"))]
|
||||
|
||||
if self.args.ftp4:
|
||||
ips = [x for x in ips if ":" not in x]
|
||||
|
|
|
@ -62,6 +62,7 @@ from .util import (
|
|||
alltrace,
|
||||
atomic_move,
|
||||
b64dec,
|
||||
eol_conv,
|
||||
exclude_dotfiles,
|
||||
formatdate,
|
||||
fsenc,
|
||||
|
@ -262,7 +263,8 @@ class HttpCli(object):
|
|||
|
||||
def _assert_safe_rem(self, rem: str) -> None:
|
||||
# sanity check to prevent any disasters
|
||||
if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
|
||||
# (this function hopefully serves no purpose; validation has already happened at this point, this only exists as a last-ditch effort just in case)
|
||||
if rem.startswith(("/", "../")) or "/../" in rem:
|
||||
raise Exception("that was close")
|
||||
|
||||
def _gen_fk(self, alg: int, salt: str, fspath: str, fsize: int, inode: int) -> str:
|
||||
|
@ -383,9 +385,20 @@ class HttpCli(object):
|
|||
try:
|
||||
cli_ip = zsl[n].strip()
|
||||
except:
|
||||
cli_ip = zsl[0].strip()
|
||||
t = "rproxy={} oob x-fwd {}"
|
||||
self.log(t.format(self.args.rproxy, zso), c=3)
|
||||
cli_ip = self.ip
|
||||
self.bad_xff = True
|
||||
if self.args.rproxy != 9999999:
|
||||
t = "global-option --rproxy %d could not be used (out-of-bounds) for the received header [%s]"
|
||||
self.log(t % (self.args.rproxy, zso), c=3)
|
||||
else:
|
||||
zsl = [
|
||||
" rproxy: %d if this client's IP-address is [%s]"
|
||||
% (-1 - zd, zs.strip())
|
||||
for zd, zs in enumerate(zsl)
|
||||
]
|
||||
t = 'could not determine the client\'s IP-address because the global-option --rproxy has not been configured, so the request-header [%s] specified by global-option --xff-hdr cannot be used safely! Please see the "reverse-proxy" section in the readme. The best approach is to configure your reverse-proxy to give copyparty the exact IP-address to assume (perhaps in another header), but you may also try the following:'
|
||||
t = t % (self.args.xff_hdr,)
|
||||
self.log("%s\n\n%s\n" % (t, "\n".join(zsl)), 3)
|
||||
|
||||
pip = self.conn.addr[0]
|
||||
xffs = self.conn.xff_nm
|
||||
|
@ -658,6 +671,9 @@ class HttpCli(object):
|
|||
self.pw = ""
|
||||
self.uname = idp_usr
|
||||
self.html_head += "<script>var is_idp=1</script>\n"
|
||||
zs = self.asrv.ases.get(idp_usr)
|
||||
if zs:
|
||||
self.set_idp_cookie(zs)
|
||||
else:
|
||||
self.log("unknown username: %r" % (idp_usr,), 1)
|
||||
|
||||
|
@ -1196,15 +1212,6 @@ class HttpCli(object):
|
|||
self.reply(b"ssdp is disabled in server config", 404)
|
||||
return False
|
||||
|
||||
if self.vpath.startswith(".cpr/dd/") and self.args.mpmc:
|
||||
if self.args.mpmc == ".":
|
||||
raise Pebkac(404)
|
||||
|
||||
loc = self.args.mpmc.rstrip("/") + self.vpath[self.vpath.rfind("/") :]
|
||||
h = {"Location": loc, "Cache-Control": "max-age=39"}
|
||||
self.reply(b"", 301, headers=h)
|
||||
return True
|
||||
|
||||
if self.vpath == ".cpr/metrics":
|
||||
return self.conn.hsrv.metrics.tx(self)
|
||||
|
||||
|
@ -2083,16 +2090,16 @@ class HttpCli(object):
|
|||
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
||||
lim = vfs.get_dbv(rem)[0].lim
|
||||
fdir = vfs.canonical(rem)
|
||||
if lim:
|
||||
fdir, rem = lim.all(
|
||||
self.ip, rem, remains, vfs.realpath, fdir, self.conn.hsrv.broker
|
||||
)
|
||||
|
||||
fn = None
|
||||
if rem and not self.trailing_slash and not bos.path.isdir(fdir):
|
||||
fdir, fn = os.path.split(fdir)
|
||||
rem, _ = vsplit(rem)
|
||||
|
||||
if lim:
|
||||
fdir, rem = lim.all(
|
||||
self.ip, rem, remains, vfs.realpath, fdir, self.conn.hsrv.broker
|
||||
)
|
||||
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
|
||||
open_ka: dict[str, Any] = {"fun": open}
|
||||
|
@ -2930,12 +2937,16 @@ class HttpCli(object):
|
|||
|
||||
def handle_chpw(self) -> bool:
|
||||
assert self.parser # !rm
|
||||
if self.args.usernames:
|
||||
self.parser.require("uname", 64)
|
||||
pwd = self.parser.require("pw", 64)
|
||||
self.parser.drop()
|
||||
|
||||
ok, msg = self.asrv.chpw(self.conn.hsrv.broker, self.uname, pwd)
|
||||
if ok:
|
||||
self.cbonk(self.conn.hsrv.gpwc, pwd, "pw", "too many password changes")
|
||||
if self.args.usernames:
|
||||
pwd = "%s:%s" % (self.uname, pwd)
|
||||
ok, msg = self.get_pwd_cookie(pwd)
|
||||
if ok:
|
||||
msg = "new password OK"
|
||||
|
@ -2948,6 +2959,15 @@ class HttpCli(object):
|
|||
|
||||
def handle_login(self) -> bool:
|
||||
assert self.parser # !rm
|
||||
if self.args.usernames and not (
|
||||
self.args.shr and self.vpath.startswith(self.args.shr1)
|
||||
):
|
||||
try:
|
||||
un = self.parser.require("uname", 64)
|
||||
except:
|
||||
un = ""
|
||||
else:
|
||||
un = ""
|
||||
pwd = self.parser.require("cppwd", 64)
|
||||
try:
|
||||
uhash = self.parser.require("uhash", 256)
|
||||
|
@ -2958,6 +2978,9 @@ class HttpCli(object):
|
|||
if not pwd:
|
||||
raise Pebkac(422, "password cannot be blank")
|
||||
|
||||
if un:
|
||||
pwd = "%s:%s" % (un, pwd)
|
||||
|
||||
dst = self.args.SRS
|
||||
if self.vpath:
|
||||
dst += quotep(self.vpaths)
|
||||
|
@ -3038,6 +3061,19 @@ class HttpCli(object):
|
|||
|
||||
return dur > 0, msg
|
||||
|
||||
def set_idp_cookie(self, ases) -> None:
|
||||
k = "cppws" if self.is_https else "cppwd"
|
||||
ck = gencookie(
|
||||
k,
|
||||
ases,
|
||||
self.args.R,
|
||||
self.args.cookie_lax,
|
||||
self.is_https,
|
||||
self.args.idp_cookie,
|
||||
"; HttpOnly",
|
||||
)
|
||||
self.out_headers["Set-Cookie"] = ck
|
||||
|
||||
def handle_mkdir(self) -> bool:
|
||||
assert self.parser # !rm
|
||||
new_dir = self.parser.require("name", 512)
|
||||
|
@ -3576,7 +3612,7 @@ class HttpCli(object):
|
|||
rem = "{}/{}".format(rp, fn).strip("/")
|
||||
dbv, vrem = vfs.get_dbv(rem)
|
||||
|
||||
if not rem.endswith(".md") and not self.can_delete:
|
||||
if not rem.lower().endswith(".md") and not self.can_delete:
|
||||
raise Pebkac(400, "only markdown pls")
|
||||
|
||||
if nullwrite:
|
||||
|
@ -3660,6 +3696,9 @@ class HttpCli(object):
|
|||
if p_field != "body":
|
||||
raise Pebkac(400, "expected body, got {}".format(p_field))
|
||||
|
||||
if "txt_eol" in vfs.flags:
|
||||
p_data = eol_conv(p_data, vfs.flags["txt_eol"])
|
||||
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu:
|
||||
if not runhook(
|
||||
|
@ -4630,7 +4669,9 @@ class HttpCli(object):
|
|||
else:
|
||||
fn = self.host.split(":")[0]
|
||||
|
||||
if vn.flags.get("zipmax") and (not self.uname or not "zipmaxu" in vn.flags):
|
||||
if vn.flags.get("zipmax") and not (
|
||||
vn.flags.get("zipmaxu") and self.uname != "*"
|
||||
):
|
||||
maxs = vn.flags.get("zipmaxs_v") or 0
|
||||
maxn = vn.flags.get("zipmaxn_v") or 0
|
||||
nf = 0
|
||||
|
@ -4684,7 +4725,7 @@ class HttpCli(object):
|
|||
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
||||
cfmt = ""
|
||||
if self.thumbcli and not self.args.no_bacode:
|
||||
for zs in ("opus", "mp3", "w", "j", "p"):
|
||||
for zs in ("opus", "mp3", "flac", "wav", "w", "j", "p"):
|
||||
if zs in self.ouparam or uarg == zs:
|
||||
cfmt = zs
|
||||
|
||||
|
@ -5024,7 +5065,7 @@ class HttpCli(object):
|
|||
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
|
||||
|
||||
fmt = self.uparam.get("ls", "")
|
||||
if not fmt and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
||||
if not fmt and self.ua.startswith(("curl/", "fetch")):
|
||||
fmt = "v"
|
||||
|
||||
if fmt in ["v", "t", "txt"]:
|
||||
|
@ -5064,6 +5105,13 @@ class HttpCli(object):
|
|||
self.reply(zb, mime="text/plain; charset=utf-8")
|
||||
return True
|
||||
|
||||
re_btn = ""
|
||||
nre = self.args.ctl_re
|
||||
if "re" in self.uparam:
|
||||
self.out_headers["Refresh"] = str(nre)
|
||||
elif nre:
|
||||
re_btn = "&re=%s" % (nre,)
|
||||
|
||||
html = self.j2s(
|
||||
"splash",
|
||||
this=self,
|
||||
|
@ -5081,6 +5129,7 @@ class HttpCli(object):
|
|||
mtpq=vs["mtpq"],
|
||||
dbwt=vs["dbwt"],
|
||||
url_suf=suf,
|
||||
re=re_btn,
|
||||
k304=self.k304(),
|
||||
no304=self.no304(),
|
||||
k304vis=self.args.k304 > 0,
|
||||
|
@ -5126,7 +5175,7 @@ class HttpCli(object):
|
|||
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p><a id="r" href="{}/?h">go home</a></p>'
|
||||
pt = "404 not found ┐( ´ -`)┌"
|
||||
|
||||
if self.ua.startswith("curl/") or self.ua.startswith("fetch"):
|
||||
if self.ua.startswith(("curl/", "fetch")):
|
||||
pt = "# acct: %s\n%s\n" % (self.uname, pt)
|
||||
self.reply(pt.encode("utf-8"), status=rc)
|
||||
return True
|
||||
|
@ -5440,6 +5489,8 @@ class HttpCli(object):
|
|||
elif nfi == 3:
|
||||
if not vp.endswith(vfi):
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
n -= 1
|
||||
if not n:
|
||||
|
@ -5564,6 +5615,8 @@ class HttpCli(object):
|
|||
elif nfi == 3:
|
||||
if not vp.endswith(vfi):
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
if not dots and "/." in vp:
|
||||
continue
|
||||
|
@ -5994,6 +6047,12 @@ class HttpCli(object):
|
|||
else:
|
||||
[x.pop(k) for k in ["name", "dt"] for y in [dirs, files] for x in y]
|
||||
|
||||
# nonce (tlnote: norwegian for flake as in snowflake)
|
||||
if self.args.no_fnugg:
|
||||
ls["fnugg"] = "nei"
|
||||
elif "fnugg" in self.headers:
|
||||
ls["fnugg"] = self.headers["fnugg"]
|
||||
|
||||
ret = json.dumps(ls)
|
||||
mime = "application/json"
|
||||
|
||||
|
@ -6176,7 +6235,8 @@ class HttpCli(object):
|
|||
if not use_filekey:
|
||||
return self.tx_404(True)
|
||||
|
||||
if add_og and not abspath.lower().endswith(".md"):
|
||||
is_md = abspath.lower().endswith(".md")
|
||||
if add_og and not is_md:
|
||||
if og_ua or self.host not in self.headers.get("referer", ""):
|
||||
self.vpath, og_fn = vsplit(self.vpath)
|
||||
vpath = self.vpath
|
||||
|
@ -6188,10 +6248,10 @@ class HttpCli(object):
|
|||
vpnodes.pop()
|
||||
|
||||
if (
|
||||
(abspath.endswith(".md") or self.can_delete)
|
||||
(is_md or self.can_delete)
|
||||
and "nohtml" not in vn.flags
|
||||
and (
|
||||
("v" in self.uparam and abspath.endswith(".md"))
|
||||
(is_md and "v" in self.uparam)
|
||||
or "edit" in self.uparam
|
||||
or "edit2" in self.uparam
|
||||
)
|
||||
|
@ -6248,11 +6308,7 @@ class HttpCli(object):
|
|||
is_ls = "ls" in self.uparam
|
||||
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
||||
|
||||
if (
|
||||
not is_ls
|
||||
and not add_og
|
||||
and (self.ua.startswith("curl/") or self.ua.startswith("fetch"))
|
||||
):
|
||||
if not is_ls and not add_og and self.ua.startswith(("curl/", "fetch")):
|
||||
self.uparam["ls"] = "v"
|
||||
is_ls = True
|
||||
|
||||
|
|
|
@ -324,7 +324,8 @@ class HttpSrv(object):
|
|||
spins = 0
|
||||
while self.ncli >= self.nclimax:
|
||||
if not spins:
|
||||
self.log(self.name, "at connection limit; waiting", 3)
|
||||
t = "at connection limit (global-option 'nc'); waiting"
|
||||
self.log(self.name, t, 3)
|
||||
|
||||
spins += 1
|
||||
time.sleep(0.1)
|
||||
|
|
|
@ -67,6 +67,8 @@ HAVE_FFPROBE = not os.environ.get("PRTY_NO_FFPROBE") and have_ff("ffprobe")
|
|||
CBZ_PICS = set("png jpg jpeg gif bmp tga tif tiff webp avif".split())
|
||||
CBZ_01 = re.compile(r"(^|[^0-9v])0+[01]\b")
|
||||
|
||||
FMT_AU = set("mp3 ogg flac wav".split())
|
||||
|
||||
|
||||
class MParser(object):
|
||||
def __init__(self, cmdline: str) -> None:
|
||||
|
@ -242,7 +244,7 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[
|
|||
ret: dict[str, Any] = {} # processed
|
||||
md: dict[str, list[Any]] = {} # raw tags
|
||||
|
||||
is_audio = fmt.get("format_name") in ["mp3", "ogg", "flac", "wav"]
|
||||
is_audio = fmt.get("format_name") in FMT_AU
|
||||
if fmt.get("filename", "").split(".")[-1].lower() in ["m4a", "aac"]:
|
||||
is_audio = True
|
||||
|
||||
|
@ -270,6 +272,8 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[
|
|||
["channel_layout", "chs"],
|
||||
["sample_rate", ".hz"],
|
||||
["bit_rate", ".aq"],
|
||||
["bits_per_sample", ".bps"],
|
||||
["bits_per_raw_sample", ".bprs"],
|
||||
["duration", ".dur"],
|
||||
]
|
||||
|
||||
|
|
|
@ -183,11 +183,7 @@ class MCast(object):
|
|||
srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False)
|
||||
|
||||
# gvfs breaks if a linklocal ip appears in a dns reply
|
||||
ll = {
|
||||
k: v
|
||||
for k, v in srv.ips.items()
|
||||
if k.startswith("169.254") or k.startswith("fe80")
|
||||
}
|
||||
ll = {k: v for k, v in srv.ips.items() if k.startswith(("169.254", "fe80"))}
|
||||
rt = {k: v for k, v in srv.ips.items() if k not in ll}
|
||||
|
||||
if self.args.ll or not rt:
|
||||
|
|
|
@ -147,6 +147,10 @@ class PWHash(object):
|
|||
def cli(self) -> None:
|
||||
import getpass
|
||||
|
||||
if self.args.usernames:
|
||||
t = "since you have enabled --usernames, please provide username:password"
|
||||
print(t)
|
||||
|
||||
while True:
|
||||
try:
|
||||
p1 = getpass.getpass("password> ")
|
||||
|
|
|
@ -850,15 +850,6 @@ class SvcHub(object):
|
|||
|
||||
def _check_env(self) -> None:
|
||||
al = self.args
|
||||
try:
|
||||
files = os.listdir(E.cfg)
|
||||
except:
|
||||
files = []
|
||||
|
||||
hits = [x for x in files if x.lower().endswith(".conf")]
|
||||
if hits:
|
||||
t = "WARNING: found config files in [%s]: %s\n config files are not expected here, and will NOT be loaded (unless your setup is intentionally hella funky)"
|
||||
self.log("root", t % (E.cfg, ", ".join(hits)), 3)
|
||||
|
||||
if self.args.no_bauth:
|
||||
t = "WARNING: --no-bauth disables support for the Android app; you may want to use --bauth-last instead"
|
||||
|
@ -868,7 +859,7 @@ class SvcHub(object):
|
|||
|
||||
have_tcp = False
|
||||
for zs in al.i:
|
||||
if not zs.startswith("unix:"):
|
||||
if not zs.startswith(("unix:", "fd:")):
|
||||
have_tcp = True
|
||||
if not have_tcp:
|
||||
zb = False
|
||||
|
@ -878,7 +869,7 @@ class SvcHub(object):
|
|||
setattr(al, zs, False)
|
||||
zb = True
|
||||
if zb:
|
||||
t = "only listening on unix-sockets; cannot enable zeroconf/mdns/ssdp as requested"
|
||||
t = "not listening on any ip-addresses (only unix-sockets and/or FDs); cannot enable zeroconf/mdns/ssdp as requested"
|
||||
self.log("root", t, 3)
|
||||
|
||||
if not self.args.no_dav:
|
||||
|
|
|
@ -25,8 +25,8 @@ from .util import (
|
|||
termsize,
|
||||
)
|
||||
|
||||
if True:
|
||||
from typing import Generator, Union
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Generator, Optional, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .svchub import SvcHub
|
||||
|
@ -245,8 +245,10 @@ class TcpSrv(object):
|
|||
|
||||
def _listen(self, ip: str, port: int) -> None:
|
||||
uds_perm = uds_gid = -1
|
||||
bound: Optional[socket.socket] = None
|
||||
tcp = False
|
||||
|
||||
if "unix:" in ip:
|
||||
tcp = False
|
||||
ipv = socket.AF_UNIX
|
||||
uds = ip.split(":")
|
||||
ip = uds[-1]
|
||||
|
@ -259,7 +261,12 @@ class TcpSrv(object):
|
|||
import grp
|
||||
|
||||
uds_gid = grp.getgrnam(uds[2]).gr_gid
|
||||
elif "fd:" in ip:
|
||||
fd = ip[3:]
|
||||
bound = socket.socket(fileno=int(fd))
|
||||
|
||||
tcp = bound.proto == socket.IPPROTO_TCP
|
||||
ipv = bound.family
|
||||
elif ":" in ip:
|
||||
tcp = True
|
||||
ipv = socket.AF_INET6
|
||||
|
@ -267,7 +274,7 @@ class TcpSrv(object):
|
|||
tcp = True
|
||||
ipv = socket.AF_INET
|
||||
|
||||
srv = socket.socket(ipv, socket.SOCK_STREAM)
|
||||
srv = bound or socket.socket(ipv, socket.SOCK_STREAM)
|
||||
|
||||
if not ANYWIN or self.args.reuseaddr:
|
||||
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
@ -285,6 +292,10 @@ class TcpSrv(object):
|
|||
if getattr(self.args, "freebind", False):
|
||||
srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1)
|
||||
|
||||
if bound:
|
||||
self.srv.append(srv)
|
||||
return
|
||||
|
||||
try:
|
||||
if tcp:
|
||||
srv.bind((ip, port))
|
||||
|
@ -437,7 +448,7 @@ class TcpSrv(object):
|
|||
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 "unix:" not in x]
|
||||
listen_ips = [x for x in listen_ips if not x.startswith(("unix:", "fd:"))]
|
||||
|
||||
nics = get_adapters(True)
|
||||
eps: dict[str, Netdev] = {}
|
||||
|
@ -583,8 +594,7 @@ class TcpSrv(object):
|
|||
if not ip:
|
||||
return ""
|
||||
|
||||
if ":" in ip:
|
||||
ip = "[{}]".format(ip)
|
||||
hip = "[%s]" % (ip,) if ":" in ip else ip
|
||||
|
||||
if self.args.http_only:
|
||||
https = ""
|
||||
|
@ -596,7 +606,7 @@ class TcpSrv(object):
|
|||
ports = t1.get(ip, t2.get(ip, []))
|
||||
dport = 443 if https else 80
|
||||
port = "" if dport in ports or not ports else ":{}".format(ports[0])
|
||||
txt = "http{}://{}{}/{}".format(https, ip, port, self.args.qrl)
|
||||
txt = "http{}://{}{}/{}".format(https, hip, port, self.args.qrl)
|
||||
|
||||
btxt = txt.encode("utf-8")
|
||||
if PY2:
|
||||
|
|
|
@ -179,7 +179,7 @@ class Tftpd(object):
|
|||
if "::" in ips:
|
||||
ips.append("0.0.0.0")
|
||||
|
||||
ips = [x for x in ips if "unix:" not in x]
|
||||
ips = [x for x in ips if not x.startswith(("unix:", "fd:"))]
|
||||
|
||||
if self.args.tftp4:
|
||||
ips = [x for x in ips if ":" not in x]
|
||||
|
|
|
@ -88,7 +88,7 @@ class ThumbCli(object):
|
|||
if rem.startswith(".hist/th/") and rem.split(".")[-1] in ["webp", "jpg", "png"]:
|
||||
return os.path.join(ptop, rem)
|
||||
|
||||
if fmt[:1] in "jw":
|
||||
if fmt[:1] in "jw" and fmt != "wav":
|
||||
sfmt = fmt[:1]
|
||||
|
||||
if sfmt == "j" and self.args.th_no_jpg:
|
||||
|
@ -129,7 +129,7 @@ class ThumbCli(object):
|
|||
|
||||
tpath = thumb_path(histpath, rem, mtime, fmt, self.fmt_ffa)
|
||||
tpaths = [tpath]
|
||||
if fmt[:1] == "w":
|
||||
if fmt[:1] == "w" and fmt != "wav":
|
||||
# also check for jpg (maybe webp is unavailable)
|
||||
tpaths.append(tpath.rsplit(".", 1)[0] + ".jpg")
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ HAVE_AVIF = False
|
|||
HAVE_WEBP = False
|
||||
|
||||
EXTS_TH = set(["jpg", "webp", "png"])
|
||||
EXTS_AC = set(["opus", "owa", "caf", "mp3"])
|
||||
EXTS_AC = set(["opus", "owa", "caf", "mp3", "flac", "wav"])
|
||||
EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split())
|
||||
|
||||
PTN_TS = re.compile("^-?[0-9a-f]{8,10}$")
|
||||
|
@ -355,8 +355,10 @@ class ThumbSrv(object):
|
|||
tex = tpath.rsplit(".", 1)[-1]
|
||||
want_mp3 = tex == "mp3"
|
||||
want_opus = tex in ("opus", "owa", "caf")
|
||||
want_flac = tex == "flac"
|
||||
want_wav = tex == "wav"
|
||||
want_png = tex == "png"
|
||||
want_au = want_mp3 or want_opus
|
||||
want_au = want_mp3 or want_opus or want_flac or want_wav
|
||||
for lib in self.args.th_dec:
|
||||
can_au = lib == "ff" and (
|
||||
ext in self.fmt_ffa or ext in self.fmt_ffv
|
||||
|
@ -371,6 +373,10 @@ class ThumbSrv(object):
|
|||
funs.append(self.conv_opus)
|
||||
elif want_mp3:
|
||||
funs.append(self.conv_mp3)
|
||||
elif want_flac:
|
||||
funs.append(self.conv_flac)
|
||||
elif want_wav:
|
||||
funs.append(self.conv_wav)
|
||||
elif want_png:
|
||||
funs.append(self.conv_waves)
|
||||
png_ok = True
|
||||
|
@ -807,6 +813,66 @@ class ThumbSrv(object):
|
|||
# fmt: on
|
||||
self._run_ff(cmd, vn, oom=300)
|
||||
|
||||
def conv_flac(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||
if self.args.no_acode or not self.args.allow_flac:
|
||||
raise Exception("flac not permitted in server config")
|
||||
|
||||
self.wait4ram(0.2, tpath)
|
||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||
if "ac" not in tags:
|
||||
raise Exception("not audio")
|
||||
|
||||
self.log("conv2 flac", 6)
|
||||
|
||||
# fmt: off
|
||||
cmd = [
|
||||
b"ffmpeg",
|
||||
b"-nostdin",
|
||||
b"-v", b"error",
|
||||
b"-hide_banner",
|
||||
b"-i", fsenc(abspath),
|
||||
b"-map", b"0:a:0",
|
||||
b"-c:a", b"flac",
|
||||
fsenc(tpath)
|
||||
]
|
||||
# fmt: on
|
||||
self._run_ff(cmd, vn, oom=300)
|
||||
|
||||
def conv_wav(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||
if self.args.no_acode or not self.args.allow_wav:
|
||||
raise Exception("wav not permitted in server config")
|
||||
|
||||
self.wait4ram(0.2, tpath)
|
||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||
if "ac" not in tags:
|
||||
raise Exception("not audio")
|
||||
|
||||
bits = tags[".bps"][1]
|
||||
if bits == 0.0:
|
||||
bits = tags[".bprs"][1]
|
||||
|
||||
codec = b"pcm_s32le"
|
||||
if bits <= 16.0:
|
||||
codec = b"pcm_s16le"
|
||||
elif bits <= 24.0:
|
||||
codec = b"pcm_s24le"
|
||||
|
||||
self.log("conv2 wav", 6)
|
||||
|
||||
# fmt: off
|
||||
cmd = [
|
||||
b"ffmpeg",
|
||||
b"-nostdin",
|
||||
b"-v", b"error",
|
||||
b"-hide_banner",
|
||||
b"-i", fsenc(abspath),
|
||||
b"-map", b"0:a:0",
|
||||
b"-c:a", codec,
|
||||
fsenc(tpath)
|
||||
]
|
||||
# fmt: on
|
||||
self._run_ff(cmd, vn, oom=300)
|
||||
|
||||
def conv_opus(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||
if self.args.no_acode or not self.args.q_opus:
|
||||
raise Exception("disabled in server config")
|
||||
|
|
|
@ -86,7 +86,10 @@ if TYPE_CHECKING:
|
|||
from .svchub import SvcHub
|
||||
|
||||
zsg = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png,tga,tif,tiff,webp"
|
||||
CV_EXTS = set(zsg.split(","))
|
||||
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 = "nohash noidx xdev xvol"
|
||||
VF_AFFECTS_INDEXING = set(zsg.split(" "))
|
||||
|
@ -372,11 +375,12 @@ class Up2k(object):
|
|||
if ineed == ihash or not ineed:
|
||||
continue
|
||||
|
||||
poke = job["poke"]
|
||||
zt = (
|
||||
ineed / ihash,
|
||||
job["size"],
|
||||
int(job["t0c"]),
|
||||
int(job["poke"]),
|
||||
int(job.get("t0c", poke)),
|
||||
int(poke),
|
||||
djoin(vtop, job["prel"], job["name"]),
|
||||
)
|
||||
ret.append(zt)
|
||||
|
@ -1478,7 +1482,7 @@ class Up2k(object):
|
|||
unreg: list[str] = []
|
||||
files: list[tuple[int, int, str]] = []
|
||||
fat32 = True
|
||||
cv = ""
|
||||
cv = vcv = ""
|
||||
|
||||
th_cvd = self.args.th_coversd
|
||||
th_cvds = self.args.th_coversd_set
|
||||
|
@ -1573,25 +1577,24 @@ class Up2k(object):
|
|||
|
||||
rsz += sz
|
||||
files.append((sz, lmod, iname))
|
||||
liname = iname.lower()
|
||||
if (
|
||||
sz
|
||||
and (
|
||||
if sz:
|
||||
liname = iname.lower()
|
||||
ext = liname.rsplit(".", 1)[-1]
|
||||
if (
|
||||
liname in th_cvds
|
||||
or (
|
||||
not cv
|
||||
and liname.rsplit(".", 1)[-1] in CV_EXTS
|
||||
and not iname.startswith(".")
|
||||
)
|
||||
)
|
||||
and (
|
||||
or (not cv and ext in ICV_EXTS and not iname.startswith("."))
|
||||
) and (
|
||||
not cv
|
||||
or liname not in th_cvds
|
||||
or cv.lower() not in th_cvds
|
||||
or th_cvd.index(liname) < th_cvd.index(cv.lower())
|
||||
)
|
||||
):
|
||||
cv = iname
|
||||
):
|
||||
cv = iname
|
||||
elif not vcv and ext in VCV_EXTS and not iname.startswith("."):
|
||||
vcv = iname
|
||||
|
||||
if not cv:
|
||||
cv = vcv
|
||||
|
||||
if not self.args.no_dirsz:
|
||||
tnf += len(files)
|
||||
|
|
|
@ -2982,6 +2982,17 @@ def justcopy(
|
|||
return tlen, "checksum-disabled", "checksum-disabled"
|
||||
|
||||
|
||||
def eol_conv(
|
||||
fin: Generator[bytes, None, None], conv: str
|
||||
) -> Generator[bytes, None, None]:
|
||||
crlf = conv.lower() == "crlf"
|
||||
for buf in fin:
|
||||
buf = buf.replace(b"\r", b"")
|
||||
if crlf:
|
||||
buf = buf.replace(b"\n", b"\r\n")
|
||||
yield buf
|
||||
|
||||
|
||||
def hashcopy(
|
||||
fin: Generator[bytes, None, None],
|
||||
fout: Union[typing.BinaryIO, typing.IO[Any]],
|
||||
|
|
|
@ -1113,18 +1113,7 @@ html.y #widget.open {
|
|||
top: -.12em;
|
||||
}
|
||||
#wtico {
|
||||
cursor: url(dd/4.png), pointer;
|
||||
animation: cursor 500ms;
|
||||
}
|
||||
#wtico:hover {
|
||||
animation: cursor 500ms infinite;
|
||||
}
|
||||
@keyframes cursor {
|
||||
0% {cursor: url(dd/2.png), pointer}
|
||||
30% {cursor: url(dd/3.png), pointer}
|
||||
50% {cursor: url(dd/4.png), pointer}
|
||||
75% {cursor: url(dd/5.png), pointer}
|
||||
85% {cursor: url(dd/4.png), pointer}
|
||||
cursor: pointer;
|
||||
}
|
||||
@keyframes spin {
|
||||
100% {transform: rotate(360deg)}
|
||||
|
@ -1385,6 +1374,7 @@ html.y #ops svg circle {
|
|||
#op_cfg input[type=text] {
|
||||
top: -.3em;
|
||||
}
|
||||
.opview select,
|
||||
.opview input[type=text] {
|
||||
color: var(--fg);
|
||||
background: var(--txt-bg);
|
||||
|
@ -1395,6 +1385,10 @@ html.y #ops svg circle {
|
|||
border-radius: .2em;
|
||||
padding: .2em .3em;
|
||||
}
|
||||
.opview select {
|
||||
padding: .3em;
|
||||
margin: .2em .4em;
|
||||
}
|
||||
.opview input.err {
|
||||
color: var(--err-fg);
|
||||
background: var(--err-bg);
|
||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
Before Width: | Height: | Size: 258 B |
Binary file not shown.
Before Width: | Height: | Size: 252 B |
Binary file not shown.
Before Width: | Height: | Size: 248 B |
Binary file not shown.
Before Width: | Height: | Size: 250 B |
|
@ -255,7 +255,7 @@ function Modpoll() {
|
|||
}
|
||||
|
||||
console.log('modpoll...');
|
||||
var url = (document.location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var url = (location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
@ -346,7 +346,7 @@ function save(e) {
|
|||
fd.append("lastmod", (force ? -1 : last_modified));
|
||||
fd.append("body", txt);
|
||||
|
||||
var url = (document.location + '').split('?')[0];
|
||||
var url = (location + '').split('?')[0];
|
||||
var xhr = new XHR();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
@ -404,7 +404,7 @@ function save_cb() {
|
|||
|
||||
function run_savechk(lastmod, txt, btn, ntry) {
|
||||
// download the saved doc from the server and compare
|
||||
var url = (document.location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var url = (location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
|
|
@ -6,7 +6,7 @@ var dom_doc = ebi('m');
|
|||
var dom_md = ebi('mt');
|
||||
|
||||
(function () {
|
||||
var n = document.location + '';
|
||||
var n = location + '';
|
||||
n = (n.slice(n.indexOf('//') + 2).split('?')[0] + '?v').split('/');
|
||||
n[0] = 'top';
|
||||
var loc = [];
|
||||
|
@ -113,7 +113,7 @@ function save(mde) {
|
|||
fd.append("lastmod", (force ? -1 : last_modified));
|
||||
fd.append("body", txt);
|
||||
|
||||
var url = (document.location + '').split('?')[0];
|
||||
var url = (location + '').split('?')[0];
|
||||
var xhr = new XHR();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
@ -166,7 +166,7 @@ function save_cb() {
|
|||
//alert('save OK -- wrote ' + r.size + ' bytes.\n\nsha512: ' + r.sha512);
|
||||
|
||||
// download the saved doc from the server and compare
|
||||
var url = (document.location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var url = (location + '').split('?')[0] + '?_=' + Date.now();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
|
|
@ -19,14 +19,7 @@
|
|||
<a href="{{ r }}/?h">control-panel</a>
|
||||
Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
|
||||
<span id="hits"></span>
|
||||
<table id="tab"><thead><tr>
|
||||
<th>size</th>
|
||||
<th>who</th>
|
||||
<th>when</th>
|
||||
<th>age</th>
|
||||
<th>dir</th>
|
||||
<th>file</th>
|
||||
</tr></thead><tbody id="tb"></tbody></table>
|
||||
<div id="tw"></div>
|
||||
</div>
|
||||
<a href="#" id="repl">π</a>
|
||||
<script>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
function render() {
|
||||
var ups = V.ups, now = V.now, html = [];
|
||||
var html = ['<table id="tab"><thead><tr><th>size</th><th>who</th><th>when</th><th>age</th><th>dir</th><th>file</th></tr></thead><tbody>'];
|
||||
var ups = V.ups, now = V.now;
|
||||
ebi('filter').value = V.filter;
|
||||
ebi('hits').innerHTML = 'showing ' + ups.length + ' files';
|
||||
|
||||
|
@ -26,7 +27,8 @@ function render() {
|
|||
var t = V.filter ? ' matching the filter' : '';
|
||||
html = ['<tr><td colspan="6">there are no uploads' + t + '</td></tr>'];
|
||||
}
|
||||
ebi('tb').innerHTML = html.join('');
|
||||
html.push('</tbody></table>');
|
||||
ebi('tw').innerHTML = html.join('\n');
|
||||
}
|
||||
render();
|
||||
|
||||
|
@ -46,7 +48,7 @@ function ask(e) {
|
|||
V = JSON.parse(this.responseText)
|
||||
}
|
||||
catch (ex) {
|
||||
ebi('tb').innerHTML = '<tr><td colspan="6">failed to decode server response as json: <pre>' + esc(this.responseText) + '</pre></td></tr>';
|
||||
ebi('tw').innerHTML = 'failed to decode server response as json: <pre>' + esc(this.responseText) + '</pre>';
|
||||
return;
|
||||
}
|
||||
render();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
var SRS = SR.trimEnd('/') + '/';
|
||||
|
||||
var t = QSA('a[k]');
|
||||
for (var a = 0; a < t.length; a++)
|
||||
t[a].onclick = rm;
|
||||
|
||||
function rm() {
|
||||
var u = SRS + '?eshare=rm&skey=' + uricom_enc(this.getAttribute('k')),
|
||||
var u = SR + '/?eshare=rm&skey=' + uricom_enc(this.getAttribute('k')),
|
||||
xhr = new XHR();
|
||||
|
||||
xhr.open('POST', u, true);
|
||||
|
@ -15,7 +13,7 @@ function rm() {
|
|||
|
||||
function bump() {
|
||||
var k = this.closest('tr').getElementsByTagName('a')[2].getAttribute('k'),
|
||||
u = SRS + '?skey=' + uricom_enc(k) + '&eshare=' + this.value,
|
||||
u = SR + '/?skey=' + uricom_enc(k) + '&eshare=' + this.value,
|
||||
xhr = new XHR();
|
||||
|
||||
xhr.open('POST', u, true);
|
||||
|
@ -27,7 +25,7 @@ function cb() {
|
|||
if (this.status !== 200)
|
||||
return modal.alert('<h6>server error</h6>' + esc(unpre(this.responseText)));
|
||||
|
||||
document.location = '?shares';
|
||||
location = '?shares';
|
||||
}
|
||||
|
||||
function qr(e) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<body>
|
||||
<div id="wrap">
|
||||
{%- if not in_shr %}
|
||||
<a id="a" href="{{ r }}/?h" class="af">refresh</a>
|
||||
<a id="a" href="{{ r }}/?h{{ re }}" class="af">refresh</a>
|
||||
<a id="v" href="{{ r }}/?hc" class="af">connect</a>
|
||||
|
||||
{%- if this.uname == '*' %}
|
||||
|
@ -120,7 +120,12 @@
|
|||
<div>
|
||||
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
||||
<input type="hidden" id="la" name="act" value="login" />
|
||||
{% if this.args.usernames %}
|
||||
<input type="text" id="lu" name="uname" placeholder=" username" size="12" />
|
||||
<input type="password" id="lp" name="cppwd" placeholder=" password" size="12" />
|
||||
{% else %}
|
||||
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
||||
{% endif %}
|
||||
<input type="hidden" name="uhash" id="uhash" value="x" />
|
||||
<input type="submit" id="ls" value="login" />
|
||||
{% if chpw %}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// please add translations in alphabetic order, but keep "nor" and "eng" first
|
||||
// (lines ending with //m are machine translations)
|
||||
var Ls = {
|
||||
"nor": {
|
||||
"a1": "oppdater",
|
||||
|
@ -93,6 +94,47 @@ var Ls = {
|
|||
"af1": "显示最近上传的文件", //m
|
||||
"ag1": "查看已知 IdP 用户", //m
|
||||
},
|
||||
"cze": {
|
||||
"a1": "obnovit",
|
||||
"b1": "ahoj cizinče <small>(nejsi přihlášen)</small>",
|
||||
"c1": "odhlásit se",
|
||||
"d1": "vypsat zásobníku", // TLNote: "d2" is the tooltip for this button
|
||||
"d2": "zobrazit stav všech aktivních vláken",
|
||||
"e1": "znovu načíst konfiguraci",
|
||||
"e2": "znovu načíst konfigurační soubory (accounts/volumes/volflags),$Na prohledat všechny e2ds úložiště$N$Npoznámka: všechny změny globálních nastavení$Nvyžadují úplné restartování, aby se projevily",
|
||||
"f1": "můžeš procházet:",
|
||||
"g1": "můžeš nahrávat do:",
|
||||
"cc1": "další věci:",
|
||||
"h1": "zakázat k304", // TLNote: "j1" explains what k304 is
|
||||
"i1": "povolit k304",
|
||||
"j1": "povolení k304 odpojí vašeho klienta při každém HTTP 304, což může zabránit některým chybovým proxy serverům, aby se zasekly (náhle nenačítaly stránky), <em>ale</em> také to obecně zpomalí věci",
|
||||
"k1": "resetovat nastavení klienta",
|
||||
"l1": "přihlaste se pro více:",
|
||||
"m1": "vítej zpět,", // TLNote: "welcome back, USERNAME"
|
||||
"n1": "404 nenalezeno ┐( ´ -`)┌",
|
||||
"o1": 'nebo možná nemáš přístup -- zkus heslo nebo <a href="' + SR + '/?h">jdi domů</a>',
|
||||
"p1": "403 zakázáno ~┻━┻",
|
||||
"q1": 'použij heslo nebo <a href="' + SR + '/?h">jdi domů</a>',
|
||||
"r1": "jdi domů",
|
||||
".s1": "znovu prohledat",
|
||||
"t1": "akce", // TLNote: this is the header above the "rescan" buttons
|
||||
"u2": "čas od posledního zápisu na server$N( upload / rename / ... )$N$N17d = 17 dní$N1h23 = 1 hodina 23 minut$N4m56 = 4 minuty 56 sekund",
|
||||
"v1": "připojit",
|
||||
"v2": "použít tento server jako místní HDD",
|
||||
"w1": "přepnout na https",
|
||||
"x1": "změnit heslo",
|
||||
"y1": "upravit sdílení", // TLNote: shows the list of folders that the user has decided to share
|
||||
"z1": "odblokovat toto sdílení:", // TLNote: the password prompt to see a hidden share
|
||||
"ta1": "nejprve vyplňte své nové heslo",
|
||||
"ta2": "zopakujte pro potvrzení nového hesla:",
|
||||
"ta3": "nalezen překlep; zkuste to prosím znovu",
|
||||
"aa1": "příchozí soubory:",
|
||||
"ab1": "deaktivovat no304",
|
||||
"ac1": "povolit no304",
|
||||
"ad1": "povolení no304 deaktivuje veškeré mezipaměti; zkuste to, pokud k304 nestačilo. To ovšem zapříčíní obrovské množství síťového provozu!",
|
||||
"ae1": "aktivní stahování:",
|
||||
"af1": "zobrazit nedávné nahrávání",
|
||||
},
|
||||
"deu": {
|
||||
"a1": "Neu laden",
|
||||
"b1": "Tach, wie geht's? <small>(Du bist nicht angemeldet)</small>",
|
||||
|
@ -173,9 +215,134 @@ var Ls = {
|
|||
"ac1": "ota no304 käyttöön",
|
||||
"ad1": "no304:n lopettaa välimuistin käytön kokonaan; kokeile tätä jos k304 ei riittänyt. Tuhlaa valtavan määrän verkkoliikennettä!",
|
||||
"ae1": "lähtevät:",
|
||||
"af1": "näytä viimeaikaiset lataukset",
|
||||
"af1": "näytä viimeaikaiset lataukset",
|
||||
"ag1": "näytä tunnetut IdP-käyttäjät",
|
||||
},
|
||||
"grc": {
|
||||
"a1": "ανανέωση",
|
||||
"b1": "γεια σου ξένε! <small>(δεν είσαι συνδεδεμένος)</small>",
|
||||
"c1": "αποσύνδεση",
|
||||
"d1": "σωρός απορριμμάτων",
|
||||
"d2": "εμφανίζει την κατάσταση όλων των ενεργών διεργασιών",
|
||||
"e1": "επαναφόρτωση του cfg",
|
||||
"e2": "φορτώνει ξανά τα αρχεία ρυθμίσεων (λογαριασμοί/τόμοι/volflags),$Nκαι κάνει επανεξέταση όλων των τόμων e2ds$N$Nσημείωση: οποιαδήποτε αλλαγή στις καθολικές ρυθμίσεις$Nαπαιτεί πλήρη επανεκκίνηση για να εφαρμοστεί",
|
||||
"f1": "μπορείς να περιηγηθείς:",
|
||||
"g1": "μπορείς να εκτελέσεις μεταφόρτωση σε:",
|
||||
"cc1": "άλλα πράγματα:",
|
||||
"h1": "απενεργοποίση k304",
|
||||
"i1": "ενεργοποίηση k304",
|
||||
"j1": "η ενεργοποίηση του k304 θα αποσυνδέσει το πρόγραμμα πελάτη σου σε κάθε HTTP 304, κάτι που μπορεί να αποτρέψει κάποια προβληματικά proxies από το να κολλάνε (να μην φορτώνουν ξαφνικά σελίδες), <em>αλλά</em> θα κάνει τα πράγματα, γενικά πιο αργά",
|
||||
"k1": "επαναφορά ρυθμίσεων στο πρόγραμμα πελάτη",
|
||||
"l1": "συνδέσου για περισσότερα:",
|
||||
"m1": "καλώς ήρθες,",
|
||||
"n1": "404 δεν βρέθηκε ┐( ´ -`)┌",
|
||||
"o1": '´η μήπως δεν έχεις πρόσβαση -- δοκίμασε έναν κωδικό <a href="' + SR + '/?h">πήγαινε στην αρχική</a>',
|
||||
"p1": "403 απαγορευμένο ~┻━┻",
|
||||
"q1": 'δοκίμασε έναν κωδικό <a href="' + SR + '/?h">πήγαινε στην αρχική</a>',
|
||||
"r1": "πίσω στην αρχική",
|
||||
".s1": "επανάληψη σάρωσης",
|
||||
"t1": "ενέργεια",
|
||||
"u2": "χρόνος από την τελευταία εγγραφή του διακομιστή$N( μεταφόρτωση / μετονομασία / ... )$N$N17d = 17 days$N1ω23 = 1 ώρα 23 λεπτά$N4λ56 = 4 λεπτά 56 δευτερόλεπτα",
|
||||
"v1": "σύνδεση",
|
||||
"v2": "χρησιμοποίησε αυτόν το διακομιστή σαν τοπικό δίσκο",
|
||||
"w1": "εναλλαγή σε https",
|
||||
"x1": "αλλαγή κωδικού",
|
||||
"y1": "επεξεργασία κοινόχρηστων φακέλων",
|
||||
"z1": "ξεκλείδωμα αυτού του κοινόχρηστου φακέλου:",
|
||||
"ta1": "συμπλήρωσε πρώτα το νέο σου κωδικό",
|
||||
"ta2": "επανέλαβε για να επιβεβαιώσεις το νέο κωδικό:",
|
||||
"ta3": "βρέθηκε τυπογραφικό λάθος· δοκίμασε ξανά",
|
||||
"aa1": "εισερχόμενα αρχεία:",
|
||||
"ab1": "απενεργοποίηση no304",
|
||||
"ac1": "ενεργοποίηση no304",
|
||||
"ad1": "η ενεργοποίηση του no304 θα απενεργοποιήσει όλη την προσωρινή αποθήκευση· δοκίμασέ το αν το k304 δεν ήταν αρκετό. Προσοχή, θα σπαταλήσει τεράστιο όγκο δικτυακής κίνησης!",
|
||||
"ae1": "ενεργές μεταφορτώσεις:",
|
||||
"af1": "προβολή πρόσφατων μεταφορτώσεων",
|
||||
},
|
||||
"ita": {
|
||||
"a1": "aggiorna",
|
||||
"b1": "ciao <small>(non sei connesso)</small>",
|
||||
"c1": "disconnetti",
|
||||
"d1": "stato",
|
||||
"d2": "mostra lo stato di tutti i thread attivi",
|
||||
"e1": "ricarica configurazione",
|
||||
"e2": "ricarica i file di configurazione (account/volumi/flag dei volumi),\n e riesegue la scansione di tutti i volumi e2ds.\n\nNota: qualsiasi modifica alle impostazioni globali richiede un riavvio completo per avere effetto",
|
||||
"f1": "puoi visualizzare:",
|
||||
"g1": "puoi caricare su:",
|
||||
"cc1": "altro:",
|
||||
"h1": "disattiva k304",
|
||||
"i1": "attiva k304",
|
||||
"j1": "k304 interrompe la connessione per ogni HTTP 304. Questo aiuta contro alcuni proxy difettosi che possono bloccarsi o smettere improvvisamente di caricare pagine, ma riduce notevolmente le prestazioni",
|
||||
"k1": "resetta impostazioni",
|
||||
"l1": "accedi:",
|
||||
"m1": "bentornato,",
|
||||
"n1": "404: file non trovato ┐( ´ -`)┌",
|
||||
"o1": "oppure forse non hai accesso? prova una password o <a href=\"SR/?h\">torna alla home</a>",
|
||||
"p1": "403: accesso negato ~┻━┻",
|
||||
"q1": "prova una password o <a href=\"SR/?h\">torna alla home</a>",
|
||||
"r1": "torna alla home",
|
||||
".s1": "mappa",
|
||||
"t1": "azione",
|
||||
"u2": "tempo dall'ultima scrittura sul server\n (caricamento / rinomina / ...)\n\n17d = 17 giorni\n1h23 = 1 ora 23 minuti\n4m56 = 4 minuti 56 secondi",
|
||||
"v1": "connetti",
|
||||
"v2": "usa questo server come un disco locale",
|
||||
"w1": "passa a https",
|
||||
"x1": "cambia password",
|
||||
"y1": "le tue condivisioni",
|
||||
"z1": "sblocca area:",
|
||||
"ta1": "devi prima inserire una nuova password",
|
||||
"ta2": "ripeti per confermare la nuova password:",
|
||||
"ta3": "errore di digitazione; riprova",
|
||||
"aa1": "in arrivo:",
|
||||
"ab1": "disattiva no304",
|
||||
"ac1": "attiva no304",
|
||||
"ad1": "no304 disabilita completamente la cache. Se k304 non è sufficiente, prova questa opzione. Aumenterà notevolmente il consumo di dati!",
|
||||
"ae1": "in uscita:",
|
||||
"af1": "mostra i file caricati di recente",
|
||||
"ag1": "mostra utenti IdP conosciuti"
|
||||
},
|
||||
"nld": {
|
||||
"a1": "Update",
|
||||
"b1": "Hallo, hoe gaat het met jou? <small>(Je bent niet ingelogd)</small>",
|
||||
"c1": "Uitloggen",
|
||||
"d1": "Voorwaarde",
|
||||
"d2": "Toont de status van alle actieve threads",
|
||||
"e1": "Configuratie opnieuw laden.",
|
||||
"e2": "Leest configuratiebestanden opnieuw in$N(accounts, volumes, volumeschakelaars)$Nen brengt alle e2ds-volumes in kaart$N$Nopmerking: veranderingen in globale parameters$Nvereist een volledige herstart van de server",
|
||||
"f1": "Je kan het volgende lezen:",
|
||||
"g1": "Je kan naar het volgende uploaden:",
|
||||
"cc1": "Schakelaars en dergelijke:",
|
||||
"h1": "k304 uitschakelen",
|
||||
"i1": "k304 inschakelen",
|
||||
"j1": "k304 verbreekt de verbinding voor elke HTTP 304. Dit helpt tegen bepaalde proxy servers die kunnen vastlopen/plotseling stoppen met het laden van pagina's, maar het vermindert ook de prestaties aanzienlijk",
|
||||
"k1": "Instellingen resetten",
|
||||
"l1": "Inloggen:",
|
||||
"m1": "Welkom terug,",
|
||||
"n1": "404: bestand bestaat niet ┐( ´ -`)┌",
|
||||
"o1": 'of misschien heb je geen toegang? probeer een wachtwoord of <a href="' + SR + '/?h">ga naar startscherm</a>',
|
||||
"p1": "403: toegang geweigerd ~┻━┻",
|
||||
"q1": 'Probeer een wachtwoord of <a href="' + SR + '/?h">ga naar startscherm</a>',
|
||||
"r1": "Ga naar startscherm",
|
||||
".s1": "Kaart",
|
||||
"t1": "Actie",
|
||||
"u2": "Tijd sinds iemand voor het laatst naar de server schreef$N( upload / naamswijziging / ... )$N$N17d = 17 dagen$N1h23 = 1 uur 23 minuten$N4m56 = 4 minuten 56 secondes",
|
||||
"v1": "Verbinden",
|
||||
"v2": "Gebruik deze server als een lokale harde schijf",
|
||||
"w1": "Overschakelen naar https",
|
||||
"x1": "Wachtwoord wijzigen",
|
||||
"y1": "Jou gedeelde items",
|
||||
"z1": "Ontgrendel gebied:",
|
||||
"ta1": "Je moet eerst een nieuw wachtwoord invoeren",
|
||||
"ta2": "Herhaal om nieuw wachtwoord te bevestigen:",
|
||||
"ta3": "Typefout gevonden; probeer het opnieuw",
|
||||
"aa1": "Inkomend:",
|
||||
"ab1": "Schakel nr. 304 uit",
|
||||
"ac1": "Schakel nr. 304 in",
|
||||
"ad1": "Nr. 304 stopt al het cachegebruik. Als k304 niet voldoende was, probeer dan deze. Vermenigvuldigt het dataverbruik.!",
|
||||
"ae1": "Uitgaand:",
|
||||
"af1": "Recent geüploade bestanden weergeven",
|
||||
"ag1": "Bekende IdP-gebruikers weergeven",
|
||||
},
|
||||
"pol": {
|
||||
"a1": "odśwież",
|
||||
"b1": "witaj, nieznajomy <small>(nie jesteś zalogowany)</small>",
|
||||
|
@ -332,7 +499,7 @@ try {
|
|||
catch (ex) { }
|
||||
|
||||
tt.init();
|
||||
var o = QS('input[name="cppwd"]');
|
||||
var o = QS('input[name="uname"]') || QS('input[name="cppwd"]');
|
||||
if (!ebi('c') && o.offsetTop + o.offsetHeight < window.innerHeight)
|
||||
o.focus();
|
||||
|
||||
|
@ -342,6 +509,9 @@ if (o && /[0-9]+$/.exec(o.innerHTML))
|
|||
|
||||
ebi('uhash').value = '' + location.hash;
|
||||
|
||||
if (/\&re=/.test('' + location))
|
||||
ebi('a').className = 'af g';
|
||||
|
||||
(function() {
|
||||
if (!ebi('x'))
|
||||
return;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
{% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint
|
||||
</span>
|
||||
{% if accs %}<a href="#" id="setpw">use real password</a>{% endif %}
|
||||
<a href="#" id="qr">show qr</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
|
|
@ -49,12 +49,14 @@ function setos(os) {
|
|||
setos(WINDOWS ? 'win' : LINUX ? 'lin' : MACOS ? 'mac' : 'idk');
|
||||
|
||||
|
||||
var pw = '';
|
||||
function setpw(e) {
|
||||
ev(e);
|
||||
modal.prompt('password:', '', function (v) {
|
||||
if (!v)
|
||||
return;
|
||||
|
||||
pw = v;
|
||||
var pw0 = ebi('pw0').innerHTML,
|
||||
oa = QSA('b');
|
||||
|
||||
|
@ -67,3 +69,12 @@ function setpw(e) {
|
|||
}
|
||||
if (ebi('setpw'))
|
||||
ebi('setpw').onclick = setpw;
|
||||
|
||||
|
||||
ebi('qr').onclick = function () {
|
||||
var url = ('' + location).split('?')[0];
|
||||
if (pw)
|
||||
url += '?pw=' + pw;
|
||||
var txt = esc(url) + '<img class="b64" width="100" height="100" src="' + addq(url, 'qr') + '" />';
|
||||
modal.alert(txt);
|
||||
};
|
||||
|
|
|
@ -964,6 +964,7 @@ function up2k_init(subtle) {
|
|||
"t": 0
|
||||
},
|
||||
"car": 0,
|
||||
"nre": 0,
|
||||
"slow_io": null,
|
||||
"oserr": false,
|
||||
"modn": 0,
|
||||
|
@ -1572,7 +1573,7 @@ function up2k_init(subtle) {
|
|||
|
||||
function linklist() {
|
||||
var ret = [],
|
||||
base = document.location.origin.replace(/\/$/, '');
|
||||
base = location.origin.replace(/\/$/, '');
|
||||
|
||||
for (var a = 0; a < st.files.length; a++) {
|
||||
var t = st.files[a],
|
||||
|
@ -1595,7 +1596,7 @@ function up2k_init(subtle) {
|
|||
ev(e);
|
||||
var txt = linklist();
|
||||
cliptxt(txt + '\n', function () {
|
||||
toast.inf(5, un_clip.format(txt.split('\n').length));
|
||||
toast.inf(5, L.un_clip.format(txt.split('\n').length));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1783,8 +1784,7 @@ function up2k_init(subtle) {
|
|||
}
|
||||
|
||||
var tasker = (function () {
|
||||
var running = false,
|
||||
was_busy = false;
|
||||
var running = false;
|
||||
|
||||
var defer = function () {
|
||||
running = false;
|
||||
|
@ -1801,7 +1801,17 @@ function up2k_init(subtle) {
|
|||
while (true) {
|
||||
var now = Date.now(),
|
||||
blocktime = now - r.tact,
|
||||
is_busy = st.car < st.files.length;
|
||||
was_busy = st.is_busy,
|
||||
is_busy = !!( // gzip take the wheel
|
||||
st.car < st.files.length ||
|
||||
st.busy.hash.length ||
|
||||
st.todo.hash.length ||
|
||||
st.busy.handshake.length ||
|
||||
st.todo.handshake.length ||
|
||||
st.busy.upload.length ||
|
||||
st.todo.upload.length ||
|
||||
st.busy.head.length ||
|
||||
st.todo.head.length);
|
||||
|
||||
if (blocktime > 2500)
|
||||
console.log('main thread blocked for ' + blocktime);
|
||||
|
@ -1809,7 +1819,16 @@ function up2k_init(subtle) {
|
|||
r.tact = now;
|
||||
|
||||
if (was_busy && !is_busy) {
|
||||
for (var a = 0; a < st.files.length; a++) {
|
||||
var nre = 0, nf = 0;
|
||||
for (var a = 0; a < st.files.length; a++)
|
||||
if (st.files[a].want_recheck)
|
||||
nre++;
|
||||
console.log('nre', nre, 'st', st.nre);
|
||||
if (st.nre != nre) {
|
||||
st.nre = nre;
|
||||
nf = st.files.length;
|
||||
}
|
||||
for (var a = 0; a < nf; a++) {
|
||||
var t = st.files[a];
|
||||
if (t.want_recheck) {
|
||||
t.rechecks++;
|
||||
|
@ -1817,7 +1836,7 @@ function up2k_init(subtle) {
|
|||
push_t(st.todo.handshake, t);
|
||||
}
|
||||
}
|
||||
is_busy = st.todo.handshake.length;
|
||||
is_busy = !!st.todo.handshake.length;
|
||||
try {
|
||||
if (!is_busy && !uc.fsearch && !msel.getsel().length && (!mp.au || mp.au.paused))
|
||||
treectl.goto();
|
||||
|
@ -1826,7 +1845,7 @@ function up2k_init(subtle) {
|
|||
}
|
||||
|
||||
if (was_busy != is_busy) {
|
||||
st.is_busy = was_busy = is_busy;
|
||||
st.is_busy = is_busy;
|
||||
|
||||
window[(is_busy ? "add" : "remove") +
|
||||
"EventListener"]("beforeunload", warn_uploader_busy);
|
||||
|
@ -1947,7 +1966,7 @@ function up2k_init(subtle) {
|
|||
|
||||
for (var a = 0; a < st.files.length; a++) {
|
||||
var t = st.files[a];
|
||||
if (t.want_recheck && !t.rechecks)
|
||||
if (t.want_recheck && t.rechecks < 999)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2693,8 +2712,9 @@ function up2k_init(subtle) {
|
|||
if (ofs !== -1) {
|
||||
err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2).trimEnd()).join(' / ');
|
||||
}
|
||||
if (!t.rechecks && (err_pend || err_srcb)) {
|
||||
if (!t.rechecks)
|
||||
t.rechecks = 0;
|
||||
if (t.rechecks < 999 && (err_pend || err_srcb)) {
|
||||
t.want_recheck = true;
|
||||
if (st.busy.upload.length || st.busy.handshake.length || st.bytes.uploaded) {
|
||||
err = L.u_dupdefer;
|
||||
|
|
|
@ -120,7 +120,7 @@ function esc(txt) {
|
|||
function basenames(txt) {
|
||||
return (txt + '').replace(/https?:\/\/[^ \/]+\//g, '/').replace(/js\?_=[a-zA-Z]{4}/g, 'js');
|
||||
}
|
||||
if ((document.location + '').indexOf(',rej,') + 1)
|
||||
if ((location + '').indexOf(',rej,') + 1)
|
||||
window.onunhandledrejection = function (e) {
|
||||
var err = e.reason;
|
||||
try {
|
||||
|
@ -180,6 +180,9 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||
if (!/\.js($|\?)/.exec(url))
|
||||
return; // chrome debugger
|
||||
|
||||
if (url.indexOf('extension://') + 1)
|
||||
return;
|
||||
|
||||
if (url.indexOf(' > eval') + 1 && !evalex_fatal)
|
||||
return; // md timer
|
||||
|
||||
|
@ -738,7 +741,7 @@ function assert_vp(path) {
|
|||
if (path.indexOf('//') + 1)
|
||||
throw 'nonlocal1: ' + path;
|
||||
|
||||
var o = window.location.origin;
|
||||
var o = location.origin;
|
||||
if (have_URL && (new URL(path, o)).origin != o)
|
||||
throw 'nonlocal2: ' + path;
|
||||
}
|
||||
|
@ -890,7 +893,7 @@ function uricom_adec(arr, li) {
|
|||
|
||||
|
||||
function get_evpath() {
|
||||
var ret = document.location.pathname;
|
||||
var ret = location.pathname;
|
||||
|
||||
if (ret.indexOf('/') !== 0)
|
||||
ret = '/' + ret;
|
||||
|
@ -1017,9 +1020,13 @@ function lhumantime(v) {
|
|||
if (!L || tp.length < 2 || tp[1].indexOf('$') + 1)
|
||||
return t;
|
||||
|
||||
var ret = '';
|
||||
for (var a = 0; a < tp.length; a += 2)
|
||||
ret += tp[a] + ' ' + L['ht_' + tp[a + 1] + (tp[a]==1?1:2)] + L.ht_and;
|
||||
var u, n, ret = '';
|
||||
for (var a = 0; a < tp.length; a += 2) {
|
||||
n = tp[a];
|
||||
u = L.ht_h5 ? (n==1 ? 1 : (n>1&&n<5) ? 2 : 5) :
|
||||
(n==1 ? 1 : 2);
|
||||
ret += tp[a] + ' ' + L['ht_' + tp[a + 1] + u] + L.ht_and;
|
||||
}
|
||||
|
||||
return ret.slice(0, -L.ht_and.length);
|
||||
}
|
||||
|
@ -1246,10 +1253,10 @@ function hist_replace(url) {
|
|||
|
||||
function sethash(hv) {
|
||||
if (window.history && history.replaceState) {
|
||||
hist_replace(document.location.pathname + document.location.search + '#' + hv);
|
||||
hist_replace(location.pathname + location.search + '#' + hv);
|
||||
}
|
||||
else {
|
||||
document.location.hash = hv;
|
||||
location.hash = hv;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,82 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0804-0013 `v1.18.10` idp speedboost
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #426 add Dutch translation (thx @DeStilleGast!) 3798e19a
|
||||
* #458 add Italian translation (thx @AOTREVAI!) a38e6e65
|
||||
* #456 transcode to flac/wav (thx @missaustraliana!) b469db3c b2d48c64 0d09fb68
|
||||
* #439 config-file can be provided through `PRTY_CONFIG` (thx @icxes!) 971360e9
|
||||
* #459 videos can become folder thumbnails 16bbcce5
|
||||
* add `--idp-cookie`, session-tickets for IdP auth (performance boost) f9502c3d
|
||||
* useful when the IdP-server becomes a bottleneck
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #412 fix PUT-uploads into volumes with `nosub` volflag 47fa4a92
|
||||
* #435 ignore spurious exceptions from browser extensions 39e55824
|
||||
* #449 IPv6 QR-Code didn't include port 66a5bf36
|
||||
* #295 do not force `d2d` in blank vfs (introduced in v1.18.3) 848315c0
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* #440 improved finnish translation (thx @icxes!) a68d5b03
|
||||
* point to the `-nc` option in the "at max connections" warning 153d240d
|
||||
* the play-button now indicates "play-as-audio" for video-files 40d56bb3
|
||||
* docs:
|
||||
* #411 improve password-hashing instructions (thx @chinponya!) c69c7c8a
|
||||
* #429 improve `--cert` helptext (thx @kzshantonu!) 7e3825f8
|
||||
* #413 copyparty is Wii Internet Channel compatible! (thx @techflashYT!) 50f16293
|
||||
* #461 how to use groups without IdP e85a7107
|
||||
* mention that WebDAV and OpenGraph are incompatible by default (and how to fix that) 0bc1b8f7
|
||||
* #345 short explanation about the sfx in quickstart ae5eefc5
|
||||
* #398 pypi-package now has extra-group `all` 6eaf8af1
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0801-2056 `v1.18.9` fix Denial-of-Service
|
||||
|
||||
## ⚠️ ATTN: this release fixes a Denial-of-Service vuln
|
||||
|
||||
[CVE-2025-54796](https://github.com/9001/copyparty/security/advisories/GHSA-5662-2rj7-f2v6): an unauthenticated user could make the server grind to a halt by accessing a particular URL
|
||||
|
||||
## recent important news
|
||||
|
||||
* [v1.18.9 (2025-08-01)](https://github.com/9001/copyparty/releases/tag/v1.18.9) fixed [CVE-2025-54796](https://github.com/9001/copyparty/security/advisories/GHSA-5662-2rj7-f2v6) (Denial-of-Service)
|
||||
* [v1.15.0 (2024-09-08)](https://github.com/9001/copyparty/releases/tag/v1.15.0) changed upload deduplication to be default-disabled
|
||||
* [v1.14.3 (2024-08-30)](https://github.com/9001/copyparty/releases/tag/v1.14.3) fixed a bug that was introduced in v1.13.8 (2024-08-13); this bug could lead to **data loss** -- see the v1.14.3 release-notes for details
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #310 translated to Spanish (thx @herruzo99!) a1dfd0be
|
||||
* #350 translated to Ukrainian (thx @MrMebelMan!) fea45e45
|
||||
* #321 translated to Russian (thx @A1Asriel!) 0b05c726
|
||||
* #381 translated to Finnish (thx @icxes and @Permik!) 7ecedb2c
|
||||
* haha it says surf
|
||||
* #312 add option to use localtime in the UI ad23b253
|
||||
* #386 initial packaging for debian (thx @Beethoven-n!) 3c6f0b17
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* CVE-2025-54796 / GHSA-5662-2rj7-f2v6 09910ba8
|
||||
* #347 fix upload-abort when uploading to a share 6d6d79fc
|
||||
* fix xiu backlog dropping on restart 3222ba3a
|
||||
* #375 fix crash on really old versions of python2.7 (thx @bb!) b69d5901
|
||||
* #388 another python2.7 fix: improve unicode support in u2c (thx @KevinXuxuxu!) 9c197535
|
||||
* log creator of new/blank markdown docs d0d2f206
|
||||
* #400 config didn't support indenting with tabs c1604288
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* `ack` was changed to `continue` 4fa7be2a
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* the translations have made the sfx size balloon from 766 to 845 KiB in under a week... nice! keep em coming :tada:
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0731-0833 `v1.18.8` sfx hotfix
|
||||
|
||||
|
|
2138
docs/chungus.conf
Normal file
2138
docs/chungus.conf
Normal file
File diff suppressed because it is too large
Load diff
|
@ -330,7 +330,6 @@ the features you can opt to drop are
|
|||
* `cm`/easymde, the "fancy" markdown editor, saves ~89k
|
||||
* `hl`, prism, the syntax hilighter, saves ~41k
|
||||
* `fnt`, source-code-pro, the monospace font, saves ~9k
|
||||
* `dd`, the custom mouse cursor for the media player tray tab, saves ~2k
|
||||
|
||||
for the `re`pack to work, first run one of the sfx'es once to unpack it
|
||||
|
||||
|
|
216
docs/logo-sq.svg
Normal file
216
docs/logo-sq.svg
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="205mm"
|
||||
height="205mm"
|
||||
viewBox="0 0 205 205"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
sodipodi:docname="logo-sq.svg"
|
||||
inkscape:export-filename="logo-sq.png"
|
||||
inkscape:export-xdpi="126.9"
|
||||
inkscape:export-ydpi="126.9"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.7165"
|
||||
inkscape:cx="367.1"
|
||||
inkscape:cy="434.1"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1022"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer5" />
|
||||
<title
|
||||
id="title1">copyparty_logo</title>
|
||||
<defs
|
||||
id="defs1">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1">
|
||||
<stop
|
||||
style="stop-color:#ffcc55;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop1" />
|
||||
<stop
|
||||
style="stop-color:#ffcc00;stop-opacity:1"
|
||||
offset="0.2"
|
||||
id="stop2" />
|
||||
<stop
|
||||
style="stop-color:#ff8800;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient2"
|
||||
x1="15"
|
||||
y1="15"
|
||||
x2="15"
|
||||
y2="143"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.7149,0,0,1,41.29,0)" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>copyparty_logo</dc:title>
|
||||
<dc:source>github.com/9001/copyparty</dc:source>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
inkscape:label="kassett"
|
||||
transform="translate(-46.02)">
|
||||
<rect
|
||||
style="fill:#333333;stroke-width:1.00023"
|
||||
id="rect1"
|
||||
width="205"
|
||||
height="205"
|
||||
x="46.02"
|
||||
y="0"
|
||||
rx="12.01"
|
||||
ry="12" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2);stroke-width:0.999736"
|
||||
id="rect2"
|
||||
width="193"
|
||||
height="128"
|
||||
x="52.02"
|
||||
y="15"
|
||||
rx="7.995"
|
||||
ry="8" />
|
||||
<rect
|
||||
style="fill:#333333;stroke-width:0.999983"
|
||||
id="rect3"
|
||||
width="142"
|
||||
height="52"
|
||||
x="77.52"
|
||||
y="77.83"
|
||||
rx="26"
|
||||
ry="26" />
|
||||
<circle
|
||||
style="fill:#cccccc"
|
||||
id="circle1"
|
||||
cx="104.5"
|
||||
cy="103.6"
|
||||
r="18" />
|
||||
<circle
|
||||
style="fill:#cccccc"
|
||||
id="circle2"
|
||||
cx="192.5"
|
||||
cy="103.6"
|
||||
r="18" />
|
||||
<path
|
||||
style="fill:#737373;stroke-width:0.999995px"
|
||||
d="m 71.53,205 7.55,-39 c 1.35,-6.2 4.23,-7.8 9.05,-8 45.27,-1 75.47,-1 120.77,0 4.9,0.2 7.5,1.8 9.1,8 l 7.5,39 z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="tekst"
|
||||
style="display:none"
|
||||
transform="translate(-46.02)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:38.8056px;line-height:1.25;font-family:Akbar;-inkscape-font-specification:Akbar;letter-spacing:3.70417px;word-spacing:0px;fill:#333333"
|
||||
x="47.15"
|
||||
y="55.55"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
x="47.15"
|
||||
y="55.55"
|
||||
style="-inkscape-font-specification:Akbar"
|
||||
rotate="0 0">copyparty</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="stensatt"
|
||||
transform="translate(-46.02)">
|
||||
<path
|
||||
d="m 119.3,58.82 q -2.2,2.24 -11.8,5.54 -8.83,3.12 -10.89,3.12 -8.1,0 -12.54,-5.04 -4.3,-4.81 -4.3,-12.9 0,-11.06 9.24,-18.36 8.16,-6.5 17.99,-6.5 1.2,0 2.4,1.78 1.3,1.67 1.3,2.78 0,4.96 -5.4,6.5 -3.4,0.95 -10.06,2.78 -5.41,2.81 -5.41,10.58 0,7.71 7.14,7.71 2.1,0 2.1,0 1.43,0 3.53,-0.39 2.8,-0.56 7.2,-1.78 4.4,-1.31 5.3,-1.31 0.8,0 4.3,1.45 z"
|
||||
style="fill:#333333;stroke-width:0.999757"
|
||||
id="path11" />
|
||||
<path
|
||||
d="m 169.6,42.52 q 0,5.02 -3.8,7.89 -3.5,2.68 -9.3,2.68 -1.4,0 -5.3,-0.71 -3.7,-0.73 -5.1,-0.73 -3.9,0 -3.9,4.74 0,1.53 0.3,4.66 0.4,3.13 0.4,4.66 0,3.4 -1.5,5.07 -2.5,0 -5.5,-1.52 -2.6,-17.03 -2.6,-21.52 0,-6.53 3.2,-14.5 4.4,-10.85 12.1,-10.85 7.3,0 14.5,7.34 6.5,6.89 6.5,12.8 z m -7.5,0.36 Q 160.9,38.94 157,35 q -4.5,-4.49 -8.5,-4.49 -2.7,0 -4.5,3.94 -1.7,3.23 -1.7,5.85 0,0.84 0.7,2.22 0.7,1.43 1.5,1.43 2.2,0 6.4,0.54 4,0.55 6.2,0.55 0.5,0 2.6,-0.73 2,-0.84 2.6,-1.32 z"
|
||||
style="fill:#333333;stroke-width:0.999656"
|
||||
id="path13" />
|
||||
<path
|
||||
d="m 218.3,43.94 q 0,5.01 -3.6,7.89 -3.3,2.67 -8.6,2.67 -1.4,0 -5.1,-0.7 -3.5,-0.73 -4.9,-0.73 -3.7,0 -3.7,4.74 0,1.51 0.4,4.65 0.4,3.13 0.4,4.67 0,3.4 -1.4,5.07 -2.7,0 -5.6,-1.53 -2.7,-17.02 -2.7,-21.42 0,-6.53 3.4,-14.5 4.3,-10.85 11.9,-10.85 6.8,0 13.3,7.34 6.2,6.89 6.2,12.8 z m -7.2,0.36 q -1.1,-3.94 -4.7,-7.88 -4.3,-4.49 -8,-4.49 -2.5,0 -4.4,3.95 -1.6,3.21 -1.6,5.84 0,0.84 0.8,2.22 0.6,1.43 1.5,1.43 2,0 5.9,0.55 3.8,0.53 5.9,0.53 0.6,0 2.4,-0.72 1.9,-0.84 2.5,-1.33 z"
|
||||
style="fill:#333333;stroke-width:0.999656"
|
||||
id="path15" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="tagger"
|
||||
transform="translate(-46.02)"
|
||||
style="display:none">
|
||||
<g
|
||||
id="g1"
|
||||
transform="translate(13.52,4.761)">
|
||||
<path
|
||||
id="path4"
|
||||
style="fill:#333333"
|
||||
d="m 111.4,83.33 -9.5,5.5 2.5,4.33 9.5,-5.5 z m -33.78,19.47 -9.52,5.5 2.5,4.4 9.52,-5.5 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path5"
|
||||
style="fill:#333333"
|
||||
d="m 88.5,73 v 11 h 5 V 73 Z m 0,39 v 11 h 5 v -11 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path6"
|
||||
style="fill:#333333"
|
||||
d="m 68.1,87.67 9.53,5.5 2.5,-4.33 -9.53,-5.5 z m 33.8,19.53 9.5,5.5 2.5,-4.4 -9.5,-5.5 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
id="g2"
|
||||
transform="rotate(30,132.7,290.2)">
|
||||
<path
|
||||
id="path7"
|
||||
style="fill:#333333"
|
||||
d="m 111.4,83.33 -9.5,5.5 2.5,4.33 9.5,-5.5 z m -33.78,19.47 -9.52,5.5 2.5,4.4 9.52,-5.5 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path8"
|
||||
style="fill:#333333"
|
||||
d="m 88.5,73 v 11 h 5 V 73 Z m 0,39 v 11 h 5 v -11 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path9"
|
||||
style="fill:#333333"
|
||||
d="m 68.1,87.67 9.53,5.5 2.5,-4.33 -9.53,-5.5 z m 33.8,19.53 9.5,5.5 2.5,-4.4 -9.5,-5.5 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.7 KiB |
209
docs/logo256.svg
Normal file
209
docs/logo256.svg
Normal file
|
@ -0,0 +1,209 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="256mm"
|
||||
height="176mm"
|
||||
viewBox="0 0 256 176"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<title
|
||||
id="title1">copyparty_logo</title>
|
||||
<defs
|
||||
id="defs1">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient1">
|
||||
<stop
|
||||
style="stop-color:#ffcc55;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop1" />
|
||||
<stop
|
||||
style="stop-color:#ffcc00;stop-opacity:1"
|
||||
offset="0.2"
|
||||
id="stop2" />
|
||||
<stop
|
||||
style="stop-color:#ff8800;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient2"
|
||||
x1="12"
|
||||
y1="12"
|
||||
x2="12"
|
||||
y2="122"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>copyparty_logo</dc:title>
|
||||
<dc:source>github.com/9001/copyparty</dc:source>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
inkscape:label="kassett">
|
||||
<rect
|
||||
style="fill:#333333"
|
||||
id="rect1"
|
||||
width="256"
|
||||
height="175"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="12"
|
||||
ry="12" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2)"
|
||||
id="rect2"
|
||||
width="232"
|
||||
height="110"
|
||||
x="12"
|
||||
y="12"
|
||||
rx="8"
|
||||
ry="8" />
|
||||
<rect
|
||||
style="fill:#333333"
|
||||
id="rect3"
|
||||
width="148"
|
||||
height="44"
|
||||
x="54"
|
||||
y="62"
|
||||
rx="22"
|
||||
ry="22" />
|
||||
<circle
|
||||
style="fill:#cccccc"
|
||||
id="circle1"
|
||||
cx="77"
|
||||
cy="84"
|
||||
r="15" />
|
||||
<circle
|
||||
style="fill:#cccccc"
|
||||
id="circle2"
|
||||
cx="179"
|
||||
cy="84"
|
||||
r="15" />
|
||||
<path
|
||||
style="fill:#737373"
|
||||
d="m 41,176 8.5,-32.7 c 1.5,-5.2 4.8,-6.5 10.2,-6.7 51.2,-0.8 85.3,-0.8 136.5,0 5.5,0.17 8.5,1.5 10.2,6.7 L 215,176 Z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="tekst"
|
||||
style="display:none">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:38.8056px;line-height:1.25;font-family:Akbar;-inkscape-font-specification:Akbar;letter-spacing:3.70417px;word-spacing:0px;fill:#333333"
|
||||
x="47.153069"
|
||||
y="55.548954"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"
|
||||
x="47.153069"
|
||||
y="55.548954"
|
||||
style="-inkscape-font-specification:Akbar"
|
||||
rotate="0 0">copyparty</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="stensatt">
|
||||
<path
|
||||
d="m 54.39,43.64 q -0.73,0.79 -4.04,1.96 -3.07,1.11 -3.75,1.11 -2.82,0 -4.36,-1.79 -1.49,-1.71 -1.49,-4.58 0,-3.92 3.21,-6.52 2.81,-2.3 6.23,-2.3 0.34,0 0.79,0.63 0.46,0.6 0.46,0.99 0,1.76 -1.87,2.3 -1.17,0.34 -3.45,0.99 -1.88,0.99 -1.88,3.76 0,2.73 2.48,2.73 0.72,0 0.72,0 0.46,0 1.23,-0.14 0.94,-0.19 2.47,-0.63 1.54,-0.46 1.82,-0.46 0.34,0 1.49,0.51 z"
|
||||
style="fill:#333333"
|
||||
id="path11" />
|
||||
<path
|
||||
d="m 74.95,38.6 q 0,3.58 -3.16,5.93 -2.73,1.96 -5.86,1.96 -2.9,0 -5.12,-2.21 -2.13,-2.22 -2.13,-5.12 0,-3.08 2.68,-5.67 2.73,-2.56 5.8,-2.56 2.99,0 5.38,2.35 2.41,2.36 2.41,5.34 z m -2.9,0.14 q 0,-1.92 -1.49,-3.16 -1.45,-1.28 -3.42,-1.28 -0.1,0 -1.36,1.37 -1.23,1.32 -2.08,1.32 -0.52,0 -0.69,-0.26 -0.99,1.96 -0.99,2.56 0,1.92 1.82,2.9 1.37,0.77 3.07,0.77 1.71,0 3.21,-0.94 1.92,-1.19 1.92,-3.27 z"
|
||||
style="fill:#333333"
|
||||
id="path12" />
|
||||
<path
|
||||
d="m 96.46,40.14 q 0,2.39 -1.63,3.75 -1.53,1.28 -4.01,1.28 -0.59,0 -2.3,-0.34 -1.62,-0.34 -2.22,-0.34 -1.79,0 -1.79,2.25 0,0.73 0.2,2.22 0.17,1.49 0.17,2.22 0,1.62 -0.66,2.41 -1.23,0 -2.56,-0.72 -1.25,-8.11 -1.25,-10.24 0,-3.12 1.5,-6.91 2.02,-5.17 5.5,-5.17 3.16,0 6.23,3.5 2.82,3.28 2.82,6.1 z m -3.25,0.17 q -0.51,-1.88 -2.22,-3.76 -1.96,-2.13 -3.66,-2.13 -1.11,0 -1.99,1.88 -0.77,1.53 -0.77,2.78 0,0.4 0.32,1.06 0.37,0.68 0.73,0.68 0.94,0 2.73,0.26 1.79,0.25 2.73,0.25 0.26,0 1.11,-0.34 0.85,-0.4 1.11,-0.63 z"
|
||||
style="fill:#333333"
|
||||
id="path13" />
|
||||
<path
|
||||
d="m 113.7,34.33 q -1.8,3.5 -2.7,5.98 -0.1,0.25 -1.4,3.84 -0.3,1.16 -0.9,3.58 -0.4,2.42 -0.8,3.59 -0.9,2.41 -2,2.25 -1.2,-0.17 -1.3,-1.37 0,-0.17 0,-0.42 0,-0.14 0.2,-1.28 0.9,-4.3 0.9,-5.5 0,-0.46 -0.1,-0.63 -1.2,-2.08 -3.5,-6.32 -2.32,-4.24 -2.1,-6.57 1.3,-1.16 1.8,-1.16 0.4,0 1,0.52 0.5,0.51 0.6,0.93 0.7,5.29 4.1,9.48 0.9,-1.54 1.6,-3.45 0.4,-1.2 1.4,-3.54 1.6,-3.81 2.9,-3.81 0.1,0 0.3,0.1 0.8,0.25 1.1,2.39 z"
|
||||
style="fill:#333333"
|
||||
id="path14" />
|
||||
<path
|
||||
d="m 134.6,41.16 q 0,2.39 -1.6,3.76 -1.6,1.28 -4,1.28 -0.6,0 -2.3,-0.35 -1.7,-0.34 -2.3,-0.34 -1.7,0 -1.7,2.26 0,0.72 0.2,2.21 0.2,1.5 0.2,2.22 0,1.62 -0.6,2.42 -1.3,0 -2.6,-0.73 -1.3,-8.1 -1.3,-10.19 0,-3.12 1.6,-6.92 1.9,-5.16 5.4,-5.16 3.2,0 6.2,3.5 2.8,3.28 2.8,6.09 z m -3.2,0.17 q -0.6,-1.88 -2.3,-3.75 -1.9,-2.14 -3.6,-2.14 -1.1,0 -2,1.88 -0.8,1.54 -0.8,2.78 0,0.4 0.4,1.06 0.3,0.68 0.7,0.68 0.9,0 2.7,0.26 1.8,0.26 2.7,0.26 0.3,0 1.1,-0.35 0.9,-0.4 1.1,-0.63 z"
|
||||
style="fill:#333333"
|
||||
id="path15" />
|
||||
<path
|
||||
d="m 155.5,45.68 q 0,0.77 -0.5,1.28 -0.5,0.52 -1.2,0.52 -1.4,0 -2.6,-0.77 -1.2,-0.8 -1.8,-1.97 -0.5,-0.1 -1.2,0.73 -0.8,0.99 -1,1.06 -1,0.46 -3.3,0.46 -1.9,0 -3.3,-2.08 -1.3,-1.82 -1.3,-3.42 0,-2.9 2.9,-5.46 2.7,-2.47 5.7,-2.47 0.8,0 1.5,0.51 0.6,0.51 0.6,1.23 0,0.46 -0.3,0.94 2.1,0.77 2.1,2.41 0,0.3 -0.1,0.9 -0.1,0.6 -0.1,0.89 0,0.35 0.1,0.52 0.4,1.11 2.1,2.9 1.6,1.62 1.6,1.87 z m -6.9,-8.62 q -0.3,0 -0.9,-0.1 -0.7,-0.14 -1,-0.14 -1.1,0 -2.7,1.66 -1.6,1.65 -1.6,2.81 0,0.69 0.6,1.54 0.7,1.11 1.8,1.11 2.3,0 3,-2.47 0.5,-2.22 0.9,-4.41 z"
|
||||
style="fill:#333333"
|
||||
id="path16" />
|
||||
<path
|
||||
d="m 174.1,36.38 q -0.3,0.34 -1.3,0.34 -0.7,0 -2.1,-0.25 -1.5,-0.26 -2.1,-0.26 -4,0 -4.7,5.89 -0.3,2.64 -0.4,2.82 -0.3,0.85 -1.4,1.96 h -1 q -0.6,-1.03 -1.1,-3.5 -0.5,-2.36 -0.5,-3.64 0,-0.99 0.1,-1.28 0.2,-0.47 0.9,-0.47 0.2,0 0.5,0.26 0.3,0.26 0.3,0.26 1.6,-3.02 2.7,-3.93 1.5,-1.45 4.3,-1.45 1.2,0 3.1,0.77 2.4,0.99 2.8,2.39 z"
|
||||
style="fill:#333333"
|
||||
id="path17" />
|
||||
<path
|
||||
d="m 196,31.91 q 0.3,0.68 0.3,1.23 0,1.59 -2.1,1.59 -0.8,0 -2.9,-0.43 -2.2,-0.46 -2.9,-0.46 -1.1,0 -1.3,0.1 -0.4,0.18 -0.4,1.03 0,1.88 0.6,5.89 0.5,5 1.3,5.23 -0.3,0.3 -0.3,0.94 -1,0.59 -2.2,0.59 -1.2,0 -1.8,-3.32 -0.1,-1.17 -0.4,-6.63 -0.1,-3.92 -0.7,-4.69 -0.2,-0.4 -3.6,-0.3 -0.9,0 -1.4,0.1 -0.4,0 -0.3,0 -0.6,0 -1,-0.6 -0.4,-1.11 -0.4,-1.2 0,-1.22 3.5,-1.7 1.4,-0.14 4,-0.43 0,-0.72 -0.1,-2.18 0,-1.5 0,-2.22 0,-3.71 1.8,-3.71 0.4,0 1,0.51 0.5,0.51 0.5,0.94 v 6.74 q 0.9,1.02 4.2,1.45 3.4,0.43 4.6,1.59 z"
|
||||
style="fill:#333333"
|
||||
id="path18" />
|
||||
<path
|
||||
d="m 214.6,34.5 q -1.7,3.5 -2.8,5.98 -0.1,0.25 -1.3,3.84 -0.4,1.16 -0.8,3.58 -0.4,2.42 -0.9,3.59 -0.8,2.41 -2,2.25 -1.2,-0.17 -1.3,-1.37 -0.1,-0.17 -0.1,-0.42 0,-0.14 0.3,-1.28 0.9,-4.3 0.9,-5.5 0,-0.46 -0.1,-0.63 -1.2,-2.08 -3.5,-6.31 -2.3,-4.25 -2,-6.58 1.2,-1.16 1.8,-1.16 0.3,0 0.8,0.52 0.5,0.51 0.6,0.93 0.8,5.3 4.2,9.48 0.9,-1.54 1.6,-3.45 0.5,-1.2 1.4,-3.54 1.5,-3.81 2.9,-3.81 0.2,0 0.3,0.1 0.7,0.25 1.1,2.39 z"
|
||||
style="fill:#333333"
|
||||
id="path19" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="tagger">
|
||||
<g
|
||||
id="g1">
|
||||
<path
|
||||
id="path4"
|
||||
style="fill:#333333"
|
||||
d="m 94.2,71.8 -8,4.6 2,3.5 8,-4.7 z m -28.4,16.3 -8,4.7 2,3.4 8,-4.6 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path5"
|
||||
style="fill:#333333"
|
||||
d="m 75,63 v 9.2 h 4 V 63 Z m 0,32.8 v 9.2 h 4 v -9.2 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path6"
|
||||
style="fill:#333333"
|
||||
d="m 96.2,92.8 -8,-4.7 -2,3.5 8,4.6 z m -28.4,-16.4 -8,-4.6 -2,3.4 8,4.7 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
id="g2">
|
||||
<path
|
||||
id="path7"
|
||||
style="fill:#333333"
|
||||
d="m 200,82 h -9 v 4 h 9 z m -33,0 h -9 v 4 h 9 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path8"
|
||||
style="fill:#333333"
|
||||
d="m 191,101 -4,-7.8 -4,2 5,7.8 z m -16,-28.2 -5,-8 -3,2 4,8 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
id="path9"
|
||||
style="fill:#333333"
|
||||
d="m 170,103 5,-7.8 -4,-2 -4,7.8 z m 17,-28.2 4,-8 -3,-2 -5,8 z"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.4 KiB |
|
@ -45,6 +45,14 @@ classifiers = [
|
|||
"Demo Server" = "https://a.ocv.me/pub/demo/"
|
||||
|
||||
[project.optional-dependencies]
|
||||
all = [
|
||||
"argon2-cffi",
|
||||
"partftpy>=0.4.0",
|
||||
"Pillow",
|
||||
"pyftpdlib",
|
||||
"pyopenssl",
|
||||
"pyzmq",
|
||||
]
|
||||
thumbnails = ["Pillow"]
|
||||
thumbnails2 = ["pyvips"]
|
||||
audiotags = ["mutagen"]
|
||||
|
@ -86,7 +94,6 @@ copyparty = [
|
|||
"web/*.css",
|
||||
"web/*.html",
|
||||
"web/a/*.bat",
|
||||
"web/dd/*.png",
|
||||
"web/deps/*.gz",
|
||||
"web/deps/*.woff*",
|
||||
]
|
||||
|
|
|
@ -137,10 +137,10 @@ repack() {
|
|||
}
|
||||
|
||||
repack sfx-full "re gz"
|
||||
repack sfx-ent "re no-dd"
|
||||
repack sfx-ent "re no-dd gz"
|
||||
repack sfx-lite "re no-dd no-cm no-hl"
|
||||
repack sfx-lite "re no-dd no-cm no-hl gz"
|
||||
repack sfx-ent "re"
|
||||
repack sfx-ent "re gz"
|
||||
repack sfx-lite "re no-cm no-hl"
|
||||
repack sfx-lite "re no-cm no-hl gz"
|
||||
|
||||
|
||||
# move fuse and up2k clients into copyparty-extras/,
|
||||
|
|
|
@ -46,8 +46,6 @@ help() { exec cat <<'EOF'
|
|||
# `no-fnt` saves ~9k by removing the source-code-pro font
|
||||
# (browsers will try to use 'Consolas' instead)
|
||||
#
|
||||
# `no-dd` saves ~2k by removing the mouse cursor
|
||||
#
|
||||
# _____________________________________________________________________
|
||||
# build behavior:
|
||||
#
|
||||
|
@ -61,8 +59,8 @@ help() { exec cat <<'EOF'
|
|||
#
|
||||
# _____________________________________________________________________
|
||||
# some usage examples:
|
||||
# ./scripts/make-sfx.sh lang eng no-cm no-hl no-dd no-fnt no-smb no-pf
|
||||
# ./scripts/rls.sh sfx lang eng no-cm no-hl no-dd no-fnt no-smb no-pf
|
||||
# ./scripts/make-sfx.sh lang eng no-cm no-hl no-fnt no-smb no-pf
|
||||
# ./scripts/rls.sh sfx lang eng no-cm no-hl no-fnt no-smb no-pf
|
||||
# (reduces v1.14.2 from 700k to 495k)
|
||||
|
||||
EOF
|
||||
|
@ -76,7 +74,6 @@ gtar=$(command -v gtar || command -v gnutar) || true
|
|||
sed() { gsed "$@"; }
|
||||
find() { gfind "$@"; }
|
||||
sort() { gsort "$@"; }
|
||||
shuf() { gshuf "$@"; }
|
||||
nproc() { gnproc; }
|
||||
sha1sum() { shasum "$@"; }
|
||||
unexpand() { gunexpand "$@"; }
|
||||
|
@ -123,7 +120,6 @@ while [ ! -z "$1" ]; do
|
|||
no-pf) no_pf=1 ; ;;
|
||||
no-fnt) no_fnt=1 ; ;;
|
||||
no-hl) no_hl=1 ; ;;
|
||||
no-dd) no_dd=1 ; ;;
|
||||
no-cm) no_cm=1 ; ;;
|
||||
dl-wd) dl_wd=1 ; ;;
|
||||
ign-wd) ign_wd=1 ; ;;
|
||||
|
@ -160,9 +156,9 @@ stamp=$(
|
|||
done | sort | tail -n 1 | sha1sum | cut -c-16
|
||||
)
|
||||
|
||||
rm -rf sfx$CSN/*
|
||||
mkdir -p sfx$CSN build
|
||||
cd sfx$CSN
|
||||
rm -rf sfx/*
|
||||
mkdir -p sfx build
|
||||
cd sfx
|
||||
|
||||
tmpdir="$(
|
||||
printf '%s\n' "$TMPDIR" /tmp |
|
||||
|
@ -398,7 +394,7 @@ ts=$(date -u +%s)
|
|||
hts=$(date -u +%Y-%m%d-%H%M%S) # --date=@$ts (thx osx)
|
||||
|
||||
mkdir -p ../dist
|
||||
sfx_out=../dist/copyparty-sfx$CSN
|
||||
sfx_out=../dist/copyparty-sfx
|
||||
|
||||
echo cleanup
|
||||
find -name '*.pyc' -delete
|
||||
|
@ -456,13 +452,6 @@ rm -f ftp/pyftpdlib/{__main__,prefork}.py
|
|||
ised "s/src:.*scp.*\)/src:local('Consolas')/" $f
|
||||
}
|
||||
|
||||
[ $no_dd ] && {
|
||||
rm -rf copyparty/web/dd
|
||||
f=copyparty/web/browser.css
|
||||
gzip -d "$f.gz" || true
|
||||
ised 's/(cursor: ?)url\([^)]+\), ?(pointer)/\1\2/; s/[0-9]+% \{cursor:[^}]+\}//; s/animation: ?cursor[^};]+//' $f
|
||||
}
|
||||
|
||||
[ $langs ] && {
|
||||
echo $langs | grep -q eng || {
|
||||
langs="eng|$langs"
|
||||
|
@ -564,7 +553,7 @@ gzres() {
|
|||
}
|
||||
|
||||
|
||||
zdir="$tmpdir/cpp-mksfx$CSN"
|
||||
zdir="$tmpdir/cpp-mksfx"
|
||||
[ -e "$zdir/$stamp" ] || rm -rf "$zdir"
|
||||
mkdir -p "$zdir"
|
||||
echo a > "$zdir/$stamp"
|
||||
|
@ -593,15 +582,7 @@ echo gen tarlist
|
|||
for d in copyparty partftpy magic j2 py2 py37 ftp; do find $d -type f || true; done | # strip_hints
|
||||
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
|
||||
sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
||||
|
||||
for n in {1..50}; do
|
||||
(grep -vE '\.gz$' list1; grep -E '\.gz$' list1 | (shuf||gshuf) ) >list || true
|
||||
s=$( (sha1sum||shasum) < list | cut -c-16)
|
||||
grep -q $s "$zdir/h" 2>/dev/null && continue
|
||||
echo $s >> "$zdir/h"
|
||||
break
|
||||
done
|
||||
[ $n -eq 50 ] && exit
|
||||
(grep -vE '\.gz$' list1; grep -E '\.gz$' list1) >list
|
||||
|
||||
echo creating tar
|
||||
tar -cf tar "${targs[@]}" --numeric-owner -T list
|
||||
|
|
|
@ -29,15 +29,11 @@ update_mpr_pkgbuild() {
|
|||
|
||||
sha=$(sha256sum "$self/../dist/copyparty-$ver.tar.gz" | awk '{print$1}')
|
||||
|
||||
# awk -v ver=$ver -v sha=$sha '
|
||||
# /^pkgver=/{sub(/[0-9\.]+/,ver)};
|
||||
# /^sha256sums=/{sub(/[0-9a-f]{64}/,sha)};
|
||||
# 1' PKGBUILD >a
|
||||
# mv a PKGBUILD
|
||||
|
||||
echo thing 1
|
||||
sed -s -i "s/pkgver=\"\"/pkgver=\"$ver\"/" PKGBUILD
|
||||
sed -s -i "s/sha256sums=(\".*\")/sha256sums=(\"$sha\")/" PKGBUILD
|
||||
awk -v ver=$ver -v sha=$sha '
|
||||
/^pkgver=/{sub(/[0-9\.]+/,ver)};
|
||||
/^sha256sums=/{sub(/[0-9a-f]{64}/,sha)};
|
||||
1' PKGBUILD >a
|
||||
mv a PKGBUILD
|
||||
|
||||
rm -rf x
|
||||
}
|
||||
|
|
|
@ -30,5 +30,5 @@ a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac7
|
|||
3e39ea6e16b502d99a2e6544579095d0f7c6097761cd85135d5e929b9dec1b32e80669a846f94ee8c2cca9be2f5fe728625d09453988864c04e16bb8445c3f91 pillow-11.3.0-cp313-cp313-win_amd64.whl
|
||||
59fbbcae044f4ee73d203ac74b553b27bfad3e6b2f3fb290fd3f8774753c6b545176b6b3399c240b092d131d152290ce732750accd962dc1e48e930be85f5e53 pyinstaller-6.14.1-py3-none-win_amd64.whl
|
||||
fc6f3e144c5f5b662412de07cb8bf0c2eb3b3be21d19ec448aef3c4244d779b9ab8027fd67a4871e6e13823b248ea0f5a7a9241a53aef30f3b51a6d3cb5bdb3f pyinstaller_hooks_contrib-2025.5-py3-none-any.whl
|
||||
2c7a52e223b8186c21009d3fa5ed6a856d8eb4ef3b98f5d24c378c6a1afbfa1378bd7a51d6addc500e263d7989efb544c862bf920055e740f137c702dfd9d18b python-3.13.5-amd64.exe
|
||||
3c37ea72ab062f65116a5faaaccbaa960a971797c78dbe6a504f41e562b018e7f28924647b5577fdb4fedfa61ffe0f1153842a8585f93b0332ed4d97905f6609 python-3.13.6-amd64.exe
|
||||
2a0420f7faaa33d2132b82895a8282688030e939db0225ad8abb95a47bdb87b45318f10985fc3cee271a9121441c1526caa363d7f2e4a4b18b1a674068766e87 setuptools-80.9.0-py3-none-any.whl
|
||||
|
|
|
@ -40,7 +40,7 @@ fns=(
|
|||
pillow-11.3.0-cp313-cp313-win_amd64.whl
|
||||
pyinstaller-6.14.1-py3-none-win_amd64.whl
|
||||
pyinstaller_hooks_contrib-2025.5-py3-none-any.whl
|
||||
python-3.13.5-amd64.exe
|
||||
python-3.13.6-amd64.exe
|
||||
setuptools-80.9.0-py3-none-any.whl
|
||||
)
|
||||
[ $w7 ] && fns+=(
|
||||
|
|
|
@ -32,11 +32,8 @@ v=$1
|
|||
rm -f ../dist/copyparty-sfx*
|
||||
shift
|
||||
./make-sfx.sh "$@"
|
||||
f=../dist/copyparty-sfx
|
||||
[ -e $f.py ] && s= || s=-gz
|
||||
# TODO: the -gz suffix is gone, can drop all the $s stuff probably
|
||||
|
||||
$f$s.py --version >/dev/null
|
||||
../dist/copyparty-sfx.py --version >/dev/null
|
||||
mv ../dist/copyparty-{sfx,int}.py
|
||||
|
||||
while [ "$1" ]; do
|
||||
case "$1" in
|
||||
|
@ -46,27 +43,8 @@ while [ "$1" ]; do
|
|||
shift
|
||||
done
|
||||
|
||||
[ $parallel -gt 1 ] && {
|
||||
printf '\033[%s' s 2r H "0;1;37;44mbruteforcing sfx size -- press enter to terminate" K u "7m $* " K $'27m\n'
|
||||
trap "rm -f .sfx-run; printf '\033[%s' s r u" INT TERM EXIT
|
||||
touch .sfx-run
|
||||
min=99999999
|
||||
for ((a=0; a<$parallel; a++)); do
|
||||
while [ -e .sfx-run ]; do
|
||||
CSN=$a ./make-sfx.sh re "$@"
|
||||
sz=$(wc -c <$f$a$s.py | awk '{print$1}')
|
||||
[ $sz -ge $min ] && continue
|
||||
mv $f$a$s.py $f$s.py.$sz
|
||||
min=$sz
|
||||
done &
|
||||
done
|
||||
read
|
||||
exit
|
||||
}
|
||||
|
||||
while true; do
|
||||
mv $f$s.py $f$s.$(wc -c <$f$s.py | awk '{print$1}').py
|
||||
./make-sfx.sh re "$@"
|
||||
done
|
||||
./make-sfx.sh re lang eng "$@"
|
||||
mv ../dist/copyparty-{sfx,en}.py
|
||||
mv ../dist/copyparty-{int,sfx}.py
|
||||
|
||||
# git tag -d v$v; git push --delete origin v$v
|
||||
|
|
|
@ -73,12 +73,6 @@ copyparty/web/browser.js,
|
|||
copyparty/web/browser2.html,
|
||||
copyparty/web/cf.html,
|
||||
copyparty/web/copyparty.gif,
|
||||
copyparty/web/dd,
|
||||
copyparty/web/dd/2.png,
|
||||
copyparty/web/dd/3.png,
|
||||
copyparty/web/dd/4.png,
|
||||
copyparty/web/dd/5.png,
|
||||
copyparty/web/dd/__init__.py,
|
||||
copyparty/web/deps,
|
||||
copyparty/web/deps/__init__.py,
|
||||
copyparty/web/deps/busy.mp3,
|
||||
|
|
|
@ -315,6 +315,7 @@ var tl_browser = {
|
|||
"ct_qdel": 'when deleting files, only ask for confirmation once">qdel',
|
||||
"ct_dir1st": 'sort folders before files">📁 first',
|
||||
"ct_nsort": 'natural sort (for filenames with leading digits)">nsort',
|
||||
"ct_utc": 'show all datetimes in UTC">UTC',
|
||||
"ct_readme": 'show README.md in folder listings">📜 readme',
|
||||
"ct_idxh": 'show index.html instead of folder listing">htm',
|
||||
"ct_sbars": 'show scrollbars">⟊',
|
||||
|
@ -390,6 +391,8 @@ var tl_browser = {
|
|||
"mt_c2owa": "opus-weba, for iOS 17.5 and newer\">owa",
|
||||
"mt_c2caf": "opus-caf, for iOS 11 through 17\">caf",
|
||||
"mt_c2mp3": "use this on very old devices\">mp3",
|
||||
"mt_c2flac": "best sound quality, but huge downloads\">flac",
|
||||
"mt_c2wav": "uncompressed playback (even bigger)\">wav",
|
||||
"mt_c2ok": "nice, good choice",
|
||||
"mt_c2nd": "that's not the recommended output format for your device, but that's fine",
|
||||
"mt_c2ng": "your device does not seem to support this output format, but let's try anyways",
|
||||
|
|
2
setup.py
2
setup.py
|
@ -132,11 +132,11 @@ args = {
|
|||
"copyparty.stolen.ifaddr",
|
||||
"copyparty.web",
|
||||
"copyparty.web.a",
|
||||
"copyparty.web.dd",
|
||||
"copyparty.web.deps",
|
||||
],
|
||||
"install_requires": ["jinja2"],
|
||||
"extras_require": {
|
||||
"all": ["argon2-cffi", "partftpy>=0.4.0", "Pillow", "pyftpdlib", "pyopenssl", "pyzmq"],
|
||||
"thumbnails": ["Pillow"],
|
||||
"thumbnails2": ["pyvips"],
|
||||
"audiotags": ["mutagen"],
|
||||
|
|
|
@ -143,7 +143,7 @@ class Cfg(Namespace):
|
|||
def __init__(self, a=None, v=None, c=None, **ka0):
|
||||
ka = {}
|
||||
|
||||
ex = "chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ka.update(**{k: False for k in ex.split()})
|
||||
|
||||
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"
|
||||
|
@ -161,10 +161,10 @@ class Cfg(Namespace):
|
|||
ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who"
|
||||
ka.update(**{k: 9 for k in ex.split()})
|
||||
|
||||
ex = "db_act forget_ip idp_store k304 loris no304 nosubtle re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs"
|
||||
ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle 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 chmod_f chpw_db doctitle df exit favico idp_h_usr ipa html_head 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 shr tcolor textfiles unlist vname xff_src zipmaxt R RS SR"
|
||||
ex = "ah_alg bname chmod_f chpw_db doctitle df exit favico idp_h_usr ipa html_head 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 shr tcolor textfiles txt_eol 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"
|
||||
|
|
Loading…
Reference in a new issue