Merge branch 'hovudstraum' into hovudstraum

Signed-off-by: ed <s@ocv.me>
This commit is contained in:
ed 2025-08-07 21:45:50 +00:00 committed by GitHub
commit 99bf800b73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 3331 additions and 368 deletions

View file

@ -437,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 `--accounts` 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)
@ -1016,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)
@ -1301,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
@ -1318,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/
@ -1333,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
@ -1390,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;
@ -2058,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
@ -2277,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
@ -2504,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
@ -2615,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
@ -2719,6 +2729,8 @@ when generating hashes using `--ah-cli` for docker or systemd services, make sur
* 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
@ -2836,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)

View file

@ -1,4 +1,7 @@
# 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.10"
pkgrel=1
@ -6,52 +9,40 @@ pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFT
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)"
"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-git: detection of musical keys"
"qm-vamp-plugins: BPM detection"
"libkeyfinder: detection of musical keys"
"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)"
)
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
backup=("etc/${pkgname}.d/init" )
backup=("etc/${pkgname}/copyparty.conf" )
sha256sums=("49c5fedf7619437bc0af125cb4e8360d9bda9d87ef45d6314d7acf163ab4cf99")
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 "┗━━━━━━━━━━━━━━━──-"
}

View file

@ -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

View file

@ -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

View file

@ -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/`

View 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

View file

@ -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

View 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

View 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
View 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`

View file

@ -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

View file

@ -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
"""
),
],
@ -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 --accounts 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")
@ -1156,14 +1162,14 @@ def add_auth(ap):
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")
@ -1212,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")
@ -1225,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)")
@ -1235,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")
@ -1243,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)")
@ -1262,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)")
@ -1295,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")
@ -1329,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")
@ -1360,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]")
@ -1374,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")
@ -1390,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")
@ -1399,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)")
@ -1413,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)")
@ -1445,7 +1452,7 @@ 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)")
@ -1458,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)")
@ -1468,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")
@ -1477,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")
@ -1506,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")
@ -1516,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)")
@ -1534,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)")
@ -1552,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")
@ -1567,7 +1575,7 @@ 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("--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")
@ -1583,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)")
@ -1593,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")

View file

@ -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,10 +1710,21 @@ 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()
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:
@ -2630,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=)([^&]+)"
@ -2930,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]:
@ -3021,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:

View file

@ -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)",

View file

@ -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]

View file

@ -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
@ -2924,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"
@ -2942,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)
@ -2952,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)
@ -3583,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:
@ -3667,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(
@ -4637,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
@ -5031,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"]:
@ -5071,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,
@ -5088,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,
@ -5133,7 +5175,7 @@ class HttpCli(object):
t = '<h1 id="n">404 not found &nbsp;┐( ´ -`)┌</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
@ -5447,6 +5489,8 @@ class HttpCli(object):
elif nfi == 3:
if not vp.endswith(vfi):
continue
else:
continue
n -= 1
if not n:
@ -5571,6 +5615,8 @@ class HttpCli(object):
elif nfi == 3:
if not vp.endswith(vfi):
continue
else:
continue
if not dots and "/." in vp:
continue
@ -6001,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"
@ -6183,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
@ -6195,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
)
@ -6255,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

View file

@ -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:

View file

@ -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> ")

View file

@ -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:

View file

@ -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] = {}

View file

@ -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]

View file

@ -375,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)

View file

@ -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]],

View file

@ -1374,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);
@ -1384,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);

View file

@ -3072,7 +3072,7 @@ var Ls = {
"u_https1": "für bessere Performance solltest du",
"u_https2": "auf HTTPS wechseln",
"u_https3": "",
"u_https3": " ",
"u_ancient": 'Dein Browser ist verdammt antik -- vielleicht solltest du <a href="#" onclick="goto(\'bup\')">stattdessen bup benutzen</a>',
"u_nowork": "Benötigt Firefox 53+ oder Chrome 57+ oder iOS 11+",
"tail_2old": "Benötigt Firefox 105+ oder Chrome 71+ oder iOS 14.5+",
@ -3122,7 +3122,7 @@ var Ls = {
"u_ehsdf": "Server hat kein Speicherplatz mehr!\n\nwerde es erneut versuchen, falls jemand\ngenug Platz schafft um fortzufahren",
"u_emtleak1": "scheint, als ob dein Browser ein Memory Leak hätte;\nbitte",
"u_emtleak2": ' <a href="{0}">wechsle auf HTTPS (empfohlen)</a> oder ',
"u_emtleak3": '',
"u_emtleak3": ' ',
"u_emtleakc": 'versuche folgendes:\n<ul><li>drücke <code>F5</code> um die Seite neu zu laden</li><li>deaktivere dann den &nbsp;<code>mt</code>&nbsp; Button in den &nbsp;<code>⚙️ Einstellungen</code></li><li>und versuche den Upload nochmal.</li></ul>Uploads werden etwas langsamer sein, aber man kann ja nicht alles haben.\nSorry für die Umstände !\n\nPS: Chrome v107 <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">hat ein Bugfix</a> dafür',
"u_emtleakf": 'versuche folgendes:\n<ul><li>drücke <code>F5</code> um die Seite neu zu laden</li><li>aktivere dann <code>🥔</code> (potato) im Upload UI<li>und versuche den Upload nochmal</li></ul>\nPS: Firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">hat hoffentlich irgendwann ein Bugfix</a>',
"u_s404": "nicht auf dem Server gefunden",
@ -3490,7 +3490,7 @@ var Ls = {
"mm_uncache": "välimuisti tyhjennetty; kaikki kappaleet ladataan uudelleen seuraavalla toistolla",
"mm_hnf": "tuota kappaletta ei enää ole olemassa",
" im_hnf": "tuota kuvaa ei enää ole olemassa",
"im_hnf": "tuota kuvaa ei enää ole olemassa",
"f_empty": 'tämä hakemisto on tyhjä',
"f_chide": 'tämä piilottaa sarakkeen «{0}»\n\nvoit palauttaa sarakkeet asetuksista',
@ -3785,6 +3785,635 @@ var Ls = {
"lang_set": "ladataanko sivu uudestaan kielen vaihtamiseksi?",
},
"grc": {
"tt": "Ελληνικά",
"cols": {
"c": "κουμπιά ενεργειών",
"dur": "διάρκεια",
"q": "ποιότητα / bitrate",
"Ac": "κωδικοποιητής ήχου",
"Vc": "κωδικοποιητής βίντεο",
"Fmt": "μορφή / container",
"Ahash": "checksum ήχου",
"Vhash": "checksum βίντεο",
"Res": "ανάλυση",
"T": "τύπος αρχείου",
"aq": "ποιότητα ήχου / bitrate",
"vq": "ποιότητα βίντεο / bitrate",
"pixfmt": "subsampling / δομή εικονοστοιχείων",
"resw": "οριζόντια ανάλυση",
"resh": "κάθετη ανάλυση",
"chs": "κανάλια ήχου",
"hz": "συχνότητα δειγματοληψίας"
},
"hks": [
[
"διάφορα",
["ESC", "κλείσιμο διαφόρων λειτουργιών"],
"διαχειριστής αρχείων",
["G", "εναλλαγή λίστας / πλέγματος"],
["T", "εναλλαγή μικρογραφιών / εικονιδίων"],
["⇧ A/D", "μέγεθος μικρογραφιών"],
["ctrl-K", "διαγραφή επιλεγμένων"],
["ctrl-X", "αποκοπή επιλογής στο πρόχειρο"],
["ctrl-C", "αντιγραφή επιλογής στο πρόχειρο"],
["ctrl-V", "επικόλληση (μετακίνηση/αντιγραφή) εδώ"],
["Y", "λήψη επιλεγμένων"],
["F2", "μετονομασία επιλεγμένων"],
"λίστα αρχείων",
["space", "εναλλαγή επιλογής αρχείου"],
["↑/↓", "μετακίνηση δείκτη επιλογής"],
["ctrl ↑/↓", "μετακίνηση δείκτη και προβολής"],
["⇧ ↑/↓", "επιλογή προηγούμενου/επόμενου αρχείου"],
["ctrl-A", "επιλογή όλων των αρχείων / φακέλων"]
], [
"πλοήγηση",
["B", "εναλλαγή σε καρτέλες διαδρομών / δέντρο διαδρομών"],
["I/K", "προηγούμενος/επόμενος φάκελος"],
["M", "γονικός φάκελος (ή σμίκρυνση τρέχοντος)"],
["V", "εναλλαγή φακέλων / δέντρο αρχείων κειμένου"],
["A/D", "μέγεθος πίνακα πλοήγησης"]
], [
"μουσική",
["J/L", "προηγούμενο/επόμενο τραγούδι"],
["U/O", "μετάβαση 10δευτ πίσω/μπροστά"],
["0..9", "μετάβαση στο 0%..90%"],
["P", "αναπαραγωγή/παύση (ξεκινάει κιόλας)"],
["S", "επιλογή αναπαραγόμενου τραγουδιού"],
["Y", "λήψη τραγουδιού"]
], [
"εικόνες",
["J/L, ←/→", "προηγούμενη/επόμενη εικόνα"],
["Home/End", "πρώτη/τελευταία εικόνα"],
["F", "πλήρης οθόνη"],
["R", "περιστροφή δεξιόστροφα"],
["⇧ R", "περιστροφή αριστερόστροφα"],
["S", "επιλογή εικόνας"],
["Y", "λήψη εικόνας"]
], [
"βίντεο",
["U/O", "μετάβαση 10δευτ πίσω/μπροστά"],
["P/K/Space", "αναπαραγωγή/παύση"],
["C", "συνέχεια στο επόμενο"],
["V", "επανάληψη"],
["M", "σίγαση"],
["[ και ]", "ορισμός διαστήματος επανάληψης"]
], [
"αρχεία κειμένου",
["I/K", "προηγούμενο/επόμενο αρχείο"],
["M", "κλείσιμο αρχείου"],
["E", "επεξεργασία αρχείου"],
["S", "επιλογή αρχείου (για αποκοπή/αντιγραφή/μετονομασία)"]
]
],
"m_ok": "Εντάξει",
"m_ng": "Άκυρο",
"enable": "Ενεργοποίηση",
"danger": "ΚΙΝΔΥΝΟΣ",
"clipped": "αντιγράφηκε στο πρόχειρο",
"ht_s1": "δευτερόλεπτο",
"ht_s2": "δευτερόλεπτα",
"ht_m1": "λεπτό",
"ht_m2": "λεπτά",
"ht_h1": "ώρα",
"ht_h2": "ώρες",
"ht_d1": "μέρα",
"ht_d2": "μέρες",
"ht_and": " και ",
"goh": "πίνακας ελέγχου",
"gop": 'προηγούμενος φάκελος στο ίδιο επίπεδο">προηγούμενο',
"gou": 'γονικός φάκελος">πάνω',
"gon": 'επόμενος φάκελος">επόμενο',
"logout": "Αποσύνδεση ",
"access": " πρόσβαση",
"ot_close": "κλείσιμο υπομενού",
"ot_search": "αναζήτηση αρχείων με βάση χαρακτηριστικά, διαδρομή / όνομα, μουσικά tags ή οποιονδήποτε συνδυασμό$N$N&lt;code&gt;foo bar&lt;/code&gt; = πρέπει να περιέχει και τα «foo» και «bar»,$N&lt;code&gt;foo -bar&lt;/code&gt; = πρέπει να περιέχει το «foo» αλλά όχι το «bar»,$N&lt;code&gt;^yana .opus$&lt;/code&gt; = να ξεκινά με «yana» και να είναι αρχείο «opus»$N&lt;code&gt;&quot;try unite&quot;&lt;/code&gt; = να περιέχει ακριβώς «try unite»$N$Nη μορφή ημερομηνίας είναι iso-8601, όπως$N&lt;code&gt;2009-12-31&lt;/code&gt; ή &lt;code&gt;2020-09-12 23:30:00&lt;/code&gt;",
"ot_unpost": "unpost: διαγραφή πρόσφατων μεταφορτώσεων ή ακύρωση ανολοκλήρωτων",
"ot_bup": "bup: βασικός uploader, υποστηρίζει μέχρι και netscape 4.0",
"ot_mkdir": "mkdir: δημιουργία νέου φακέλου",
"ot_md": "new-md: δημιουργία νέου markdown εγγράφου",
"ot_msg": "msg: αποστολή μηνύματος στο server log",
"ot_mp": "επιλογές media player",
"ot_cfg": "επιλογές ρυθμίσεων",
"ot_u2i": 'up2k: ανέβασε αρχεία (αν έχεις δικαίωμα εγγραφής) ή ενεργοποίησε τη λειτουργία αναζήτησης για να δεις αν υπάρχουν ήδη στο server$N$Nοι μεταφορτώσεις συνεχίζονται αν διακοπούν, είναι πολυνηματικές και διατηρούν τις χρονοσφραγίδες, αλλά καταναλώνουν περισσότερο CPU από τον [🎈]&nbsp; (βασικός uploader)<br /><br />κατά τη διάρκεια της μεταφόρτωσης, αυτό το εικονίδιο δείχνει την πρόοδό της!',
"ot_u2w": 'up2k: ανέβασε αρχεία με υποστήριξη συνέχισης (κλείσε τον browser και ρίξε τα ίδια αρχεία ξανά μετά)$N$Nπολυνηματικό, διατηρεί τις χρονοσφραγίδες, αλλά καταναλώνει περισσότερο CPU από τον [🎈]&nbsp; (βασικός uploader)<br /><br />κατά τη διάρκεια της μεταφόρτωσης, αυτό το εικονίδιο δείχνει την πρόοδό της!',
"ot_noie": 'Χρησιμοποίησε Chrome / Firefox / Edge',
"ab_mkdir": "δημιουργία φακέλου",
"ab_mkdoc": "νέο markdown έγγραφο",
"ab_msg": "στείλε μήνυμα στο server log",
"ay_path": "πήγαινε σε φακέλους",
"ay_files": "πήγαινε σε αρχεία",
"wt_ren": "μετονομασία επιλεγμένων$NΣυντόμευση: F2",
"wt_del": "διαγραφή επιλεγμένων$NΣυντόμευση: ctrl-K",
"wt_cut": "αποκοπή επιλεγμένων &lt;small&gt;(και επικόλληση αλλού)&lt;/small&gt;$NΣυντόμευση: ctrl-X",
"wt_cpy": "αντιγραφή επιλεγμένων στο πρόχειρο$N(για επικόλληση αλλού)$NΣυντόμευση: ctrl-C",
"wt_pst": "επικόλληση αποκομμένων / αντεγραμμένων$NΣυντόμευση: ctrl-V",
"wt_selall": "επιλογή όλων$NΣυντόμευση: ctrl-A (με το αρχείο επιλεγμένο)",
"wt_selinv": "αντιστροφή επιλογής",
"wt_zip1": "κατέβασμα φακέλου ως συμπιεσμένο αρχείο",
"wt_selzip": "κατέβασμα επιλογής ως συμπιεσμένο αρχείο",
"wt_seldl": "κατέβασμα επιλογής ως μεμονωμένα αρχεία$NΣυντόμευση: Y",
"wt_npirc": "αντιγραφή πληροφοριών τραγουδιού σε μορφή irc",
"wt_nptxt": "αντιγραφή πληροφοριών τραγουδιού ως κείμενο",
"wt_m3ua": "προσθήκη σε m3u λίστα αναπαραγωγής (μετά πάτησε <code>📻αντιγραφή</code>)",
"wt_m3uc": "αντιγραφή m3u λίστας αναπαραγωγής στο πρόχειρο",
"wt_grid": "εναλλαγή πλέγματος / λίστας$NΣυντόμευση: G",
"wt_prev": "προηγούμενο κομμάτι$NΣυντόμευση: J",
"wt_play": "αναπαραγωγή / παύση$NΣυντόμευση: P",
"wt_next": "επόμενο κομμάτι$NΣυντόμευση: L",
"ul_par": "παράλληλες μεταφορτώσεις:",
"ut_rand": "τυχαιοποίηση ονομάτων αρχείων",
"ut_u2ts": "αντιγραφή της τελευταίας τροποποιημένης χρονοσφραγίδας αλλαγής$Nαπό το σύστημά σου στον server\">📅",
"ut_ow": "αντικατάσταση σε ήδη υπάρχοντα αρχεία του server?$N🛡: ποτέ (θα δημιουργηθεί νέο όνομα)$N🕒: αν το αρχείο του server είναι παλαιότερο$N♻: πάντα να αντικαθίστανται αν διαφέρουν",
"ut_mt": "συνέχιση υπολογισμού hash για άλλα αρχεία κατά τη μεταφόρτωση$N$Nαπενεργοποίησέ το αν η CPU ή ο δίσκος σου ζορίζονται",
"ut_ask": 'επιβεβαίωση πριν ξεκινήσει η μεταφόρτωση">💭',
"ut_pot": "βελτίωση ταχύτητας μεταφόρτωσης σε αργές συσκευές$Nμε απλοποίηση του UI",
"ut_srch": "μην ανεβάζεις, έλεγξε αν τα αρχεία$Nυπάρχουν ήδη στον server (ψάχνει σε όλους τους φακέλους που έχεις πρόσβαση)",
"ut_par": "κάνε παύση στις μεταφορτώσεις βάζοντάς το 0$N$Nαύξησε το αν έχεις αργή/μεγάλη καθυστέρηση σύνδεσης$N$Nκράτα το 1 σε LAN ή αν ο server έχει αργό δίσκο",
"ul_btn": "ρίξε αρχεία / φακέλους<br>εδώ (ή κάνε κλικ σε μένα)",
"ul_btnu": "Μ Ε Τ Α Φ Ο Ρ Τ Ω Σ Η",
"ul_btns": "Α Ν Α Ζ Η Τ Η Σ Η",
"ul_hash": "υπολογισμός hash",
"ul_send": "αποστολή",
"ul_done": "ολοκληρώθηκε",
"ul_idle1": "καμία μεταφόρτωση στην ουρά για την ώρα",
"ut_etah": "μέση ταχύτητα &lt;em&gt;υπολογισμού hash&lt;/em&gt; και εκτίμηση χρόνου μέχρι την ολοκλήρωση",
"ut_etau": "μέση ταχύτητα &lt;em&gt;μεταφόρτωσης&lt;/em&gt; και εκτίμηση χρόνου μέχρι την ολοκλήρωση",
"ut_etat": "μέση &lt;em&gt;συνολική&lt;/em&gt; ταχύτητα και εκτίμηση χρόνου μέχρι την ολοκλήρωση",
"uct_ok": "ολοκληρώθηκε επιτυχώς",
"uct_ng": "no-good: απέτυχε / απορρίφθηκε / δεν βρέθηκε",
"uct_done": "ολοκληρωμένα και αποτυχημένα",
"uct_bz": "κάνει hash ή μεταφορτώνει",
"uct_q": "σε αναμονή, εκκρεμεί",
"utl_name": "όνομα αρχείου",
"utl_ulist": "λίστα",
"utl_ucopy": "αντιγραφή",
"utl_links": "σύνδεσμοι",
"utl_stat": "κατάσταση",
"utl_prog": "πρόοδος",
// keep short:
"utl_404": "404",
"utl_err": "ΣΦΑΛΜΑ",
"utl_oserr": "ΣΦ-ΛΕ",
"utl_found": "βρέθηκε",
"utl_defer": "αναβολή",
"utl_yolo": "YOLO",
"utl_done": "έγινε",
"ul_flagblk": "τα αρχεία προστέθηκαν στην ουρά</b><br>αλλά υπάρχει άλλη ενεργή μεταφόρτωση σε άλλη καρτέλα,<br>οπότε περίμενε να τελειώσει αυτό πρώτα",
"ul_btnlk": "ο διακομιστής έχει κλειδώσει αυτήν την επιλογή σε αυτήν την κατάσταση",
"udt_up": "Μεταφόρτωση",
"udt_srch": "Αναζήτηση",
"udt_drop": "ρίξ' το εδώ",
"u_nav_m": '<h6>οκ, τι έχουμε εδώ;</h6><code>Enter</code> = Αρχεία (ένα ή περισσότερα)\n<code>ESC</code> = Ένας φάκελος (μαζί με υποφακέλους)',
"u_nav_b": '<a href="#" id="modal-ok">Αρχεία</a><a href="#" id="modal-ng">Ένας φάκελος</a>',
"cl_opts": "διακόπτες",
"cl_themes": "θέμα",
"cl_langs": "γλώσσα",
"cl_ziptype": "λήψη φακέλου",
"cl_uopts": "διακόπτες μεταφόρτωσης",
"cl_favico": "favicon",
"cl_bigdir": "μεγάλοι φάκελοι",
"cl_hsort": "#ταξινόμηση",
"cl_keytype": "σημείωση πλήκτρων",
"cl_hiddenc": "κρυφές στήλες",
"cl_hidec": "κρύψε",
"cl_reset": "επανεκκίνηση",
"cl_hpick": "πάτησε στις κεφαλίδες στηλών για να τις κρύψεις στον πίνακα παρακάτω",
"cl_hcancel": "η απόκρυψη στηλών ακυρώθηκε",
"ct_grid": '田 το πλέγμα',
"ct_ttips": '◔ ◡ ◔"> συμβουλές εργαλείων',
"ct_thumb": 'σε προβολή πλέγματος, εναλλαγή εικονιδίων ή μικρογραφιών$NΠλήκτρο συντόμευσης: T">🖼️ μικρογραφίες',
"ct_csel": 'χρησιμοποίησε CTRL και SHIFT για επιλογή αρχείων σε προβολή πλέγματος">επιλογή',
"ct_ihop": 'όταν η προβολή εικόνων κλείνει, κάνε scroll στο τελευταίο προβολόμενο αρχείο">g⮯',
"ct_dots": 'εμφάνιση κρυφών αρχείων (αν το επιτρέπει ο server)">dotfiles',
"ct_qdel": 'όταν διαγράφεις αρχεία, ζήτα επιβεβαίωση μόνο μία φορά">γρήγορη διαγραφή',
"ct_dir1st": 'ταξινόμηση φακέλων πριν από τα αρχεία">📁 πρώτα',
"ct_nsort": 'φυσική ταξινόμηση (για ονόματα αρχείων με αριθμούς στην αρχή)">φυσική ταξινόμηση',
"ct_utc": 'εμφάνιση όλων των ημερομηνιών σε UTC">UTC',
"ct_readme": 'εμφάνιση README.md στις λίστες φακέλων">📜 πληροφορίες',
"ct_idxh": 'εμφάνιση index.html αντί για λίστα φακέλων">html',
"ct_sbars": 'εμφάνιση μπαρών κύλισης">⟊',
"cut_umod": "αν το αρχείο υπάρχει ήδη στον server, ενημέρωσέ το με την τελευταία χρονοσφραγίδα τροποποίησης για να ταιριάζει με το τοπικό αρχείο (απαιτεί δικαιώματα εγγραφής+διαγραφής)\">re📅",
"cut_turbo": "το κουμπί yolo, πιθανόν να ΜΗΝ ΘΕΛΕΙΣ να το ενεργοποιήσεις:$N$Nχρησιμοποίησέ το αν μεταφορτώνεις πολλά αρχεία και χρειάστηκε να ξαναρχίσεις, και θες να συνεχίσεις τη μεταφότρωση όσο το δυνατόν πιο γρήγορα$N$Nαντικαθιστά τον έλεγχο hash με απλό <em>&quot;έχει το ίδιο μέγεθος αρχείου στον server?&quot;</em> οπότε αν το περιεχόμενο είναι διαφορετικό, ΔΕΝ θα ανέβει$N$Nπρέπει να το κλείσεις όταν τελειώσει η μεταφόρτωση και μετά να &quot;μεταφορτώσεις&quot; πάλι τα ίδια αρχεία για να τα επιβεβαιώσει το τοπικό σου πρόγραμμα\">turbo",
"cut_datechk": "δεν επηρεάζει τίποτα εκτός αν το turbo είναι ενεργοποιημένο$N$Nμειώνει λίγο τον παράγοντα yolo; ελέγχει αν οι χρονοσφραγίδες στο διακομιστή ταιριάζουν με τα δικά σου$N$Nπιάνει <em>θεωρητικά</em> τις περισσότερες μισοτελειωμένες/κατεστραμμένες μεταφορτώσεις, αλλά δεν αντικαθιστά τον έλεγχο με το turbo απενεργοποιημένο μετέπειτα\">έλεγχος ημερομηνίας",
"cut_u2sz": "μέγεθος (σε MiB) κάθε κομματιού μεταφόρτωσης; μεγάλες τιμές λειτουργούν καλύτερα σε μεγαλύτερες αποστάσεις διακομιστή-πελάτη. Δοκίμασε μικρές τιμές σε πολύ άστατες συνδέσεις",
"cut_flag": "εξασφαλίζει ότι μόνο μία καρτέλα μεταφορτώνει κάθε φορά $N -- οι άλλες καρτέλες πρέπει να το έχουν κι αυτές ενεργό $N -- επηρεάζει μόνο τις καρτέλες που βρίσκονται στο ίδιο διεύθυνση",
"cut_az": "μεταφόρτωσε τα αρχεία αλφαβητικά, αντί για το μικρότερο αρχείο, πρώτα$N$Nη αλφαβητική σειρά βοηθά να καταλάβεις αν κάτι χάλασε στο διακομιστή αλλά κάνει το ανέβασμα λίγο πιο αργό σε fiber / LAN",
"cut_nag": "ειδοποίηση λειτουργικού συστήματος όταν τελειώσει η μεταφόρτωση$N(μόνο αν ο browser ή η καρτέλα δεν είναι ενεργά)",
"cut_sfx": "ηχητική ειδοποίηση όταν τελειώσει η μεταφόρτωση$N(μόνο αν ο browser ή η καρτέλα δεν είναι ενεργά)",
"cut_mt": "χρησιμοποίησε multithreading για να επιταχύνεις το hashing των αρχείων$N$Nχρησιμοποιεί web-workers και χρειάζεται$Nπερισσότερη RAM (μέχρι 512 MiB επιπλέον)$N$Nκάνει το https 30% πιο γρήγορο, το http 4.5x πιο γρήγορο\">mt",
"cut_wasm": "χρησιμοποίησε wasm αντί για τον ενσωματωμένο hasher του browser; βελτιώνει την ταχύτητα σε chrome-based browsers αλλά αυξάνει το φορτίο της CPU, και παλιές εκδόσεις chrome έχουν bugs που κάνουν το browser να τρώει όλη τη RAM και να κρασάρει αν ενεργοποιηθεί\">wasm",
"cft_text": "κείμενο favicon (κενό και ανανέωση για απενεργοποίηση)",
"cft_fg": "χρώμα προσκηνίου",
"cft_bg": "χρώμα παρασκηνίου",
"cdt_lim": "μέγιστος αριθμός αρχείων προς εμφάνιση σε ένα φάκελο",
"cdt_ask": "όταν φτάνεις στο τέλος,$Nαντί να φορτώσει περισσότερα αρχεία,$Nρώτα τι να κάνει",
"cdt_hsort": "πόσους κανόνες ταξινόμησης (&lt;code&gt;,sorthref&lt;/code&gt;) να συμπεριλάβει σε URLs πολυμέσων. Αν το βάλεις 0 αγνοεί και κανόνες ταξινόμησης στους συνδέσμους πολυμέσων",
"tt_entree": "εμφάνιση navpane (δέντρο διαδρομών)$NΠλήκτρο συντόμευσης: B",
"tt_detree": "εμφάνιση breadcrumbs (καρτέλες διαδρομών)$NΠλήκτρο συντόμευσης: B",
"tt_visdir": "κύλιση στον επιλεγμένο φάκελο",
"tt_ftree": "εναλλαγή δέντρου διαδρομών / αρχείων κειμένου$NΠλήκτρο συντόμευσης: V",
"tt_pdock": "εμφάνιση γονικών φακέλων σε σταθερή μπάρα επάνω",
"tt_dynt": "αυτόματη επέκταση καθώς επεκτείνεται το δέντρο διαδρομών",
"tt_wrap": "αναδίπλωση λέξεων",
"tt_hover": "αποκάλυψη των γραμμών που ξεπερνούν το πλάτος με το ποντίκι πάνω τους$N( σπάει το scroll εκτός αν το ποντίκι $N&nbsp; είναι στην αριστερή στήλη )",
"ml_pmode": "στο τέλος του φακέλου...",
"ml_btns": "εντολές",
"ml_tcode": "μετακωδικοποίηση",
"ml_tcode2": "μετακωδικοποίηση σε",
"ml_tint": "φίλτρο χρώματος",
"ml_eq": "ισοσταθμιστής ήχου",
"ml_drc": "συμπιεστής δυναμικής εμβέλειας",
"mt_loop": "επανάληψη ενός τραγουδιού\">🔁",
"mt_one": "σταμάτα μετά από ένα τραγούδι\">1⃣",
"mt_shuf": "τυχαία σειρά τραγουδιών σε κάθε φάκελο\">🔀",
"mt_aplay": "αυτόματη αναπαραγωγή αν υπάρχει song-ID στη διεύθυνση που μπήκες στο διακομιστή$N$Nη απενεργοποίηση αυτού, σταματά το URL από το να ενημερώνεται με τα song-ID ενώ παίζει η μουσική για να αποτραπεί η αυτόματη αναπαραγωγή αν χαθούν αυτές οι ρυθμίσεις αλλά το URL παραμείνει το ίδιο\">a▶",
"mt_preload": "ξεκίνα τη φόρτωση του επόμενου τραγουδιού κοντά στο τέλος για συνεχόμενη ακρόαση\">προφόρτωση",
"mt_prescan": "πήγαινε στον επόμενο φάκελο πριν τελειώσει το τελευταίο τραγούδι$Nγια να μη σταματήσει το πρόγραμμα περιήγησης να παίζει μουσική\">nav",
"mt_fullpre": "προσπάθησε να προφορτώσεις ολόκληρο το τραγούδι;$N✅ ενεργό σε <b>αναξιόπιστες</b> συνδέσεις,$N❌ πιθανότατα απενεργοποιημένο σε αργές συνδέσεις\">πλήρες",
"mt_fau": "σε κινητά, πρόλαβε να μην σταματήσει η μουσική αν το επόμενο τραγούδι δεν προφορτώθηκε γρήγορα (μπορεί να προκαλέσει πρόβλημα στην εμφάνιση των ετικετών)\">☕️",
"mt_waves": "γραμμή αναζήτησης κυματομορφής:$Nεμφάνιση έντασης ήχου στην μπάρα αναζήτησης\">~s",
"mt_npclip": "εμφάνισε κουμπιά για αντιγραφή του τρέχοντος τραγουδιού\">/np",
"mt_m3u_c": "εμφάνισε κουμπιά για αντιγραφή των$Nεπιλεγμένων τραγουδιών ως καταχωρήσεις λίστας m3u8\">📻",
"mt_octl": "ενσωμάτωση στο λειτουργικό σύστημα (σηντομεύσεις πλήκτρων πολυμέσων / osd)\">έλεγχος-OS",
"mt_oseek": "επιτρέπει την αναζήτηση μέσω ενσωμάτωσης του λειτουργικού συστήματος$N$Nσημείωση: σε μερικές συσκευές (iPhones),$Nαντικαθιστά το κουμπί επόμενου τραγουδιού\">αναζήτηση",
"mt_oscv": "εμφάνιση εξωφύλλου άλμπουμ σε osd\">εξώφυλλο",
"mt_follow": "κρατά το τρέχον κομμάτι ορατό κατά την κύλιση\">🎯",
"mt_compact": "συμπαγή κουμπιά ελέγχου\">⟎",
"mt_uncache": "καθάρισε την προσωρινή μνήμη &nbsp;(δοκίμασε αυτό αν ο browser έχει αποθηκεύσει$Nχαλασμένο αντίγραφο τραγουδιού και αρνείται να παίξει)\">εκκαθάριση",
"mt_mloop": "τυχαία αναπαραγωγή στον ανοικτό φάκελο\">🔁 τυχαία αναπαραγωγή",
"mt_mnext": "φόρτωση επόμενου φακέλου και συνέχιση\">📂 επόμενο",
"mt_mstop": "σταμάτησε την αναπαραγωγή\">⏸ σταμάτημα",
"mt_cflac": "μετατροπή flac / wav σε opus\">flac",
"mt_caac": "μετατροπή aac / m4a σε opus\">aac",
"mt_coth": "μετατροπή όλων των άλλων (εκτός των mp3) σε opus\">άλλο",
"mt_c2opus": "καλύτερη επιλογή για desktop, laptop, android\">opus",
"mt_c2owa": "opus-weba, για iOS 17.5 και νεότερα\">owa",
"mt_c2caf": "opus-caf, για iOS 11 έως 17\">caf",
"mt_c2mp3": "χρησιμοποίησε αυτό σε πολύ παλιές συσκευές\">mp3",
"mt_c2flac": "βέλτιστη ποιότητα ήχου αλλά τεράστιο αρχείο για μεταφόρτωση\">flac",
"mt_c2wav": "ασυμπίεστη αναπαραγωγή (ακόμα μεγαλύτερο αρχείο)\">wav",
"mt_c2ok": "μια χαρά, σοφή επιλογή",
"mt_c2nd": "δεν είναι η προτεινόμενη μορφή εξόδου για τη συσκευή σου, αλλά αυτό είναι ok",
"mt_c2ng": "η συσκευή σου φαίνεται να μην υποστηρίζει αυτήν τη μορφή εξόδου, αλλά ας το δοκιμάσουμε ούτως ή άλλως",
"mt_xowa": "υπάρχουν bugs σε iOS που εμποδίζουν την αναπαραγωγή στο παρασκήνιο με αυτήν τη μορφή· χρησιμοποίησε caf ή mp3 αντ’ αυτού",
"mt_tint": "επίπεδο φόντου (0-100) στην μπάρα αναζήτησης$Nγια να κάνεις το buffering λιγότερο ενοχλητικό",
"mt_eq": "ενεργοποιεί τον ισοσταθμιστή και τον έλεγχο ενίσχυσης;$N$Nενίσχυση &lt;code&gt;0&lt;/code&gt; = στάνταρ 100% ένταση (απαράλλαχτη)$N$Nεύρος &lt;code&gt;1 &nbsp;&lt;/code&gt; = στάνταρ στερεοφωνικό (απαράλλαχτο)$Nεύρος &lt;code&gt;0.5&lt;/code&gt; = 50% αριστερά-δεξιά μίξη ήχου$Nεύρος &lt;code&gt;0 &nbsp;&lt;/code&gt; = μονοφωνικό$N$Nενίσχυση &lt;code&gt;-0.8&lt;/code&gt; &amp; εύρος &lt;code&gt;10&lt;/code&gt; = αφαίρεση φωνής :^)$N$Nη ενεργοποίηση του ισοσταθμιστή κάνει τα άλμπουμ χωρίς κενά, να παίζουν χωρίς καθόλου κενά, οπότε άφησέ το ενεργό με όλες τις τιμές στο μηδέν (εκτός από εύρος = 1) αν σε νοιάζει",
"mt_drc": "ενεργοποιεί τον συμπιεστή δυναμικής εμβέλειας (εξομάλυνση έντασης / ακραία συμπίεση έντασης); θα ενεργοποιήσει και τον ισοσταθμιστή για να ισορροπήσει τον ήχο, οπότε βάλε όλα τα πεδία ισοσταθμιστή εκτός από το 'εύρος' στο 0 αν δεν το θες$N$Nχαμηλώνει την ένταση του ήχου πάνω από το όριο (THRESHOLD) dB; για κάθε RATIO dB πέρα από το όριο υπάρχει 1 dB εξόδου, οπότε οι προεπιλεγμένες τιμές όριο -24 και 'λόγος' 12 σημαίνουν ότι δεν θα ξεπεράσει ποτέ τα -22 dB και είναι ασφαλές να αυξήσεις την ενίσχυση ισοσταθμιστή σε 0.8, ή και 1.8 με ATK 0 και μεγάλο RLS όπως 90 (δουλεύει μόνο σε firefox· το RLS είναι max 1 σε άλλους browsers)$N$N(δες wikipedia, το εξηγούν καλύτερα)",
"mb_play": "παίξε",
"mm_hashplay": "να παίξω αυτό το αρχείο ήχου;",
"mm_m3u": "πάτα <code>Enter/OK</code> για Αναπαραγωγή\nπάτα <code>ESC/Άκυρο</code> για Επεξεργασία",
"mp_breq": "χρειάζεται firefox 82+ ή chrome 73+ ή iOS 15+",
"mm_bload": "φορτώνει...",
"mm_bconv": "μετατροπή σε {0}, περίμενε...",
"mm_opusen": "ο browser σου δεν παίζει αρχεία aac / m4a;\nη μετατροπή σε opus είναι τώρα ενεργή",
"mm_playerr": "η αναπαραγωγή, απέτυχε: ",
"mm_eabrt": "Η προσπάθεια αναπαραγωγής ακυρώθηκε",
"mm_enet": "Η σύνδεση του ίντερνέτ σου είναι χάλια",
"mm_edec": "Το αρχείο αυτό είναι μάλλον κατεστραμμένο;;",
"mm_esupp": "Ο browser σου δεν καταλαβαίνει αυτή τη μορφή ήχου",
"mm_eunk": "Άγνωστο σφάλμα",
"mm_e404": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 404: Το αρχείο δεν βρέθηκε.",
"mm_e403": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 403: Άρνηση πρόσβασης.\n\nΔοκίμασε F5 για επαναφόρτωση, ίσως να έχεις αποσυνδεθεί",
"mm_e500": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 500: Έλεγξε τα logs του διακομιστή.",
"mm_e5xx": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα διακομιστή",
"mm_nof": "δεν βρέθηκαν άλλα αρχεία ήχου τριγύρω",
"mm_prescan": "Αναζήτηση μουσικής για επόμενο τραγούδι...",
"mm_scank": "Βρέθηκε το επόμενο τραγούδι:",
"mm_uncache": "κρυφή μνήμη καθαρίστηκε· όλα τα τραγούδια θα ξανακατεβούν στην επόμενη αναπαραγωγή",
"mm_hnf": "το τραγούδι αυτό πλέον δεν υπάρχει",
"im_hnf": "η εικόνα αυτή πλέον δεν υπάρχει",
"f_empty": "αυτός ο φάκελος είναι άδειος",
"f_chide": "αυτό θα κρύψει τη στήλη «{0}»\n\nμπορείς να εμφανίσεις τις στήλες από τις ρυθμίσεις",
"f_bigtxt": "αυτό το αρχείο είναι {0} MiB σε μέγεθος — σίγουρα θέλεις να το δεις ως κείμενο;",
"f_bigtxt2": "να δεις μόνο το τέλος του αρχείου αντί για όλο; αυτό ενεργοποιεί και το following/tailing, που δείχνει νέες γραμμές που προστίθενται ζωντανά",
"fbd_more": '<div id="blazy">εμφανίζονται <code>{0}</code> από <code>{1}</code> αρχεία; <a href="#" id="bd_more">δείξε {2}</a> ή <a href="#" id="bd_all">δείξε τα όλα</a></div>',
"fbd_all": '<div id="blazy">εμφανίζονται <code>{0}</code> από <code>{1}</code> αρχεία; <a href="#" id="bd_all">δείξε όλα</a></div>',
"f_anota": "μόνο {0} από τα {1} αντικείμενα επιλέχθηκαν;\nγια να επιλέξεις ολόκληρο το φάκελο, κύλησε πρώτα μέχρι κάτω",
"f_dls": 'οι σύνδεσμοι αρχείων στον τρέχοντα φάκελο έχουν\nμετατραπεί σε συνδέσμους λήψης',
"f_partial": "Για να κατεβάσεις με ασφάλεια ένα αρχείο που ανεβαίνει, κλίκαρε το αρχείο με το ίδιο όνομα, αλλά χωρίς την κατάληξη <code>.PARTIAL</code>. Πάτα Άκυρο ή Escape για να σταματήσεις.\n\nΠάτα OK / Enter αν αγνοείς την προειδοποίηση και κατέβασε το <code>.PARTIAL</code> αρχείο, που σχεδόν σίγουρα θα είναι κατεστραμμένο.",
"ft_paste": "επικόλλησε {0} αντικείμενα$NΠλήκτρο συντόμευσης: ctrl-V",
"fr_eperm": 'δεν μπορεί να μετονομαστεί:\nδεν έχεις δικαίωμα “μετακίνησης” σε αυτόν το φάκελο',
"fd_eperm": 'δεν μπορεί να διαγραφεί:\nδεν έχεις δικαίωμα “διαγραφής” σε αυτόν το φάκελο',
"fc_eperm": 'δεν μπορεί να κοπεί:\nδεν έχεις δικαίωμα “μετακίνησης” σε αυτόν το φάκελο',
"fp_eperm": 'δεν μπορεί να επικολληθεί:\nδεν έχεις δικαίωμα “εγγραφής” σε αυτόν το φάκελο',
"fr_emore": "επίλεξε τουλάχιστον ένα αντικείμενο για μετονομασία",
"fd_emore": "επίλεξε τουλάχιστον ένα αντικείμενο για διαγραφή",
"fc_emore": "επίλεξε τουλάχιστον ένα αντικείμενο για αποκοπή",
"fcp_emore": "επίλεξε τουλάχιστον ένα αντικείμενο για αντιγραφή στο πρόχειρο",
"fs_sc": "μοιράσου το φάκελο που βρίσκεσαι",
"fs_ss": "μοιράσου τα επιλεγμένα αρχεία",
"fs_just1d": "δεν μπορείς να επιλέξεις περισσότερους από έναν φακέλους,\nή να αναμείξεις αρχεία και φακέλους στην ίδια επιλογή",
"fs_abrt": "❌ ακύρωση",
"fs_rand": "🎲 τυχαίο όνομα",
"fs_go": "✅ δημιούργησε κοινή χρήση",
"fs_name": "όνομα",
"fs_src": "πηγή",
"fs_pwd": "κωδικός",
"fs_exp": "λήξη",
"fs_tmin": "λεπτά",
"fs_thrs": "ώρες",
"fs_tdays": "ημέρες",
"fs_never": "αιώνιο",
"fs_pname": "προαιρετικό όνομα συνδέσμου; αν είναι κενό, θα είναι τυχαίο",
"fs_tsrc": "το αρχείο ή ο φάκελος προς κοινή χρήση",
"fs_ppwd": "προαιρετικός κωδικός",
"fs_w8": "δημιουργία κοινής χρήσης...",
"fs_ok": "πάτα <code>Enter/OK</code> για Πρόχειρο\nπάτα <code>ESC/Άκυρο</code> για Κλείσιμο",
"frt_dec": "μπορεί να διορθώσει μερικές περιπτώσεις κατεστραμμένων ονομάτων αρχείων\">αποκωδικοποίηση url",
"frt_rst": "επανέφερε τα ονόματα αρχείων στα αρχικά τους\">↺ επαναφορά",
"frt_abrt": "ακύρωσε και κλείσε αυτό το παράθυρο\">❌ ακύρωση",
"frb_apply": "ΕΦΑΡΜΟΓΗ ΜΕΤΟΝΟΜΑΣΙΑΣ",
"fr_adv": "μαζική / μεταδεδομένα / μετονομασία με πρότυπα\">προχωρημένη",
"fr_case": "regex με διάκριση πεζών/κεφαλαίων\">case",
"fr_win": "ασφαλή ονόματα για windows; αντικαθιστά <code>&lt;&gt;:&quot;\\|?*</code> με ιαπωνικούς χαρακτήρες πλήρους πλάτους\">win",
"fr_slash": "αντικαθίσταται <code>/</code> με χαρακτήρα που δεν δημιουργεί νέους φακέλους\">όχι /",
"fr_re": "μοτίβα αναζήτησης (regex) για αναζήτηση στα αρχικά ονόματα; τα καταγραφόμενα groups μπορούν να χρησιμοποιηθούν στο πεδίο μορφοποίησης παρακάτω όπως &lt;code&gt;(1)&lt;/code&gt; και &lt;code&gt;(2)&lt;/code&gt; και ούτω καθεξής",
"fr_fmt": "εμπνευσμένο από foobar2000:$N&lt;code&gt;(title)&lt;/code&gt; αντικαθίσταται από τίτλο τραγουδιού,$N&lt;code&gt;[(artist) - ](title)&lt;/code&gt; παραλείπει το [this] αν το artist είναι κενό$N&lt;code&gt;$lpad((tn),2,0)&lt;/code&gt; γεμίζει τον αριθμό κομματιού σε 2 ψηφία",
"fr_pdel": "διαγραφή",
"fr_pnew": "αποθήκευση ως",
"fr_pname": "δώσε όνομα για τη νέα προεπιλογή",
"fr_aborted": "ακυρώθηκε",
"fr_lold": "παλιό όνομα",
"fr_lnew": "νέο όνομα",
"fr_tags": "ετικέτες για τα επιλεγμένα αρχεία (μόνο για ανάγνωση):",
"fr_busy": "μετονομασία {0} αντικειμένων...\n\n{1}",
"fr_efail": "αποτυχία μετονομασίας:\n",
"fr_nchg": "{0} από τα νέα ονόματα άλλαξαν λόγω <code>win</code> και/ή <code>όχι /</code>\n\nΕίναι ΟΚ να συνεχίσουμε με αυτά τα ονόματα;",
"fd_ok": "διαγραφή OK",
"fd_err": "αποτυχία διαγραφής:\n",
"fd_none": "δεν διαγράφηκε τίποτα; ίσως μπλοκαρισμένο από τις ρυθμίσεις του διακομιστή (xbd);",
"fd_busy": "διαγραφή {0} αντικειμένων...\n\n{1}",
"fd_warn1": "ΔΙΑΓΡΑΦΗ αυτών των {0} αντικειμένων;",
"fd_warn2": "<b>Τελευταία ευκαιρία!</b> Δεν υπάρχει αναίρεση. Διαγραφή;",
"fc_ok": "αποκοπή {0} αντικειμένων",
"fc_warn": 'αποκοπή {0} αντικειμένων\n\nαλλά: μόνο <b>αυτή</b> η καρτέλα browser μπορεί να τα επικολλήσει\n(λόγω πολύ μεγάλης επιλογής)',
"fcc_ok": "αντιγράφηκαν {0} αντικείμενα στο πρόχειρο",
"fcc_warn": "αντιγράφηκαν {0} αντικείμενα στο πρόχειρο\n\nαλλά: μόνο <b>αυτή</b> η καρτέλα browser μπορεί να τα επικολλήσει\n(λόγω πολύ μεγάλης επιλογής)",
"fp_apply": "χρησιμοποίησε αυτά τα ονόματα",
"fp_ecut": "πρώτα κάνε αποκοπή ή αντιγραφή κάποιων αρχείων / φακέλων για επικόλληση / μετακίνηση\n\nσημείωση: μπορείς να αποκόπτεις / επικολλάς ανάμεσα σε διαφορετικές καρτέλες browser",
"fp_ename": "τα {0} αντικείμενα δεν μπορούν να μετακινηθούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό για να τα αγνοήσεις:",
"fcp_ename": "τα {0} αντικείμενα δεν μπορούν να αντιγραφούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό για να τα αγνοήσεις:",
"fp_emore": "υπάρχουν ακόμα συγκρούσεις ονομάτων που πρέπει να διορθωθούν",
"fp_ok": "μετακίνηση OK",
"fcp_ok": "αντιγραφή OK",
"fp_busy": "μετακίνηση {0} αντικειμένων...\n\n{1}",
"fcp_busy": "αντιγραφή {0} αντικειμένων...\n\n{1}",
"fp_err": "αποτυχία μετακίνησης:\n",
"fcp_err": "αποτυχία αντιγραφής:\n",
"fp_confirm": "να μετακινηθούν αυτά τα {0} αντικείμενα εδώ;",
"fcp_confirm": "να αντιγραφούν αυτά τα {0} αντικείμενα εδώ;",
"fp_etab": "αποτυχία ανάγνωσης πρόχειρου από άλλη καρτέλα browser",
"fp_name": "μεταφόρτωση αρχείου από τη συσκευή σου. Δώσε του όνομα:",
"fp_both_m": '<h6>διάλεξε τι θα επικολλήσεις</h6><code>Enter</code> = Μετακίνηση {0} αρχείων από «{1}»\n<code>ESC</code> = Μεταφόρτωση {2} αρχείων από τη συσκευή σου',
"fcp_both_m": '<h6>διάλεξε τι θα επικολλήσεις</h6><code>Enter</code> = Αντιγραφή {0} αρχείων από «{1}»\n<code>ESC</code> = Μεταφόρτωση {2} αρχείων από τη συσκευή σου',
"fp_both_b": '<a href="#" id="modal-ok">Μετακίνηση</a><a href="#" id="modal-ng">Μεταφόρτωση</a>',
"fcp_both_b": '<a href="#" id="modal-ok">Αντιγραφή</a><a href="#" id="modal-ng">Μεταφόρτωση</a>',
"mk_noname": "γράψε ένα όνομα στο πεδίο κειμένου αριστερά πριν το κάνεις :p",
"tv_load": "Φόρτωση αρχείου κειμένου:\n\n{0}\n\n{1}% ({2} από {3} MiB φορτωμένα)",
"tv_xe1": "αδυναμία φόρτωσης αρχείου κειμένου:\n\nσφάλμα ",
"tv_xe2": "404, αρχείο δεν βρέθηκε",
"tv_lst": "λίστα αρχείων κειμένου σε",
"tvt_close": "επιστροφή στην προβολή φακέλου$NΣυντόμευση: M (ή Esc)\">❌ κλείσιμο",
"tvt_dl": "κατέβασε αυτό το αρχείο$NΣυντόμευση: Y\">💾 λήψη",
"tvt_prev": "προβολή προηγούμενου εγγράφου$NΣυντόμευση: i\">⬆ προηγούμενο",
"tvt_next": "προβολή επόμενου εγγράφου$NΣυντόμευση: K\">⬇ επόμενο",
"tvt_sel": "επέλεξε αρχείο &nbsp; (για αποκοπή / αντιγραφή / διαγραφή / ...)$NΣυντόμευση: S\">επιλογή",
"tvt_edit": "άνοιγμα αρχείου στον επεξεργαστή κειμένου$NΣυντόμευση: E\">✏️ επεξεργασία",
"tvt_tail": "παρακολούθηση αρχείου για αλλαγές; εμφάνιση νέων γραμμών σε πραγματικό χρόνο\">📡 παρακολούθηση",
"tvt_wrap": "αναδίπλωση λέξεων\">↵",
"tvt_atail": "κλείδωμα κύλισης στο κάτω μέρος\">⚓",
"tvt_ctail": "αποκωδικοποίηση χρωμάτων τερματικού (ansi escape codes)\">🌈",
"tvt_ntail": "όριο κύλισης (πόσα bytes κειμένου να κρατούνται φορτωμένα)",
"m3u_add1": "το τραγούδι προστέθηκε στη λίστα m3u",
"m3u_addn": "προστέθηκαν {0} τραγούδια στη λίστα m3u",
"m3u_clip": "η λίστα m3u αντιγράφηκε στο πρόχειρο\n\nπρέπει να φτιάξεις ένα νέο αρχείο κειμένου με όνομα η_λίσταου.m3u και να επικολλήσεις τη λίστα μέσα· αυτό θα το καταστήσει αναπαράξιμο",
"gt_vau": "μην δείχνεις το βίντεο, παίξε μόνο τον ήχο\">🎧",
"gt_msel": "ενεργοποίηση επιλογής αρχείων; ctrl-κλικ σε αρχείο για παράκαμψη$N$N&lt;em&gt;όταν είναι ενεργό: διπλό κλικ σε αρχείο / φάκελο το ανοίγει&lt;/em&gt;$N$NΣυντόμευση: S\">πολλαπλή επιλογή",
"gt_crop": "κεντραρισμένη περικοπή μικρογραφιών\">περικοπή",
"gt_3x": "μικρογραφίες υψηλής ανάλυσης\">3x",
"gt_zoom": "ζουμ",
"gt_chop": "κόψε",
"gt_sort": "ταξινόμηση κατά",
"gt_name": "όνομα",
"gt_sz": "μέγεθος",
"gt_ts": "ημερομηνία",
"gt_ext": "τύπος",
"gt_c1": "μεγαλύτερη περικοπή ονομάτων αρχείων (δείξε λιγότερα)",
"gt_c2": "μικρότερη περικοπή ονομάτων αρχείων (δείξε περισσότερα)",
"sm_w8": "αναζήτηση...",
"sm_prev": "τα παρακάτω αποτελέσματα αναζήτησης προέρχονται από προηγούμενη αναζήτηση:\n ",
"sl_close": "κλείσιμο αποτελεσμάτων αναζήτησης",
"sl_hits": "εμφανίζονται {0} αποτελέσματα",
"sl_moar": "φόρτωσε περισσότερα",
"s_sz": "μέγεθος",
"s_dt": "ημερομηνία",
"s_rd": "μονοπάτι",
"s_fn": "όνομα",
"s_ta": "ετικέτες",
"s_ua": "ανέβηκε@",
"s_ad": "προχωρ.",
"s_s1": "ελάχιστο σε MiB",
"s_s2": "μέγιστο σε MiB",
"s_d1": "ελάχιστο iso8601",
"s_d2": "μέγιστο iso8601",
"s_u1": "μεταφορτώθηκε αργότερα",
"s_u2": "και/ή πριν",
"s_r1": "το μονοπάτι περιέχει &nbsp; (χωρισμένα με κενό)",
"s_f1": "το όνομα περιέχει &nbsp; (άρνηση με -nope)",
"s_t1": "οι ετικέτες περιέχουν &nbsp; (^=αρχή, τέλος=$)",
"s_a1": "συγκεκριμένες ιδιότητες μεταδεδομένων",
"md_eshow": "δεν μπορεί να εμφανιστεί ",
"md_off": "[📜<em>readme</em>] απενεργοποιημένο στο [⚙️] -- κρυμμένο έγγραφο",
"badreply": "Αποτυχία ανάλυσης απάντησης από το διακομιστή",
"xhr403": "403: Πρόσβαση αρνήθηκε\n\nδοκίμασε το F5, ίσως αποσυνδέθηκες",
"xhr0": "άγνωστο (πιθανόν αποσύνδεση από το διακομιστή ή ο διακομιστής είναι εκτός σύνδεσης)",
"cf_ok": "συγγνώμη γι' αυτό -- η προστασία DD" + wah + "oS ενεργοποιήθηκε\n\nοι διαδικασίες θα συνεχιστούν σε περίπου 30 δευτερόλεπτα\n\nαν δεν γίνει τίποτα, πάτα F5 για επαναφόρτωση",
"tl_xe1": "αδύνατη η λίστα υποφακέλων:\n\nσφάλμα ",
"tl_xe2": "404: Ο φάκελος δεν βρέθηκε",
"fl_xe1": "αδύνατη η λίστα αρχείων σε φάκελο:\n\nσφάλμα ",
"fl_xe2": "404: Ο φάκελος δεν βρέθηκε",
"fd_xe1": "αδύνατη η δημιουργία υποφακέλου:\n\nσφάλμα ",
"fd_xe2": "404: Ο γονικός φάκελος δεν βρέθηκε",
"fsm_xe1": "αδύνατη η αποστολή μηνύματος:\n\nσφάλμα ",
"fsm_xe2": "404: Ο γονικός φάκελος δεν βρέθηκε",
"fu_xe1": "αποτυχία φόρτωσης λίστας unpost από το διακομιστή:\n\nσφάλμα ",
"fu_xe2": "404: Το αρχείο δεν βρέθηκε??",
"fz_tar": "μη συμπιεσμένο αρχείο gnu-tar (linux / mac)",
"fz_pax": "μη συμπιεσμένο pax-format tar (πιο αργό)",
"fz_targz": "gnu-tar με συμπίεση gzip επίπεδο 3$N$Nσυνήθως πολύ αργό, οπότε$Nχρησιμοποίησε καλύτερα μη συμπιεσμένο tar",
"fz_tarxz": "gnu-tar με συμπίεση xz επίπεδο 1$N$Nσυνήθως πολύ αργό, οπότε$Nχρησιμοποίησε καλύτερα μη συμπιεσμένο tar",
"fz_zip8": "zip με ονόματα αρχείων utf8 (ίσως να κολλάει σε windows 7 και παλιότερα)",
"fz_zipd": "zip με παραδοσιακά ονόματα cp437, για πολύ παλιό λογισμικό",
"fz_zipc": "cp437 με crc32 υπολογισμένο νωρίτερα,$Nγια MS-DOS PKZIP v2.04g (οκτώβριος 1993)$N(παίρνει παραπάνω χρόνο πριν ξεκινήσει η μεταφόρτωση)",
"un_m1": "μπορείς να διαγράψεις τα πρόσφατα αρχεία που μεταφόρτωσες (ή να ακυρώσεις τα μισοτελειωμένα) παρακάτω",
"un_upd": "ανανέωση",
"un_m4": "ή μοιράσου τα αρχεία που βλέπεις παρακάτω:",
"un_ulist": "εμφάνιση",
"un_ucopy": "αντιγραφή",
"un_flt": "προαιρετικό φίλτρο:&nbsp; η διεύθυνση πρέπει να περιέχει",
"un_fclr": "καθαρισμός φίλτρου",
"un_derr": "αποτυχία διαγραφής unpost:\n",
"un_f5": "κάτι χάλασε, δοκίμασε την ανανέωση ή πάτα F5",
"un_uf5": "συγγνώμη αλλά πρέπει να ανανεώσεις τη σελίδα (πχ με F5 ή CTRL-R) πριν ακυρώσεις αυτήν την αποστολή",
"un_nou": "<b>προσοχή:</b> ο διακομιστής είναι πολύ φορτωμένος για να δείξει μισοτελειωμένες αποστολές· πάτα την ανανέωση, σε λίγο",
"un_noc": "<b>προσοχή:</b> το unpost των ολοκληρωμένων αρχείων δεν επιτρέπεται από τη ρύθμιση του διακομιστή",
"un_max": "εμφανίζονται τα πρώτα 2000 αρχεία (χρησιμοποίησε φίλτρο)",
"un_avail": "μπορείς να διαγράψεις {0} πρόσφατα αρχεία<br />μπορείς να ακυρώσεις {1} μισοτελειωμένες αποστολές",
"un_m2": "ταξινομημένα κατά χρόνο μεταφόρτωσης; τα πιο πρόσφατα πρώτα:",
"un_no1": "άκυρο! καμία μεταφόρτωση δεν είναι αρκετά πρόσφατη",
"un_no2": "άκυρο! καμία μεταφόρτωση με αυτό το φίλτρο δεν είναι αρκετά πρόσφατη",
"un_next": "διάγραψε τα επόμενα {0} αρχεία παρακάτω",
"un_abrt": "άκυρο",
"un_del": "διαγραφή",
"un_m3": "φορτώνω τις πρόσφατες μεταφορτώσεις σου...",
"un_busy": "διαγράφω {0} αρχεία...",
"un_clip": "αντιγράφηκαν {0} σύνδεσμοι στο πρόχειρο",
"u_https1": "πρέπει",
"u_https2": "μετάβαση σε https",
"u_https3": "για καλύτερη απόδοση",
"u_ancient": 'ο browser σου είναι εντυπωσιακά απαρχαιωμένος — ίσως να <a href="#" onclick="goto(\'bup\')">χρησιμοποιήσεις το bup αντί γι\' αυτό</a>',
"u_nowork": "χρειάζεται firefox 53+ ή chrome 57+ ή iOS 11+",
"tail_2old": "χρειάζεται firefox 105+ ή chrome 71+ ή iOS 14.5+",
"u_nodrop": "ο browser σου είναι πολύ παλιός για drag&amp;drop μεταφορτώσεις",
"u_notdir": "αυτός δεν είναι φάκελος!\n\nο browser σου είναι πολύ παλιός,\nδοκίμασε drag&amp;drop αντ' αυτού",
"u_uri": "για να κάνεις drag&amp;drop εικόνων από άλλα παράθυρα browser,\nρίξ' τες πάνω στο μεγάλο κουμπί μεταφόρτωσης",
"u_enpot": 'άλλαξε στο <a href="#">potato UI</a> (ίσως ανεβάζει πιο γρήγορα)',
"u_depot": 'άλλαξε στο <a href="#">fancy UI</a> (ίσως ανεβάζει πιο αργά)',
"u_gotpot": "αλλάζω στο potato UI για πιο γρήγορη μεταφόρτωση,\n\nμπορείς να το αλλάξεις πάλι αν θες!",
"u_pott": "<p>αρχεία: &nbsp; <b>{0}</b> ολοκληρωμένα, &nbsp; <b>{1}</b> αποτυχημένα, &nbsp; <b>{2}</b> σε εξέλιξη, &nbsp; <b>{3}</b> σε ουρά</p>",
"u_ever": "αυτός είναι ο βασικός uploader; το up2k θέλει τουλάχιστον<br>chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1",
"u_su2k": 'αυτός είναι ο βασικός uploader; το <a href="#" id="u2yea">up2k</a> είναι καλύτερο',
"u_uput": "βελτιστοποίηση για ταχύτητα (παράλειψη ελέγχου ακεραιότητας)",
"u_ewrite": "δεν έχεις δικαίωμα εγγραφής σε αυτόν τον φάκελο",
"u_eread": "δεν έχεις δικαίωμα ανάγνωσης σε αυτόν τον φάκελο",
"u_enoi": "η αναζήτηση αρχείων δεν είναι ενεργοποιημένη στο αρχείο ρυθμίσεων του διακομιστή",
"u_enoow": "δεν μπορείς να κάνεις αντικατάσταση εδώ· χρειάζεται δικαίωμα Διαγραφής",
"u_badf": "Αυτά τα {0} αρχεία (από {1} συνολικά) παραλείφθηκαν, πιθανώς λόγω δικαιωμάτων συστήματος αρχείων:\n\n",
"u_blankf": "Αυτά τα {0} αρχεία (από {1} συνολικά) είναι άδεια / κενά· να τα μεταφορτώσω έτσι κι αλλιώς;\n\n",
"u_applef": "Αυτά τα {0} αρχεία (από {1} συνολικά) πιθανώς δεν είναι επιθυμητά;\nΠάτα <code>OK/Enter</code> για ΝΑ ΑΓΝΟΗΘΟΥΝ τα παρακάτω αρχεία,\nΠάτα <code>Cancel/ESC</code> για ΝΑ ΜΗΝ ΑΠΟΚΛΕΙΣΤΟΥΝ και να ΜΕΤΑΦΟΡΤΩΘΟΎΝ κι αυτά:\n\n",
"u_just1": "\nΊσως δουλέψει καλύτερα αν επιλέξεις μόνο ένα αρχείο",
"u_ff_many": "αν χρησιμοποιείς <b>Linux / MacOS / Android,</b> τότε τόσα αρχεία <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>μπορεί</em> να κατάρρευση του Firefox!</a>\nαν γίνει αυτό, δοκίμασε ξανά (ή χρησιμοποίησε τον Chrome).",
"u_up_life": "Αυτή η μεταφόρτωση θα διαγραφεί από το διακομιστή\n{0} μετά την ολοκλήρωσή της",
"u_asku": "μεταφόρτωση αυτών των {0} αρχείων στο <code>{1}</code>",
"u_unpt": "μπορείς να αναιρέσεις / διαγράψεις αυτήν τη μεταφόρτωση χρησιμοποιώντας το 🧯, πάνω αριστερά",
"u_bigtab": "θα εμφανιστούν {0} αρχεία\n\nαυτό μπορεί να κάνει τον browser σου να κολλήσει, είσαι σίγουρος;",
"u_scan": "Σάρωση αρχείων...",
"u_dirstuck": "ο επεξεργαστής φακέλων κόλλησε προσπαθώντας να προσπελάσει τα εξής {0} αντικείμενα· θα τα παραλείψει:",
"u_etadone": "Ολοκληρώθηκε ({0}, {1} αρχεία)",
"u_etaprep": "(προετοιμασία για μεταφόρτωση)",
"u_hashdone": "το hashing ολοκληρώθηκε",
"u_hashing": "υπολογισμός hash",
"u_hs": "handshaking...",
"u_started": "τα αρχεία ανεβαίνουν τώρα· δες τα στο [🚀]",
"u_dupdefer": "διπλότυπο; θα επεξεργαστεί μετά από όλα τα άλλα αρχεία",
"u_actx": "πάτα αυτό το κείμενο για να μην χάσεις<br />απόδοση όταν αλλάζεις παράθυρα/καρτέλες",
"u_fixed": "ΟΚ!&nbsp; Το διόρθωσα 👍",
"u_cuerr": "αποτυχία μεταφόρτωσης τμήματοςς {0} από {1};\nπιθανώς ακίνδυνο, συνεχίζω\n\nαρχείο: {2}",
"u_cuerr2": "ο διακομιστής απέρριψε τη μεταφόρτωση (τμήμα {0} από {1});\nθα ξαναδοκιμάσει αργότερα\n\nαρχείο: {2}\n\nσφάλμα ",
"u_ehstmp": "θα ξαναδοκιμάσει; δες κάτω δεξιά",
"u_ehsfin": "ο διακομιστής απέρριψε το αίτημα ολοκλήρωσης της μεταφόρτωσης; ξαναδοκιμάζει...",
"u_ehssrch": "ο διακομιστής απέρριψε το αίτημα αναζήτησης; ξαναδοκιμάζει...",
"u_ehsinit": "ο διακομιστής απέρριψε το αίτημα για εκκίνηση μεταφόρτωσης; ξαναδοκιμάζει...",
"u_eneths": "σφάλμα δικτύου κατά το handshake μεταφόρτωσης; ξαναδοκιμάζει...",
"u_enethd": "σφάλμα δικτύου κατά τον έλεγχο ύπαρξης στόχου; ξαναδοκιμάζει...",
"u_cbusy": "ο διακομιστής περιμένει να μας εμπιστευτεί ξανά μετά από πρόβλημα δικτύου...",
"u_ehsdf": "ο διακομιστής έμεινε από χώρο στο δίσκο!\n\nθα συνεχίσει να ξαναδοκιμάζει,\nσε περίπτωση που κάποιος\nελευθερώσει αρκετό χώρο για συνέχεια",
"u_emtleak1": "φαίνεται πως ο browser σου έχει διαρροή μνήμης;\nπαρακαλώ",
"u_emtleak2": ' <a href="{0}">αλλαγή σε https (συνιστάται)</a> ή ',
"u_emtleak3": ' ',
"u_emtleakc": 'δοκίμασε τα εξής:\n<ul><li>πάτα <code>F5</code> για ανανέωση σελίδας</li><li>μετά απενεργοποίησε το &nbsp;<code>mt</code>&nbsp; κουμπί στις &nbsp;<code>⚙️ ρυθμίσεις</code></li><li>και δοκίμασε ξανά τη μεταφόρτωση</li></ul>Οι μεταφορτώσιες θα είναι λίγο πιο αργές, αλλά ok.\nΣυγγνώμη για την ταλαιπωρία!\n\nPS: το chrome v107 <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">έχει διόρθωση γι\' αυτό</a>',
"u_emtleakf": 'δοκίμασε τα εξής:\n<ul><li>πάτα <code>F5</code> για ανανέωση σελίδας</li><li>μετά άνοιξε το <code>🥔</code> (potato) στο UI μεταφόρτωσης<li>και δοκίμασε ξανά τη μεταφόρτωση</li></ul>\nPS: ο firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">ελπίζει να φτιάξει αυτό το bug</a> κάποια στιγμή',
"u_s404": "δεν βρέθηκε στο διακομιστή",
"u_expl": "επεξήγηση",
"u_maxconn": "οι περισσότεροι browser το περιορίζουν στα 6, αλλά ο firefox σου επιτρέπει να το αυξήσεις με <code>connections-per-server</code> στο <code>about:config</code>",
"u_tu": '<p class="warn">ΠΡΟΕΙΔΟΠΟΙΗΣΗ: το turbo είναι ενεργοποιημένο, <span>&nbsp;το πρόγραμμα πελάτη ίσως να μην ανιχνεύσει και να μην ξαναεκκινήσει μισοτελειωμένες μεταφορτώσεις; δες τα tooltip του κουμπιού turbo</span></p>',
"u_ts": '<p class="warn">ΠΡΟΕΙΔΟΠΟΙΗΣΗ: το turbo είναι ενεργοποιημένο, <span>&nbsp;τα αποτελέσματα αναζήτησης μπορεί να είναι λάθος; δες τα tooltip του κουμπιού turbo</span></p>',
"u_turbo_c": "το turbo είναι απενεργοποιημένο στο αρχείο ρυθμίσεων του διακομιστή",
"u_turbo_g": "απενεργοποιώ το turbo επειδή δεν έχεις δικαίωμα\nγια τη λίστα φακέλων σε αυτόν τον τόμο",
"u_life_cfg": 'αυτόματη διαγραφή μετά από <input id="lifem" p="60" /> λεπτά (ή <input id="lifeh" p="3600" /> ώρες)',
"u_life_est": 'η μεταφόρτωση θα διαγραφεί <span id="lifew" tt="τοπική ώρα">---</span>',
"u_life_max": 'αυτός ο φάκελος επιβάλλει\nμέγιστη διάρκεια ζωής {0}',
"u_unp_ok": "επιτρέπεται το unpost για {0}",
"u_unp_ng": "δεν επιτρέπεται το unpost",
"ue_ro": "έχεις μόνο δικαίωμα ανάγνωσης σε αυτόν το φάκελο\n\n",
"ue_nl": "δεν είσαι συνδεδεμένος τώρα",
"ue_la": 'είσαι συνδεδεμένος ως "{0}"',
"ue_sr": "είσαι σε λειτουργία αναζήτησης αρχείων\n\nπήγαινε σε λειτουργία μεταφόρτωσης πατώντας το 🔎 (δίπλα στο μεγάλο κουμπί ΑΝΑΖΗΤΗΣΗΣ) και δοκίμασε πάλι\n\nσυγγνώμη",
"ue_ta": "δοκίμασε να μεταφορτώσεις εκ νέου, θα πρέπει να δουλέψει τώρα",
"ue_ab": "αυτό το αρχείο ανεβαίνει σε άλλο φάκελο και η μεταφόρτωση πρέπει να ολοκληρωθεί πριν ανέβει αλλού.\n\nΜπορείς να ακυρώσεις και να ξεχάσεις την αρχική μεταφόρτωση με το κουμπί 🧯 πάνω αριστερά",
"ur_1uo": "ΟΚ: Το αρχείο ανέβηκε επιτυχώς",
"ur_auo": "ΟΚ: Και τα {0} αρχεία ανέβηκαν επιτυχώς",
"ur_1so": "ΟΚ: Το αρχείο βρέθηκε στο διακομιστή",
"ur_aso": "ΟΚ: Και τα {0} αρχεία βρέθηκαν στο διακομιστή",
"ur_1un": "Η μεταφόρτωση απέτυχε, συγγνώμη",
"ur_aun": "Και οι {0} μεταφορτώσεις απέτυχαν, συγγνώμη",
"ur_1sn": "Το αρχείο ΔΕΝ βρέθηκε στο διακομιστή",
"ur_asn": "Τα {0} αρχεία ΔΕΝ βρέθηκαν στο διακομιστή",
"ur_um": "Ολοκληρώθηκε;\n{0} μεταφορτώσεις είναι OK,\n{1} μεταφορτώσεις απέτυχαν, συγγνώμη",
"ur_sm": "Ολοκληρώθηκε;\n{0} αρχεία βρέθηκαν στο διακομιστή,\n{1} αρχεία ΔΕΝ βρέθηκαν στο διακομιστή",
"lang_set": "ανανέωση σελίδας για εφαρμογή της αλλαγής;"
},
"ita": {
"tt": "Italiano",
@ -6931,7 +7560,7 @@ var Ls = {
},
};
var LANGS = ["eng", "nor", "chi", "cze", "deu", "fin", "ita", "nld", "rus", "spa", "ukr"];
var LANGS = ["eng", "nor", "chi", "cze", "deu", "fin", "grc", "ita", "nld", "rus", "spa", "ukr"];
if (window.langmod)
langmod();
@ -7155,13 +7784,12 @@ ebi('op_cfg').innerHTML = (
'</div>\n' +
'<div>\n' +
' <h3>' + L.cl_themes + '</h3>\n' +
' <div id="themes">\n' +
' <div><select id="themes"></select></div>\n' +
' </div>\n' +
'</div>\n' +
'<div>\n' +
' <h3>' + L.cl_langs + '</h3>\n' +
' <div id="langs">\n' +
' </div>\n' +
' <div><select id="langs"></select></div>\n' +
'</div>\n' +
(have_zip ? (
'<div><h3>' + L.cl_ziptype + '</h3><div id="arc_fmt"></div></div>\n'
@ -7208,7 +7836,7 @@ ebi('op_cfg').innerHTML = (
' </td>\n' +
' </div>\n' +
'</div>\n' +
'<div><h3>' + L.cl_keytype + '</h3><div id="key_notation"></div></div>\n' +
'<div><h3>' + L.cl_keytype + '</h3><div><select id="key_notation"></select></div></div>\n' +
'<div><h3>' + L.cl_hiddenc + ' &nbsp;' + (MOBILE ? '<a href="#" id="hcolsh">' + L.cl_hidec + '</a> / ' : '') + '<a href="#" id="hcolsr">' + L.cl_reset + '</a></h3><div id="hcols"></div></div>'
);
@ -7375,6 +8003,10 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
dk, mp;
if (location.pathname.indexOf('//') === 0)
hist_replace(location.pathname.replace(/^\/+/, '/'));
if (window.og_fn) {
hash0 = 1;
hist_replace(vsplit(get_evpath())[0]);
@ -12590,7 +13222,7 @@ function ev_load_m3u(e) {
function () { load_m3u(url); },
function () {
if (has(perms, 'write') && has(perms, 'delete'))
window.location = url + '?edit';
location = url + '?edit';
else
showfile.show(url);
}
@ -13189,7 +13821,7 @@ var treectl = (function () {
r.reqls = function (url, hpush, back, hydrate) {
if (IE && !history.pushState)
return window.location = url;
return location = url;
var xhr = new XHR(),
m = /[?&](k=[^&#]+)/.exec(url),
@ -13205,6 +13837,7 @@ var treectl = (function () {
xhr.hydrate = hydrate;
xhr.ts = Date.now();
xhr.open('GET', xhr.top + '?ls' + uq, true);
xhr.setRequestHeader('Fnugg', '' + xhr.ts);
xhr.onload = xhr.onerror = recvls;
xhr.send();
@ -13270,6 +13903,9 @@ var treectl = (function () {
if (r.chk_index_html(this.top, res))
return;
if (this.ts != res.fnugg && res.fnugg != 'nei' && sread('no_fnugg') !== '1')
toast.warn(60, "WARNING: A proxy/CDN between your webbrowser and the server is misbehaving, and caching responses it shouldn't. As a result, you are now seeing stale directory listings. There will be many issues.\n\nIf you need to ignore this and stop these messages, you can set the global-option 'no-fnugg' on the server, or click <code>π</code> and run this: <code>STG.no_fnugg=1</code>");
for (var a = 0; a < res.files.length; a++)
if (res.files[a].tags === undefined)
res.files[a].tags = {};
@ -14109,25 +14745,21 @@ var mukey = (function () {
defnot = 'rekobo_alnum';
var map = {},
html = [];
html = [],
cb = ebi('key_notation');
for (var k in maps) {
if (!maps.hasOwnProperty(k))
continue;
html.push(
'<span><input type="radio" name="keytype" value="' + k + '" id="key_' + k + '">' +
'<label for="key_' + k + '">' + k + '</label></span>');
html.push('<option value="{0}">{0}</option>'.format(k));
for (var a = 0; a < 24; a++)
maps[k][a] = maps[k][a].trim();
}
ebi('key_notation').innerHTML = html.join('\n');
cb.innerHTML = html.join('');
function set_key_notation(e) {
ev(e);
var notation = this.getAttribute('value');
load_notation(notation);
function set_key_notation() {
load_notation(cb.value);
try_render();
}
@ -14184,14 +14816,10 @@ var mukey = (function () {
if (!maps[notation])
notation = defnot;
ebi('key_' + notation).checked = true;
cb.value = notation;
cb.onchange = set_key_notation;
load_notation(notation);
var o = QSA('#key_notation input');
for (var a = 0; a < o.length; a++) {
o[a].onchange = set_key_notation;
}
return {
"render": try_render
};
@ -14230,17 +14858,17 @@ var settheme = (function () {
showfile.setstyle();
bchrome();
var html = [], itheme = ax.indexOf(theme[0]) * 2 + (light ? 1 : 0),
var html = [],
cb = ebi('themes'),
itheme = ax.indexOf(theme[0]) * 2 + (light ? 1 : 0),
names = ['classic dark', 'classic light', 'pm-monokai', 'flat light', 'vice', 'hotdog stand', 'hacker', 'hi-con'];
for (var a = 0; a < themes; a++)
html.push('<a href="#" class="btn tgl' + (a == itheme ? ' on' : '') +
'" tt="' + (names[a] || 'custom') + '">' + a + '</a>');
html.push('<option value="{0}">{0} ┃ {1}</option>'.format(a, names[a] || 'custom'));
ebi('themes').innerHTML = html.join('');
var btns = QSA('#themes a');
for (var a = 0; a < themes; a++)
btns[a].onclick = r.go;
cb.value = itheme;
cb.onchange = r.onsel;
if (chldr) {
var x = r.ldr[itheme] || [tre];
@ -14249,12 +14877,13 @@ var settheme = (function () {
}
bcfg_set('light', light);
tt.att(ebi('themes'));
}
r.go = function (e) {
var i = e;
try { ev(e); i = e.target.textContent; } catch (ex) { }
r.onsel = function () {
r.go(parseInt(ebi('themes').value));
};
r.go = function (i) {
light = i % 2 == 1;
var c = ax[Math.floor(i / 2)],
l = light ? 'y' : 'z';
@ -14262,7 +14891,7 @@ var settheme = (function () {
themen = c + l;
swrite('cpp_thm', theme);
freshen();
}
};
freshen();
return r;
@ -14272,26 +14901,25 @@ var settheme = (function () {
(function () {
function freshen() {
lang = sread("cpp_lang", LANGS) || lang;
var k, html = [];
var k, cb = ebi('langs'), html = [];
for (var a = 0; a < LANGS.length; a++) {
k = LANGS[a];
html.push('<a href="#" class="btn tgl' + (k == lang ? ' on' : '') +
'" tt="' + Ls[k].tt + '">' + k + '</a>');
html.push('<option value="{0}">{0} ┃ {1}</option>'.format(k, Ls[k].tt));
}
ebi('langs').innerHTML = html.join('');
var btns = QSA('#langs a');
for (var a = 0, aa = btns.length; a < aa; a++)
btns[a].onclick = setlang;
cb.innerHTML = html.join('');
cb.onchange = setlang;
cb.value = lang;
}
function setlang(e) {
ev(e);
var t = L.lang_set;
L = Ls[this.textContent];
swrite("cpp_lang", this.textContent);
lang = ebi('langs').value;
L = Ls[lang];
swrite("cpp_lang", lang);
freshen();
modal.confirm(L.lang_set + "\n\n" + t, location.reload.bind(location), null);
};
}
freshen();
})();

View file

@ -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';

View file

@ -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';

View file

@ -19,14 +19,7 @@
<a href="{{ r }}/?h">control-panel</a>
&nbsp; Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
&nbsp; <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>

View file

@ -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();

View file

@ -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) {

View file

@ -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 %}

View file

@ -215,9 +215,50 @@ 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": "γεια σου ξένε! &nbsp; <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 δεν βρέθηκε &nbsp;┐( ´ -`)┌",
"o1": '´η μήπως δεν έχεις πρόσβαση -- δοκίμασε έναν κωδικό <a href="' + SR + '/?h">πήγαινε στην αρχική</a>',
"p1": "403 απαγορευμένο &nbsp;~┻━┻",
"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 &nbsp; <small>(non sei connesso)</small>",
@ -259,7 +300,7 @@ var Ls = {
"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? &nbsp; <small>(Je bent niet ingelogd)</small>",
@ -416,7 +457,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();
@ -426,6 +467,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;

View file

@ -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>

View file

@ -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);
};

View file

@ -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;

View file

@ -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 {
@ -741,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;
}
@ -893,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;
@ -1249,10 +1249,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;
}
}

2138
docs/chungus.conf Normal file

File diff suppressed because it is too large Load diff

View file

@ -74,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 "$@"; }
@ -157,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 |
@ -395,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
@ -554,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"
@ -583,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

View file

@ -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

View file

@ -143,7 +143,7 @@ class Cfg(Namespace):
def __init__(self, a=None, v=None, c=None, **ka0):
ka = {}
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_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_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"
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"