Compare commits

...

44 commits

Author SHA1 Message Date
ed fb9f0441c9 fix possible deadlock on shutdown;
if a ?tar=w is hammering the thumbnailer queue on shutdown, give up
issuing sentinels (the workers will see !!stopping and abort anyways)
2025-11-23 21:45:03 +09:00
ed a359b89edd support thumbnail pregen for all output formats 2025-11-23 21:43:55 +09:00
Habetdin 77f74ddb2f
fix ongoing-xfer links in controlpanel (#977)
when viewing the controlpanel from a path other than the webroot,
the links to ongoing transfers were invalid
2025-11-19 16:52:31 +01:00
ed f7e7b03f6f reduce redirect delay 2025-11-20 00:48:34 +09:00
ed 7a291403ca contextual login caption; closes #1012 2025-11-20 00:46:19 +09:00
ed b427d7802a metrics: allow list of users 2025-11-20 00:37:36 +09:00
ed c424a55d6f more hints for invalid config 2025-11-20 00:26:44 +09:00
ed 7d62335c13 repurpose new-md to new-textfile 2025-11-20 00:24:28 +09:00
ed 9385daeae8 fix pypi packaging; closes #1003 2025-11-19 23:49:23 +09:00
ed 79e1078671 panic if unknown users in ipu; closes #959 2025-11-19 23:28:39 +09:00
ed cad15fbf60 warn against changing -j 2025-11-19 23:26:19 +09:00
Robert Ismo 9f08efcabd
inverse unix philosophy (#994)
Signed-off-by: Robert Ismo <robertismo@protonmail.com>
Signed-off-by: ed <s@ocv.me>
Co-authored-by: ed <s@ocv.me>
2025-11-19 15:24:52 +01:00
ed ac085b8149 mtag: fix geotag usage 2025-11-02 14:56:28 +00:00
ed 1c15c0d5d1 mtag: add geotag.py 2025-11-02 13:12:13 +00:00
Nicolas Mémeint 904c984bda
nixos: Only create and bind volumes without variables (#962) 2025-11-02 12:59:48 +00:00
Nicolas Mémeint 3242145e52
nixos: Use volume/global chmod-d for module directory creation (#963) 2025-11-02 12:59:41 +00:00
ed dff6aa2435 update pkgs to 1.19.20 2025-11-02 01:13:32 +00:00
ed 450cd86dc1 v1.19.20 2025-11-02 01:09:40 +00:00
ed db60951d9f apply per-vol (md|lg)_sb during nav 2025-11-02 00:58:13 +00:00
Carson c00314a292
apply per-volume sb_lg during navigation (#967)
Signed-off-by: Carson <57198646+carson-coder@users.noreply.github.com>
2025-11-02 00:29:12 +00:00
ed 2cc53ea151 list files in /?shares; closes #961 2025-10-31 23:09:14 +00:00
ed e9ab040ce8 docker: fix crossbuild from aarch64 2025-10-26 23:30:57 +01:00
ed e005930cd0 readme: pyvips on windows 2025-10-26 23:23:17 +01:00
ed 4fcd2c4193 update pkgs to 1.19.19 2025-10-25 19:21:56 +00:00
ed cec44aa1dd v1.19.19 2025-10-25 19:18:51 +00:00
ed e3524d85bd fix for archlinux pkgbuild 2025-10-25 19:02:06 +00:00
ed 1963ed1795 update pkgs to 1.19.18 2025-10-25 14:36:57 +00:00
ed f0297777eb v1.19.18 2025-10-25 14:34:01 +00:00
ed 36ab77e0bf fix og-specific issue with single-file volumes;
if a textfile was shared with permission r below/inside a more
restrictive volume, then:

* if the parent volume had either permission g or h, then the files in
  the parent folder could be accessed

* if the parent volume had anything stricter than g or h, then filenames
  in the parent volume could be seen, but not accessed
2025-10-25 14:23:55 +00:00
ed db87ea5ce1 send msg when uploads finished; closes #949 2025-10-25 14:00:29 +00:00
ed b1efc0065c disable e2d for single-file volumes;
avoids some warnings in the log
2025-10-25 13:06:04 +00:00
ed a325353b1b option to default-disable tooltips; closes #937 2025-10-25 00:39:37 +00:00
ed 1c17b63b76 fix filesize color on select 2025-10-25 00:15:36 +00:00
ed d3dd34569a u2c: fix delete-url (closes #948);
u2c would produce an URL which triggered a windows-only
sanchk serverside, rejecting the delete-request
2025-10-24 21:48:59 +00:00
ed cdd5e78adf fix unlistc* when parent is jumpvol 2025-10-23 21:54:32 +00:00
ed fff7291dcf show h vols in ls and tree,
and compensate with some optimizations
2025-10-23 21:44:28 +00:00
ed 7f5810f1a7 ENTERPRISE packaging (closes #941);
introduce copyparty-en.pyz, affectionately known as companyparty.pyz

like copyparty-en.py, it is english-only but with a twist;
also remove smb support because the impacket references can look sus
2025-10-22 22:20:57 +00:00
ed b624a38747 ENTERPRISE file extensions (#941 omake);
in case a snakeoil salesman managed to convince your workplace into
purchasing their "internet security solution" which blocks downloads
of certain files according to file extensions -- or, in other words,
smoke and mirrors, with a comforting false sense of security
2025-10-22 22:10:02 +00:00
ed c9e45c12d8 fix permission h with og 2025-10-22 21:00:34 +00:00
/dev/urandom 5c42ad1c78
tl/esperanto: fix capitalization (#936)
Signed-off-by: /dev/urandom <53902042+slashdevslashurandom@users.noreply.github.com>
2025-10-22 19:44:05 +00:00
ed 547a7ab1cc add download mirror https://copyparty.eu/ 2025-10-19 18:19:45 +00:00
ed a04570ff5e packaging: fix $PWD assumption 2025-10-19 17:53:43 +00:00
ed 93eb862c60 docker: replace confusing config example 2025-10-19 17:52:03 +00:00
ed 81881a449b update pkgs to 1.19.17 2025-10-17 23:20:30 +00:00
62 changed files with 555 additions and 165 deletions

View file

@ -21,6 +21,7 @@ made in Norway 🇳🇴
* top * top
* [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉 * [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
* [mirrors](#mirrors) - other places to download copyparty from
* [at home](#at-home) - make it accessible over the internet * [at home](#at-home) - make it accessible over the internet
* [on servers](#on-servers) - you may also want these, especially on servers * [on servers](#on-servers) - you may also want these, especially on servers
* [features](#features) - also see [comparison to similar software](./docs/versus.md) * [features](#features) - also see [comparison to similar software](./docs/versus.md)
@ -157,7 +158,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty` * or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
* or install [on arch](#arch-package) [on NixOS](#nixos-module) [through nix](#nix-package) * or install [on arch](#arch-package) / [homebrew](#homebrew-formulae) [on NixOS](#nixos-module) [through nix](#nix-package)
* or if you are on android, [install copyparty in termux](#install-on-android) * or if you are on android, [install copyparty in termux](#install-on-android)
* or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS) * or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS)
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md) * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
@ -195,6 +196,18 @@ some recommended options:
* see [accounts and volumes](#accounts-and-volumes) (or `--help-accounts`) for the syntax and other permissions * see [accounts and volumes](#accounts-and-volumes) (or `--help-accounts`) for the syntax and other permissions
### mirrors
other places to download copyparty from (non-github links):
* https://copyparty.eu/ (hetzner, finland, official mirror):
* https://copyparty.eu/py = https://copyparty.eu/copyparty-sfx.py = the sfx
* https://copyparty.eu/en = https://copyparty.eu/copyparty-en.py = the english-only sfx
* https://copyparty.eu/pyz = https://copyparty.eu/copyparty.pyz = the zipapp
* https://copyparty.eu/enz = https://copyparty.eu/copyparty-en.pyz = the enterprise pyz
* https://copyparty.eu/cli = online cli helptext
### at home ### at home
make it accessible over the internet by starting a [cloudflare quicktunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) like so: make it accessible over the internet by starting a [cloudflare quicktunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) like so:
@ -309,7 +322,7 @@ small collection of user feedback
project goals / philosophy project goals / philosophy
* inverse linux philosophy -- do all the things, and do an *okay* job * inverse unix philosophy -- do all the things, and do an *okay* job
* quick drop-in service to get a lot of features in a pinch * quick drop-in service to get a lot of features in a pinch
* some of [the alternatives](./docs/versus.md) might be a better fit for you * some of [the alternatives](./docs/versus.md) might be a better fit for you
* run anywhere, support everything * run anywhere, support everything
@ -621,7 +634,7 @@ the main tabs in the ui
* `[🧯]` [unpost](#unpost): undo/delete accidental uploads * `[🧯]` [unpost](#unpost): undo/delete accidental uploads
* `[🚀]` and `[🎈]` are the [uploaders](#uploading) * `[🚀]` and `[🎈]` are the [uploaders](#uploading)
* `[📂]` mkdir: create directories * `[📂]` mkdir: create directories
* `[📝]` new-md: create a new markdown document * `[📝]` new-file: create a new textfile
* `[📟]` send-msg: either to server-log or into textfiles if `--urlform save` * `[📟]` send-msg: either to server-log or into textfiles if `--urlform save`
* `[🎺]` audio-player config options * `[🎺]` audio-player config options
* `[⚙️]` general client config options * `[⚙️]` general client config options
@ -2969,7 +2982,9 @@ enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0` enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips` `pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram
* to install `pyvips` on Linux: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
* to install `pyvips` on windows: `pip install --user -U "pyvips[binary]"`
to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%` to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
@ -3066,7 +3081,8 @@ another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere
* but note that it currently still needs to extract the web-resources somewhere (they'll land in the default TEMP-folder of your OS)
> there is also [copyparty-en.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.pyz), english-only and without smb support (enterprise-friendly)
# install on android # install on android

View file

@ -68,3 +68,8 @@ instead of affecting all volumes, you can set the options for just one volume li
* `:c,mtp=key=f,audio-key.py` * `:c,mtp=key=f,audio-key.py`
* `:c,mtp=.bpm=f,audio-bpm.py` * `:c,mtp=.bpm=f,audio-bpm.py`
* `:c,mtp=ahash,vhash=f,media-hash.py` * `:c,mtp=ahash,vhash=f,media-hash.py`
# tips & tricks
* to delete tags for all files below `blog*` and rescan that, `sqlite3 .hist/up2k.db "delete from mt where w in (select substr(w,1,16) from up where rd like 'blog%')";`

53
bin/mtag/geotag.py Executable file
View file

@ -0,0 +1,53 @@
import json
import re
import sys
from copyparty.util import fsenc, runcmd
"""
uses exiftool to geotag images based on embedded gps coordinates in exif data
adds four new metadata keys:
.gps_lat = latitute
.gps_lon = longitude
.masl = meters above sea level
city = "city, subregion, region"
usage: -mtp .masl,.gps_lat,.gps_lon,city=ad,t10,bin/mtag/geotag.py
example: https://a.ocv.me/pub/blog/j7/8/?grid=0
"""
def main():
cmd = b"exiftool -api geolocation -n".split(b" ")
rc, so, se = runcmd(cmd + [fsenc(sys.argv[1])])
ptn = re.compile("([^:]*[^ :]) *: (.*)")
city = ["", "", ""]
ret = {}
for ln in so.split("\n"):
m = ptn.match(ln)
if not m:
continue
k, v = m.groups()
if k == "Geolocation City":
city[2] = v
elif k == "Geolocation Subregion":
city[1] = v
elif k == "Geolocation Region":
city[0] = v
elif k == "GPS Latitude":
ret[".gps_lat"] = "%.04f" % (float(v),)
elif k == "GPS Longitude":
ret[".gps_lon"] = "%.04f" % (float(v),)
elif k == "GPS Altitude":
ret[".masl"] = str(int(float(v)))
v = ", ".join(city).strip(", ")
if v:
ret["city"] = v
print(json.dumps(ret))
if __name__ == "__main__":
main()

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
S_VERSION = "2.13" S_VERSION = "2.15"
S_BUILD_DT = "2025-09-05" S_BUILD_DT = "2025-10-25"
""" """
u2c.py: upload to copyparty u2c.py: upload to copyparty
@ -232,6 +232,7 @@ class HCli(object):
MJ = "application/json" MJ = "application/json"
MO = "application/octet-stream" MO = "application/octet-stream"
MM = "application/x-www-form-urlencoded"
CLEN = "Content-Length" CLEN = "Content-Length"
web = None # type: HCli web = None # type: HCli
@ -979,6 +980,7 @@ class Ctl(object):
self.nfiles, self.nbytes = self.stats self.nfiles, self.nbytes = self.stats
self.filegen = walkdirs([], ar.files, ar.x) self.filegen = walkdirs([], ar.files, ar.x)
self.recheck = [] # type: list[File] self.recheck = [] # type: list[File]
self.last_file = None
if ar.safe: if ar.safe:
self._safe() self._safe()
@ -1015,6 +1017,11 @@ class Ctl(object):
self._fancy() self._fancy()
file = self.last_file
if self.up_br and file:
zs = quotep(file.name.encode("utf-8", WTF8))
web.req("POST", file.url, {}, b"msg=upload-queue-empty;" + zs, MM)
self.ok = not self.errs self.ok = not self.errs
def _safe(self): def _safe(self):
@ -1225,9 +1232,7 @@ class Ctl(object):
while req: while req:
print("DELETING ~%s#%s" % (srd, len(req))) print("DELETING ~%s#%s" % (srd, len(req)))
body = json.dumps(req).encode("utf-8") body = json.dumps(req).encode("utf-8")
sc, txt = web.req( sc, txt = web.req("POST", "/?delete", {}, body, MJ)
"POST", self.ar.url + "?delete", {}, body, MJ
)
if sc == 413 and "json 2big" in txt: if sc == 413 and "json 2big" in txt:
print(" (delete request too big; slicing...)") print(" (delete request too big; slicing...)")
req = req[: len(req) // 2] req = req[: len(req) // 2]
@ -1455,6 +1460,7 @@ class Ctl(object):
file = fsl.file file = fsl.file
cids = fsl.cids cids = fsl.cids
self.last_file = file
with self.mutex: with self.mutex:
if not self.uploader_busy: if not self.uploader_busy:

View file

@ -48,6 +48,8 @@ let
accountsWithPlaceholders = mapAttrs (name: attrs: passwordPlaceholder name); accountsWithPlaceholders = mapAttrs (name: attrs: passwordPlaceholder name);
volumesWithoutVariables = filterAttrs (k: v: !(hasInfix "\${" v.path)) cfg.volumes;
configStr = '' configStr = ''
${mkSection "global" cfg.settings} ${mkSection "global" cfg.settings}
${cfg.globalExtraConfig} ${cfg.globalExtraConfig}
@ -325,7 +327,7 @@ in
BindPaths = BindPaths =
(if cfg.settings ? hist then [ cfg.settings.hist ] else [ ]) (if cfg.settings ? hist then [ cfg.settings.hist ] else [ ])
++ [ externalStateDir ] ++ [ externalStateDir ]
++ (mapAttrsToList (k: v: v.path) cfg.volumes); ++ (mapAttrsToList (k: v: v.path) volumesWithoutVariables);
# ProtectSystem = "strict"; # ProtectSystem = "strict";
# Note that unlike what 'ro' implies, # Note that unlike what 'ro' implies,
# this actually makes it impossible to read anything in the root FS, # this actually makes it impossible to read anything in the root FS,
@ -364,10 +366,20 @@ in
#: in front of things means it wont change it if the directory already exists. #: in front of things means it wont change it if the directory already exists.
group = ":${cfg.group}"; group = ":${cfg.group}";
user = ":${cfg.user}"; user = ":${cfg.user}";
mode = ":755"; mode = ":${
# Use volume permissions if set
if (value.flags ? chmod_d) then
value.flags.chmod_d
# Else, use global permission if set
else if (cfg.settings ? chmod-d) then
cfg.settings.chmod-d
# Else, use the default permission
else
"755"
}";
}; };
} }
) cfg.volumes ) volumesWithoutVariables
); );
users.groups = lib.mkIf (cfg.group == "copyparty") { users.groups = lib.mkIf (cfg.group == "copyparty") {

View file

@ -3,7 +3,7 @@
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead. # NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
pkgname=copyparty pkgname=copyparty
pkgver="1.19.16" pkgver="1.19.20"
pkgrel=1 pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++" pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any") arch=("any")
@ -23,7 +23,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
) )
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
backup=("etc/${pkgname}/copyparty.conf" ) backup=("etc/${pkgname}/copyparty.conf" )
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682") sha256sums=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
build() { build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -2,7 +2,7 @@
pkgname=copyparty pkgname=copyparty
pkgver=1.19.16 pkgver=1.19.20
pkgrel=1 pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++" pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any") arch=("any")
@ -20,7 +20,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
) )
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
backup=("/etc/${pkgname}.d/init" ) backup=("/etc/${pkgname}.d/init" )
sha256sums=("d8cc10d3623eaccc8acaebdd3b0102a1ebf878a03ffe6e737d132c66f799d682") sha256sums=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
build() { build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -1,5 +1,5 @@
{ {
"url": "https://github.com/9001/copyparty/releases/download/v1.19.16/copyparty-1.19.16.tar.gz", "url": "https://github.com/9001/copyparty/releases/download/v1.19.20/copyparty-1.19.20.tar.gz",
"version": "1.19.16", "version": "1.19.20",
"hash": "sha256-2MwQ02I+rMyKyuvdOwECoev4eKA//m5zfRMsZveZ1oI=" "hash": "sha256-BQzMNFVOWSEKynpn2HoYbmmz9NvgE9XuLxGiLCWagqY="
} }

View file

@ -55,7 +55,7 @@ except:
zs = """ zs = """
web/a/partyfuse.py web/a/partyfuse.py
web/a/u2c.py web/a/u2c.py
web/a/webdav-cfg.bat web/a/webdav-cfg.txt
web/baguettebox.js web/baguettebox.js
web/browser.css web/browser.css
web/browser.html web/browser.html
@ -125,6 +125,11 @@ web/util.js
web/w.hash.js web/w.hash.js
""" """
RES = set(zs.strip().split("\n")) RES = set(zs.strip().split("\n"))
RESM = {
"web/a/partyfuse.txt": "web/a/partyfuse.py",
"web/a/u2c.txt": "web/a/u2c.py",
"web/a/webdav-cfg.bat": "web/a/webdav-cfg.txt",
}
class EnvParams(object): class EnvParams(object):

View file

@ -1160,7 +1160,6 @@ def add_general(ap, nc, srvname):
ap2 = ap.add_argument_group("general options") 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("-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("-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="\033[34mREPEATABLE:\033[0m add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\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("-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("--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]")
@ -1175,6 +1174,7 @@ def add_general(ap, nc, srvname):
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("--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("--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("--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("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
ap2.add_argument("--license", action="store_true", help="show licenses and exit") ap2.add_argument("--license", action="store_true", help="show licenses and exit")
ap2.add_argument("--version", action="store_true", help="show versions and exit") ap2.add_argument("--version", action="store_true", help="show versions and exit")
ap2.add_argument("--versionb", action="store_true", help="show version and exit") ap2.add_argument("--versionb", action="store_true", help="show version and exit")
@ -1491,6 +1491,7 @@ def add_hooks(ap):
def add_stats(ap): 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("--stats", action="store_true", help="enable openmetrics at /.cpr/metrics for admin accounts")
ap2.add_argument("--stats-u", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to access /.cpr/metrics even if they aren't admin")
ap2.add_argument("--nos-hdd", action="store_true", help="disable disk-space metrics (used/free space)") 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)") ap2.add_argument("--nos-vol", action="store_true", help="disable volume size metrics (num files, total bytes, vmaxb/vmaxn)")
ap2.add_argument("--nos-vst", action="store_true", help="disable volume state metrics (indexing, analyzing, activity)") ap2.add_argument("--nos-vst", action="store_true", help="disable volume state metrics (indexing, analyzing, activity)")
@ -1796,6 +1797,7 @@ def add_ui(ap, retry: int):
ap2.add_argument("--ufavico", metavar="TXT", type=u, default="", help="URL to .ico/png/gif/svg file; \033[33m--favico\033[0m takes precedence unless disabled (volflag=ufavico)") ap2.add_argument("--ufavico", metavar="TXT", type=u, default="", help="URL to .ico/png/gif/svg file; \033[33m--favico\033[0m takes precedence unless disabled (volflag=ufavico)")
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("--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("--mpmc", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--notooltips", action="store_true", help="tooltips disabled as default")
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("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html") ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html") ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")

View file

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

View file

@ -398,6 +398,9 @@ class VFS(object):
self.vpath = vpath # absolute path in the virtual filesystem self.vpath = vpath # absolute path in the virtual filesystem
self.vpath0 = vpath0 # original vpath (before idp expansion) self.vpath0 = vpath0 # original vpath (before idp expansion)
self.axs = axs self.axs = axs
self.uaxs: dict[
str, tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool]
] = {}
self.flags = flags # config options self.flags = flags # config options
self.root = self self.root = self
self.dev = 0 # st_dev self.dev = 0 # st_dev
@ -555,29 +558,19 @@ class VFS(object):
def can_access( def can_access(
self, vpath: str, uname: str self, vpath: str, uname: str
) -> tuple[bool, bool, bool, bool, bool, bool, bool, bool]: ) -> tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool]:
"""can Read,Write,Move,Delete,Get,Upget,Admin,Dot""" """can Read,Write,Move,Delete,Get,Upget,Html,Admin,Dot"""
# NOTE: only used by get_perms, which is only used by hooks; the lowest of fruits
if vpath: if vpath:
vn, _ = self._find(undot(vpath)) vn, _ = self._find(undot(vpath))
else: else:
vn = self vn = self
c = vn.axs return vn.uaxs[uname]
return (
uname in c.uread,
uname in c.uwrite,
uname in c.umove,
uname in c.udel,
uname in c.uget,
uname in c.upget,
uname in c.uadmin,
uname in c.udot,
)
# skip uhtml because it's rarely needed
def get_perms(self, vpath: str, uname: str) -> str: def get_perms(self, vpath: str, uname: str) -> str:
zbl = self.can_access(vpath, uname) zbl = self.can_access(vpath, uname)
ret = "".join(ch for ch, ok in zip("rwmdgGa.", zbl) if ok) ret = "".join(ch for ch, ok in zip("rwmdgGha.", zbl) if ok)
if "rwmd" in ret and "a." in ret: if "rwmd" in ret and "a." in ret:
ret += "A" ret += "A"
return ret return ret
@ -772,19 +765,16 @@ class VFS(object):
virt_vis[name] = vn2 virt_vis[name] = vn2
continue continue
ok = False u_has = vn2.uaxs.get(uname) or [False] * 9
zx = vn2.axs
axs = [zx.uread, zx.uwrite, zx.umove, zx.udel, zx.uget]
for pset in permsets: for pset in permsets:
ok = True ok = True
for req, lst in zip(pset, axs): for req, zb in zip(pset, u_has):
if req and uname not in lst: if req and not zb:
ok = False ok = False
if ok:
break break
if ok: if ok:
virt_vis[name] = vn2 virt_vis[name] = vn2
break
if ".hist" in abspath: if ".hist" in abspath:
p = abspath.replace("\\", "/") if WINDOWS else abspath p = abspath.replace("\\", "/") if WINDOWS else abspath
@ -1822,6 +1812,15 @@ class AuthSrv(object):
derive_args(self.args) derive_args(self.args)
self.setup_auth_ord() self.setup_auth_ord()
if self.args.ipu:
# syntax (CIDR=UNAME) is verified in load_ipu
zsl = [x.split("=", 1)[1] for x in self.args.ipu]
zsl = [x for x in zsl if x not in acct]
if zsl:
t = "ERROR: unknown users in ipu: %s" % (zsl,)
self.log(t, 1)
raise Exception(t)
self.setup_pwhash(acct) self.setup_pwhash(acct)
defpw = acct.copy() defpw = acct.copy()
self.setup_chpw(acct) self.setup_chpw(acct)
@ -1995,6 +1994,23 @@ class AuthSrv(object):
umap[usr].sort() umap[usr].sort()
setattr(vfs, "a" + perm, umap) setattr(vfs, "a" + perm, umap)
for vol in vfs.all_nodes.values():
za = vol.axs
vol.uaxs = {
un: (
un in za.uread,
un in za.uwrite,
un in za.umove,
un in za.udel,
un in za.uget,
un in za.upget,
un in za.uhtml,
un in za.uadmin,
un in za.udot,
)
for un in unames
}
all_users = {} all_users = {}
missing_users = {} missing_users = {}
associated_users = {} associated_users = {}
@ -2328,6 +2344,10 @@ class AuthSrv(object):
free_umask = False free_umask = False
have_reflink = False have_reflink = False
for vol in vfs.all_nodes.values(): for vol in vfs.all_nodes.values():
if os.path.isfile(vol.realpath):
vol.flags["is_file"] = True
vol.flags["d2d"] = True
if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa: if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa:
vol.flags["e2ds"] = True vol.flags["e2ds"] = True
@ -2642,7 +2662,14 @@ class AuthSrv(object):
errors = True errors = True
for vol in vfs.all_nodes.values(): for vol in vfs.all_nodes.values():
if not vol.realpath or os.path.isfile(vol.realpath): if not vol.flags.get("is_file"):
continue
zs = "og opds xlink"
for zs in zs.split():
vol.flags.pop(zs, None)
for vol in vfs.all_nodes.values():
if not vol.realpath or vol.flags.get("is_file"):
continue continue
ccs = vol.flags["casechk"][:1].lower() ccs = vol.flags["casechk"][:1].lower()
if ccs in ("y", "n"): if ccs in ("y", "n"):
@ -3033,6 +3060,7 @@ class AuthSrv(object):
"lifetime": vf.get("lifetime") or 0, "lifetime": vf.get("lifetime") or 0,
"unlist": vf.get("unlist") or "", "unlist": vf.get("unlist") or "",
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"), "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
} }
if "ufavico_h" in vf: if "ufavico_h" in vf:
vn.js_ls["ufavico"] = vf["ufavico_h"] vn.js_ls["ufavico"] = vf["ufavico_h"]
@ -3054,7 +3082,8 @@ class AuthSrv(object):
"have_emp": int(self.args.emp), "have_emp": int(self.args.emp),
"md_no_br": int(vf.get("md_no_br") or 0), "md_no_br": int(vf.get("md_no_br") or 0),
"ext_th": vf.get("ext_th_d") or {}, "ext_th": vf.get("ext_th_d") or {},
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"), "sb_lg": vn.js_ls["sb_lg"],
"sb_md": vn.js_ls["sb_md"],
"sba_md": vf.get("md_sba") or "", "sba_md": vf.get("md_sba") or "",
"sba_lg": vf.get("lg_sba") or "", "sba_lg": vf.get("lg_sba") or "",
"txt_ext": self.args.textfiles.replace(",", " "), "txt_ext": self.args.textfiles.replace(",", " "),
@ -3088,6 +3117,10 @@ class AuthSrv(object):
for zs in zs.split(): for zs in zs.split():
if vf.get(zs): if vf.get(zs):
js_htm[zs] = 1 js_htm[zs] = 1
zs = "notooltips"
for zs in zs.split():
if getattr(self.args, zs, False):
js_htm[zs] = 1
vn.js_htm = json_hesc(json.dumps(js_htm)) vn.js_htm = json_hesc(json.dumps(js_htm))
vols = list(vfs.all_nodes.values()) vols = list(vfs.all_nodes.values())
@ -3436,7 +3469,7 @@ class AuthSrv(object):
raise Exception("volume not found: " + zs) raise Exception("volume not found: " + zs)
self.log(str({"users": users, "vols": vols, "flags": flags})) self.log(str({"users": users, "vols": vols, "flags": flags}))
t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) uadmin({})" t = "/{}: read({}) write({}) move({}) del({}) dots({}) get({}) upGet({}) html({}) uadmin({})"
for k, zv in self.vfs.all_vols.items(): for k, zv in self.vfs.all_vols.items():
vc = zv.axs vc = zv.axs
vs = [ vs = [

View file

@ -198,7 +198,7 @@ class FtpFs(AbstractedFS):
if not avfs: if not avfs:
raise FSE(t.format(vpath), 1) raise FSE(t.format(vpath), 1)
cr, cw, cm, cd, _, _, _, _ = avfs.can_access("", self.h.uname) cr, cw, cm, cd, _, _, _, _, _ = avfs.uaxs[self.h.uname]
if r and not cr or w and not cw or m and not cm or d and not cd: if r and not cr or w and not cw or m and not cm or d and not cd:
raise FSE(t.format(vpath), 1) raise FSE(t.format(vpath), 1)
@ -250,6 +250,7 @@ class FtpFs(AbstractedFS):
td = 0 td = 0
if w and need_unlink: if w and need_unlink:
assert td # type: ignore # !rm
if td >= -1 and td <= self.args.ftp_wt: if td >= -1 and td <= self.args.ftp_wt:
# within permitted timeframe; allow overwrite or resume # within permitted timeframe; allow overwrite or resume
do_it = True do_it = True

View file

@ -30,7 +30,7 @@ try:
except: except:
pass pass
from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode from .__init__ import ANYWIN, RES, RESM, TYPE_CHECKING, EnvParams, unicode
from .__version__ import S_VERSION from .__version__ import S_VERSION
from .authsrv import LEELOO_DALLAS, VFS # typechk from .authsrv import LEELOO_DALLAS, VFS # typechk
from .bos import bos from .bos import bos
@ -124,7 +124,17 @@ from .util import (
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
import typing import typing
from typing import Any, Generator, Iterable, Match, Optional, Pattern, Type, Union from typing import (
Any,
Generator,
Iterable,
Match,
Optional,
Pattern,
Sequence,
Type,
Union,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from .httpconn import HttpConn from .httpconn import HttpConn
@ -165,6 +175,24 @@ RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
UPARAM_CC_OK = set("doc move tree".split()) UPARAM_CC_OK = set("doc move tree".split())
PERMS_rwh = [
[True, False],
[False, True],
[False, False, False, False, False, False, True],
]
def _build_zip_xcode() -> Sequence[str]:
ret = "opus mp3 flac wav p".split()
for codec in ("w", "j"):
for suf in ("", "f", "f3", "3"):
ret.append("%s%s" % (codec, suf))
return ret
ZIP_XCODE_L = _build_zip_xcode()
ZIP_XCODE_S = set(ZIP_XCODE_L)
class HttpCli(object): class HttpCli(object):
""" """
@ -229,6 +257,7 @@ class HttpCli(object):
self.can_delete = False self.can_delete = False
self.can_get = False self.can_get = False
self.can_upget = False self.can_upget = False
self.can_html = False
self.can_admin = False self.can_admin = False
self.can_dot = False self.can_dot = False
self.out_headerlist: list[tuple[str, str]] = [] self.out_headerlist: list[tuple[str, str]] = []
@ -737,6 +766,7 @@ class HttpCli(object):
if "bcasechk" in vn.flags and not vn.casechk(rem, True): if "bcasechk" in vn.flags and not vn.casechk(rem, True):
return self.tx_404() and False return self.tx_404() and False
try:
( (
self.can_read, self.can_read,
self.can_write, self.can_write,
@ -744,11 +774,13 @@ class HttpCli(object):
self.can_delete, self.can_delete,
self.can_get, self.can_get,
self.can_upget, self.can_upget,
self.can_html,
self.can_admin, self.can_admin,
self.can_dot, self.can_dot,
) = ( ) = avn.uaxs[self.uname]
avn.can_access("", self.uname) if avn else [False] * 8 except:
) pass # default is all-false
self.avn = avn self.avn = avn
self.vn = vn # note: do not dbv due to walk/zipgen self.vn = vn # note: do not dbv due to walk/zipgen
self.rem = rem self.rem = rem
@ -775,7 +807,7 @@ class HttpCli(object):
guess = "modifying" if (origin and host) else "stripping" guess = "modifying" if (origin and host) else "stripping"
t = "cors-reject %s because request-header Origin=%r does not match request-protocol %r and host %r based on request-header Host=%r (note: if this request is not malicious, check if your reverse-proxy is accidentally %s request headers, in particular 'Origin', for example by running copyparty with --ihead='*' to show all request headers)" t = "cors-reject %s because request-header Origin=%r does not match request-protocol %r and host %r based on request-header Host=%r (note: if this request is not malicious, check if your reverse-proxy is accidentally %s request headers, in particular 'Origin', for example by running copyparty with --ihead='*' to show all request headers)"
self.log(t % (self.mode, origin, proto, self.host, host, guess), 3) self.log(t % (self.mode, origin, proto, self.host, host, guess), 3)
raise Pebkac(403, "rejected by cors-check") raise Pebkac(403, "rejected by cors-check (see serverlog)")
# getattr(self.mode) is not yet faster than this # getattr(self.mode) is not yet faster than this
if self.mode == "POST": if self.mode == "POST":
@ -921,7 +953,7 @@ class HttpCli(object):
return False return False
self.log("banned for {:.0f} sec".format(rt), 6) self.log("banned for {:.0f} sec".format(rt), 6)
self.terse_reply(b"thank you for playing", 403) self.terse_reply(b"thank you for playing (see serverlog and readme)", 403)
return True return True
def permit_caching(self) -> None: def permit_caching(self) -> None:
@ -1275,6 +1307,20 @@ class HttpCli(object):
else: else:
return self.tx_res(res_path) return self.tx_res(res_path)
if res_path in RESM:
ap = self.E.mod_ + RESM[res_path]
if (
"txt" not in self.uparam
and "mime" not in self.uparam
and not self.ouparam.get("dl")
):
# return mimetype matching request extension
self.ouparam["dl"] = res_path.split("/")[-1]
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
return self.tx_file(ap)
else:
return self.tx_res(res_path)
self.tx_404() self.tx_404()
return False return False
@ -3284,9 +3330,9 @@ class HttpCli(object):
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True) vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
self._assert_safe_rem(rem) self._assert_safe_rem(rem)
ext = "" if "." not in new_file else new_file.split(".")[-1] if not self.can_delete and not new_file.lower().endswith(".md"):
if not ext or len(ext) > 5 or not self.can_delete: t = "you can only create .md files because you don't have the delete-permission"
new_file += ".md" raise Pebkac(400, t)
sanitized = sanitize_fn(new_file, "") sanitized = sanitize_fn(new_file, "")
fdir = vfs.canonical(rem) fdir = vfs.canonical(rem)
@ -3325,7 +3371,6 @@ class HttpCli(object):
raise Pebkac(500, "that file exists already") raise Pebkac(500, "that file exists already")
with open(fsenc(fn), "wb") as f: with open(fsenc(fn), "wb") as f:
f.write(b"`GRUNNUR`\n")
if "fperms" in vfs.flags: if "fperms" in vfs.flags:
set_fperms(f, vfs.flags) set_fperms(f, vfs.flags)
@ -4179,8 +4224,11 @@ class HttpCli(object):
# force download # force download
if "dl" in self.ouparam: if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path)) cdis = self.ouparam["dl"] or req_path
self.out_headers["Content-Disposition"] = cdis zs = gen_content_disposition(os.path.basename(cdis))
self.out_headers["Content-Disposition"] = zs
else:
cdis = req_path
# #
# if-modified # if-modified
@ -4246,7 +4294,7 @@ class HttpCli(object):
elif "mime" in self.uparam: elif "mime" in self.uparam:
mime = str(self.uparam.get("mime")) mime = str(self.uparam.get("mime"))
else: else:
mime = guess_mime(req_path) mime = guess_mime(cdis)
logmsg += unicode(status) + logtail logmsg += unicode(status) + logtail
@ -4354,8 +4402,11 @@ class HttpCli(object):
# force download # force download
if "dl" in self.ouparam: if "dl" in self.ouparam:
cdis = gen_content_disposition(os.path.basename(req_path)) cdis = self.ouparam["dl"] or req_path
self.out_headers["Content-Disposition"] = cdis zs = gen_content_disposition(os.path.basename(cdis))
self.out_headers["Content-Disposition"] = zs
else:
cdis = req_path
# #
# if-modified # if-modified
@ -4483,7 +4534,7 @@ class HttpCli(object):
elif "rmagic" in self.vn.flags: elif "rmagic" in self.vn.flags:
mime = guess_mime(req_path, fs_path) mime = guess_mime(req_path, fs_path)
else: else:
mime = guess_mime(req_path) mime = guess_mime(cdis)
if "nohtml" in self.vn.flags and "html" in mime: if "nohtml" in self.vn.flags and "html" in mime:
mime = "text/plain; charset=utf-8" mime = "text/plain; charset=utf-8"
@ -4913,8 +4964,11 @@ class HttpCli(object):
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]})) # for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
cfmt = "" cfmt = ""
if self.thumbcli and not self.args.no_bacode: if self.thumbcli and not self.args.no_bacode:
for zs in ("opus", "mp3", "flac", "wav", "w", "j", "p"): if uarg in ZIP_XCODE_S:
if zs in self.ouparam or uarg == zs: cfmt = uarg
else:
for zs in ZIP_XCODE_L:
if zs in self.ouparam:
cfmt = zs cfmt = zs
if cfmt: if cfmt:
@ -5261,7 +5315,7 @@ class HttpCli(object):
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn)) dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
if self.args.have_unlistc: if self.args.have_unlistc:
allvols = self.asrv.vfs.all_vols allvols = self.asrv.vfs.all_nodes
rvol = [x for x in rvol if "unlistcr" not in allvols[x[1:-1]].flags] rvol = [x for x in rvol if "unlistcr" not in allvols[x[1:-1]].flags]
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags] wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
@ -5540,7 +5594,7 @@ class HttpCli(object):
rem, rem,
self.uname, self.uname,
not self.args.no_scandir, not self.args.no_scandir,
[[True, False], [False, True]], PERMS_rwh,
) )
dots = self.uname in vn.axs.udot dots = self.uname in vn.axs.udot
dk_sz = vn.flags.get("dk") dk_sz = vn.flags.get("dk")
@ -5572,7 +5626,13 @@ class HttpCli(object):
for x in vfs_virt: for x in vfs_virt:
if x != excl: if x != excl:
try: try:
dvn, drem = vfs.get(vjoin(top, x), self.uname, True, False) dvn, drem = vfs.get(vjoin(top, x), self.uname, False, False)
if (
self.uname not in dvn.axs.uread
and self.uname not in dvn.axs.uwrite
and self.uname not in dvn.axs.uhtml
):
raise Exception()
bos.stat(dvn.canonical(drem, False)) bos.stat(dvn.canonical(drem, False))
except: except:
x += "\n" x += "\n"
@ -5978,6 +6038,15 @@ class HttpCli(object):
if self.uname != self.args.shr_adm: if self.uname != self.args.shr_adm:
rows = [x for x in rows if x[5] == self.uname] rows = [x for x in rows if x[5] == self.uname]
q = "select vp from sf where k=? limit 99"
for r in rows:
if not r[4]:
r[4] = "---"
else:
zstl = cur.execute(q, (r[0],)).fetchall()
zsl = [html_escape(zst[0]) for zst in zstl]
r[4] = "<br />".join(zsl)
html = self.j2s( html = self.j2s(
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time()) "shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
) )
@ -6498,8 +6567,7 @@ class HttpCli(object):
return self.tx_svg("upload\nonly") return self.tx_svg("upload\nonly")
if not self.can_read and self.can_get and self.avn: if not self.can_read and self.can_get and self.avn:
axs = self.avn.axs if not self.can_html:
if self.uname not in axs.uhtml:
pass pass
elif is_dir: elif is_dir:
for fn in ("index.htm", "index.html"): for fn in ("index.htm", "index.html"):
@ -6520,6 +6588,7 @@ class HttpCli(object):
fk_pass = True fk_pass = True
is_dir = False is_dir = False
add_og = False
rem = vjoin(rem, fn) rem = vjoin(rem, fn)
vrem = vjoin(vrem, fn) vrem = vjoin(vrem, fn)
abspath = ap2 abspath = ap2
@ -6713,7 +6782,7 @@ class HttpCli(object):
rem, rem,
self.uname, self.uname,
not self.args.no_scandir, not self.args.no_scandir,
[[True, False], [False, True]], PERMS_rwh,
lstat="lt" in self.uparam, lstat="lt" in self.uparam,
throw=True, throw=True,
) )

View file

@ -383,8 +383,8 @@ class HttpSrv(object):
if nloris < nconn / 2: if nloris < nconn / 2:
continue continue
t = "slowloris (idle-conn): {} banned for {} min" t = "slow%s (idle-conn): %s banned for %d min" # slowloris
self.log(self.name, t.format(ip, self.args.loris, nclose), 1) self.log(self.name, t % ("loris", ip, self.args.loris), 1)
self.bans[ip] = int(time.time() + self.args.loris * 60) self.bans[ip] = int(time.time() + self.args.loris * 60)
if self.args.log_conn: if self.args.log_conn:

View file

@ -17,10 +17,10 @@ class Metrics(object):
self.hsrv = hsrv self.hsrv = hsrv
def tx(self, cli: "HttpCli") -> bool: def tx(self, cli: "HttpCli") -> bool:
if not cli.avol: args = cli.args
if not cli.avol and cli.uname.lower() not in args.stats_u_set:
raise Pebkac(403, "'stats' not allowed for user " + cli.uname) raise Pebkac(403, "'stats' not allowed for user " + cli.uname)
args = cli.args
if not args.stats: if not args.stats:
raise Pebkac(403, "the stats feature is not enabled in server config") raise Pebkac(403, "the stats feature is not enabled in server config")

View file

@ -1081,7 +1081,7 @@ class SvcHub(object):
vs = os.path.expandvars(os.path.expanduser(vs)) vs = os.path.expandvars(os.path.expanduser(vs))
setattr(al, k, vs) setattr(al, k, vs)
for k in "idp_adm".split(" "): for k in "idp_adm stats_u".split(" "):
vs = getattr(al, k) vs = getattr(al, k)
vsa = [x.strip() for x in vs.split(",")] vsa = [x.strip() for x in vs.split(",")]
vsa = [x.lower() for x in vsa if x] vsa = [x.lower() for x in vsa if x]

View file

@ -270,6 +270,9 @@ class ThumbSrv(object):
def shutdown(self) -> None: def shutdown(self) -> None:
self.stopping = True self.stopping = True
Daemon(self._fire_sentinels, "thumbstopper")
def _fire_sentinels(self):
for _ in range(self.nthr): for _ in range(self.nthr):
self.q.put(None) self.q.put(None)

View file

@ -2,11 +2,14 @@
# which should help on really slow connections # which should help on really slow connections
# but then why are you using copyparty in the first place # but then why are you using copyparty in the first place
pk: $(addsuffix .gz, $(wildcard tl/*.js *.js *.css)) pk: $(addsuffix .gz, $(wildcard tl/*.js *.js *.css) \
un: $(addsuffix .un, $(wildcard tl/*.gz *.gz)) a/webdav-cfg.txt )
un: $(addsuffix .un, $(wildcard tl/*.gz *.gz a/*.gz))
%.gz: % %.gz: %
pigz -11 -J 34 -I 573 $< pigz -c11 -J 34 -I 573 <$< >$@
touch -r $< $@
rm $<
%.un: % %.un: %
pigz -d $< pigz -d $<

View file

@ -1036,6 +1036,7 @@ html.dz #flogout {
box-shadow: 0 .1em 1.2em var(--g-play-sh); box-shadow: 0 .1em 1.2em var(--g-play-sh);
} }
#files tbody tr.sel td, #files tbody tr.sel td,
#files tbody tr.sel span,
#ggrid>a.sel, #ggrid>a.sel,
#ggrid>a[tt].sel { #ggrid>a[tt].sel {
color: var(--g-sel-fg); color: var(--g-sel-fg);

View file

@ -51,8 +51,9 @@
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}"> <form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
<input type="hidden" name="act" value="new_md" /> <input type="hidden" name="act" value="new_md" />
📝<input type="text" name="name" class="i" placeholder="weekend-plans"> 📝<input type="text" name="name" class="i" placeholder="weekend-plans">
<input type="submit" value="new markdown doc"> <input type="submit" value="new file">
</form> </form>
<span id="new_mdi"></p>
</div> </div>
<div id="op_msg" class="opview opbox {% if not ls0 %}act{% endif %}"> <div id="op_msg" class="opview opbox {% if not ls0 %}act{% endif %}">
@ -137,7 +138,6 @@
lang = "{{ lang }}", lang = "{{ lang }}",
dfavico = "{{ favico }}", dfavico = "{{ favico }}",
have_tags_idx = {{ have_tags_idx }}, have_tags_idx = {{ have_tags_idx }},
sb_lg = "{{ sb_lg }}",
logues = {{ logues|tojson if sb_lg else "[]" }}, logues = {{ logues|tojson if sb_lg else "[]" }},
ls0 = {{ ls0|tojson }}; ls0 = {{ ls0|tojson }};

View file

@ -119,7 +119,7 @@ if (1)
"ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones", "ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones",
"ot_bup": "bup: basic uploader, even supports netscape 4.0", "ot_bup": "bup: basic uploader, even supports netscape 4.0",
"ot_mkdir": "mkdir: create a new directory", "ot_mkdir": "mkdir: create a new directory",
"ot_md": "new-md: create a new markdown document", "ot_md": "new-file: create a new textfile",
"ot_msg": "msg: send a message to the server log", "ot_msg": "msg: send a message to the server log",
"ot_mp": "media player options", "ot_mp": "media player options",
"ot_cfg": "configuration options", "ot_cfg": "configuration options",
@ -128,7 +128,7 @@ if (1)
"ot_noie": 'Please use Chrome / Firefox / Edge', "ot_noie": 'Please use Chrome / Firefox / Edge',
"ab_mkdir": "make directory", "ab_mkdir": "make directory",
"ab_mkdoc": "new markdown doc", "ab_mkdoc": "new textfile",
"ab_msg": "send msg to srv log", "ab_msg": "send msg to srv log",
"ay_path": "skip to folders", "ay_path": "skip to folders",
@ -440,6 +440,8 @@ if (1)
"fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>', "fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "type a name into the text field on the left before you do that :p", "mk_noname": "type a name into the text field on the left before you do that :p",
"nmd_i1": "also add the file extension you want, for example <code>.md</code>",
"nmd_i2": "you can only create <code>.md</code> files because you don't have the delete-permission",
"tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)", "tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)",
"tv_xe1": "could not load textfile:\n\nerror ", "tv_xe1": "could not load textfile:\n\nerror ",
@ -7148,6 +7150,8 @@ var treectl = (function () {
if (res.files[a].tags === undefined) if (res.files[a].tags === undefined)
res.files[a].tags = {}; res.files[a].tags = {};
sb_lg = res.sb_lg;
sb_md = res.sb_md;
dnsort = res.dnsort; dnsort = res.dnsort;
read_dsort(res.dsort); read_dsort(res.dsort);
dcrop = res.dcrop; dcrop = res.dcrop;
@ -7741,6 +7745,8 @@ function apply_perms(res) {
if (up2k) if (up2k)
up2k.set_fsearch(); up2k.set_fsearch();
ebi('new_mdi').innerHTML = has(perms, "delete") ? L.nmd_i1 : L.nmd_i2;
widget.setvis(); widget.setvis();
thegrid.setvis(); thegrid.setvis();
if (!have_read && have_write) if (!have_read && have_write)
@ -8797,7 +8803,7 @@ function show_md(md, name, div, url, depth) {
var els = QSA('#epi a'); var els = QSA('#epi a');
for (var a = 0, aa = els.length; a < aa; a++) { for (var a = 0, aa = els.length; a < aa; a++) {
var href = els[a].getAttribute('href'); var href = els[a].getAttribute('href');
if (!href.startsWith('#') || href.startsWith('#md-')) if (!href || !href.startsWith('#') || href.startsWith('#md-'))
continue; continue;
els[a].setAttribute('href', '#md-' + href.slice(1)); els[a].setAttribute('href', '#md-' + href.slice(1));

View file

@ -43,7 +43,7 @@
<script> <script>
setTimeout(function() { setTimeout(function() {
location.replace("{{ redir }}"); location.replace("{{ redir }}");
}, 1000); }, 600);
</script> </script>
{%- endif %} {%- endif %}
{%- if js %} {%- if js %}

View file

@ -58,6 +58,7 @@ th {
#wrap th { #wrap th {
padding: .3em .6em; padding: .3em .6em;
text-align: left; text-align: left;
vertical-align: top;
white-space: nowrap; white-space: nowrap;
} }
#wrap td+td+td+td+td+td+td+td { #wrap td+td+td+td+td+td+td+td {
@ -71,7 +72,10 @@ th {
#wrap td:last-child { #wrap td:last-child {
border-radius: 0 .5em .5em 0; border-radius: 0 .5em .5em 0;
} }
#wrap.terse td div {
height: 2.3em;
overflow-y: hidden;
}
html.z { html.z {

View file

@ -14,8 +14,9 @@
</head> </head>
<body> <body>
<div id="wrap"> <div id="wrap" class="terse">
<a href="{{ r }}/?shares">refresh</a> <a href="{{ r }}/?shares">refresh</a>
<a id="xpnd" href="#">files</a>
<a href="{{ r }}/?h">control-panel</a> <a href="{{ r }}/?h">control-panel</a>
<span>axs = perms (read,write,move,delet)</span> <span>axs = perms (read,write,move,delet)</span>
@ -46,7 +47,7 @@
<td>{{ "yes" if pw else "--" }}</td> <td>{{ "yes" if pw else "--" }}</td>
<td><a href="{{ r }}/{{ vp|e }}">/{{ vp|e }}</a></td> <td><a href="{{ r }}/{{ vp|e }}">/{{ vp|e }}</a></td>
<td>{{ pr }}</td> <td>{{ pr }}</td>
<td>{{ st }}</td> <td><div>{{ st }}</div></td>
<td>{{ un|e }}</td> <td>{{ un|e }}</td>
<td>{{ t0 }}</td> <td>{{ t0 }}</td>
<td>{{ t1 }}</td> <td>{{ t1 }}</td>

View file

@ -28,6 +28,11 @@ function cb() {
location = '?shares'; location = '?shares';
} }
ebi('xpnd').onclick = function (e) {
ev(e);
clmod(ebi('wrap'), 'terse', 't');
};
function qr(e) { function qr(e) {
ev(e); ev(e);
var href = this.href, var href = this.href,

View file

@ -42,7 +42,7 @@
<thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead> <thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead>
<tbody> <tbody>
{%- for u in ups %} {%- for u in ups %}
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr> <tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ r }}/{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr>
{%- endfor %} {%- endfor %}
</tbody> </tbody>
</table> </table>
@ -54,7 +54,7 @@
<thead><tr><th>%</th><th>sent</th><th>speed</th><th>eta</th><th>idle</th><th></th><th>dir</th><th>file</th></tr></thead> <thead><tr><th>%</th><th>sent</th><th>speed</th><th>eta</th><th>idle</th><th></th><th>dir</th><th>file</th></tr></thead>
<tbody> <tbody>
{%- for u in dls %} {%- for u in dls %}
<tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr> <tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ r }}/{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr>
{%- endfor %} {%- endfor %}
</tbody> </tbody>
</table> </table>
@ -120,7 +120,11 @@
</form> </form>
</div> </div>
{%- else %} {%- else %}
{%- if this.uname == '*' %}
<h1 id="l">login for more:</h1> <h1 id="l">login for more:</h1>
{%- else %}
<h1 id="l">change account:</h1>
{%- endif %}
<div> <div>
{%- if this.args.idp_login %} {%- if this.args.idp_login %}
<ul><li> <ul><li>

View file

@ -64,7 +64,7 @@
<li>old version of rclone? replace all <code>=</code> with <code>&nbsp;</code> (space)</li> <li>old version of rclone? replace all <code>=</code> with <code>&nbsp;</code> (space)</li>
</ul> </ul>
<p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p> <p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.txt?dl=webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p>
<pre> <pre>
{%- if un %} {%- if un %}
net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} <b>{{ pw }}</b> /user:{{ b_un }}{% endif %} net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} <b>{{ pw }}</b> /user:{{ b_un }}{% endif %}

View file

@ -116,7 +116,7 @@ Ls.chi = {
"ot_unpost": "取消发布:删除最近上传的内容,或中止未完成的内容", "ot_unpost": "取消发布:删除最近上传的内容,或中止未完成的内容",
"ot_bup": "bup基础上传器甚至支持 Netscape 4.0", "ot_bup": "bup基础上传器甚至支持 Netscape 4.0",
"ot_mkdir": "mkdir创建新目录", "ot_mkdir": "mkdir创建新目录",
"ot_md": "new-md创建新 Markdown 文档", "ot_md": "new-file创建新的文本文件", //m
"ot_msg": "msg向服务器日志发送消息", "ot_msg": "msg向服务器日志发送消息",
"ot_mp": "媒体播放器选项", "ot_mp": "媒体播放器选项",
"ot_cfg": "配置选项", "ot_cfg": "配置选项",
@ -125,7 +125,7 @@ Ls.chi = {
"ot_noie": '请使用 Chrome / Firefox / Edge', "ot_noie": '请使用 Chrome / Firefox / Edge',
"ab_mkdir": "创建目录", "ab_mkdir": "创建目录",
"ab_mkdoc": "新建 Markdown 文档", "ab_mkdoc": "新建文本文件", //m
"ab_msg": "发送消息到服务器日志", "ab_msg": "发送消息到服务器日志",
"ay_path": "跳转到文件夹", "ay_path": "跳转到文件夹",
@ -437,6 +437,8 @@ Ls.chi = {
"fcp_both_b": '<a href="#" id="modal-ok">复制</a><a href="#" id="modal-ng">上传</a>', //m "fcp_both_b": '<a href="#" id="modal-ok">复制</a><a href="#" id="modal-ng">上传</a>', //m
"mk_noname": "在左侧文本框中输入名称,然后再执行此操作 :p", "mk_noname": "在左侧文本框中输入名称,然后再执行此操作 :p",
"nmd_i1": "还可以添加需要的文件扩展名,例如 <code>.txt</code>", //m
"nmd_i2": "由于没有删除权限,你只能创建 <code>.md</code> 文件", //m
"tv_load": "加载文本文件:\n\n{0}\n\n{1}% ({2} 的 {3} MiB 已加载)", "tv_load": "加载文本文件:\n\n{0}\n\n{1}% ({2} 的 {3} MiB 已加载)",
"tv_xe1": "无法加载文本文件:\n\n错误 ", "tv_xe1": "无法加载文本文件:\n\n错误 ",

View file

@ -120,7 +120,7 @@ Ls.cze = {
"ot_unpost": "unpost: smazat vaše nedávné nahrání nebo zrušit nedokončené", "ot_unpost": "unpost: smazat vaše nedávné nahrání nebo zrušit nedokončené",
"ot_bup": "bup: základní nahrávač, podporuje i netscape 4.0", "ot_bup": "bup: základní nahrávač, podporuje i netscape 4.0",
"ot_mkdir": "mkdir: vytvořit nový adresář", "ot_mkdir": "mkdir: vytvořit nový adresář",
"ot_md": "new-md: vytvořit nový markdown dokument", "ot_md": "new-file: vytvořit nový textový soubor", //m
"ot_msg": "msg: poslat zprávu do logu serveru", "ot_msg": "msg: poslat zprávu do logu serveru",
"ot_mp": "možnosti přehrávače médií", "ot_mp": "možnosti přehrávače médií",
"ot_cfg": "možnosti konfigurace", "ot_cfg": "možnosti konfigurace",
@ -129,7 +129,7 @@ Ls.cze = {
"ot_noie": 'Prosím použijte Chrome / Firefox / Edge', "ot_noie": 'Prosím použijte Chrome / Firefox / Edge',
"ab_mkdir": "vytvořit adresář", "ab_mkdir": "vytvořit adresář",
"ab_mkdoc": "nový markdown dokument", "ab_mkdoc": "nový textový soubor", //m
"ab_msg": "poslat zprávu do logu serveru", "ab_msg": "poslat zprávu do logu serveru",
"ay_path": "přejít na složky", "ay_path": "přejít na složky",
@ -441,6 +441,8 @@ Ls.cze = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopírovat</a><a href="#" id="modal-ng">Nahrát</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopírovat</a><a href="#" id="modal-ng">Nahrát</a>',
"mk_noname": "napište název do textového pole vlevo předtím než to uděláte :p", "mk_noname": "napište název do textového pole vlevo předtím než to uděláte :p",
"nmd_i1": "můžeš také přidat příponu souboru, například <code>.txt</code>", //m
"nmd_i2": "můžeš vytvářet pouze <code>.md</code> soubory, protože nemáš oprávnění mazat", //m
"tv_load": "Načítání textového dokumentu:\n\n{0}\n\n{1}% ({2} z {3} MiB načteno)", "tv_load": "Načítání textového dokumentu:\n\n{0}\n\n{1}% ({2} z {3} MiB načteno)",
"tv_xe1": "nelze načíst textový soubor:\n\nchyba ", "tv_xe1": "nelze načíst textový soubor:\n\nchyba ",

View file

@ -116,7 +116,7 @@ Ls.deu = {
"ot_unpost": "unpost: lösche deine letzten Uploads oder breche unvollständige ab", "ot_unpost": "unpost: lösche deine letzten Uploads oder breche unvollständige ab",
"ot_bup": "bup: Basic Uploader, unterstützt sogar Neuheiten wie Netscape 4.0", "ot_bup": "bup: Basic Uploader, unterstützt sogar Neuheiten wie Netscape 4.0",
"ot_mkdir": "mkdir: Neuen Ordner erstellen", "ot_mkdir": "mkdir: Neuen Ordner erstellen",
"ot_md": "new-md: Neues Markdown-Dokument erstellen", "ot_md": "new-file: Neues Textdokument erstellen", //m
"ot_msg": "msg: Eine Nachricht an das Server-Log schicken", "ot_msg": "msg: Eine Nachricht an das Server-Log schicken",
"ot_mp": "Media Player-Optionen", "ot_mp": "Media Player-Optionen",
"ot_cfg": "Konfigurationsoptionen", "ot_cfg": "Konfigurationsoptionen",
@ -125,7 +125,7 @@ Ls.deu = {
"ot_noie": 'Bitte benutze Chrome / Firefox / Edge', "ot_noie": 'Bitte benutze Chrome / Firefox / Edge',
"ab_mkdir": "Ordner erstellen", "ab_mkdir": "Ordner erstellen",
"ab_mkdoc": "Markdown Doc erstellen", "ab_mkdoc": "Textdatei erstellen", //m
"ab_msg": "Nachricht an Server Log senden", "ab_msg": "Nachricht an Server Log senden",
"ay_path": "zu Ordnern springen", "ay_path": "zu Ordnern springen",
@ -437,6 +437,8 @@ Ls.deu = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopieren</a><a href="#" id="modal-ng">Hochladen</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopieren</a><a href="#" id="modal-ng">Hochladen</a>',
"mk_noname": "Tipp' mal vorher lieber einen Namen in das Textfeld links, bevor du das machst :p", "mk_noname": "Tipp' mal vorher lieber einen Namen in das Textfeld links, bevor du das machst :p",
"nmd_i1": "Fügen Sie auch die gewünschte Dateiendung hinzu, z. B. <code>.txt</code>", //m
"nmd_i2": "Sie können nur <code>.md</code>-Dateien erstellen, da Ihnen die Löschberechtigung fehlt", //m
"tv_load": "Textdatei wird geladen:\n\n{0}\n\n{1}% ({2} von {3} MiB geladen)", "tv_load": "Textdatei wird geladen:\n\n{0}\n\n{1}% ({2} von {3} MiB geladen)",
"tv_xe1": "Konnte Textdatei nicht laden:\n\nFehler ", "tv_xe1": "Konnte Textdatei nicht laden:\n\nFehler ",

View file

@ -1,5 +1,5 @@
// Linioj finiĝantaj per //m estas nekontrolitaj maŝinaj tradukoj // Linioj, finiĝantaj per "//m", estas nekontrolitaj maŝinaj tradukoj
Ls.epo = { Ls.epo = {
"tt": "Esperanto", "tt": "Esperanto",
@ -116,7 +116,7 @@ Ls.epo = {
"ot_unpost": "unpost: forigi viaj plej lastaj alŝutoj, aŭ ĉesigi nefinigitajn", "ot_unpost": "unpost: forigi viaj plej lastaj alŝutoj, aŭ ĉesigi nefinigitajn",
"ot_bup": "bup: fundamenta alŝutilo, funkias eĉ kun netscape 4.0", "ot_bup": "bup: fundamenta alŝutilo, funkias eĉ kun netscape 4.0",
"ot_mkdir": "mkdir: krei novan dosierujon", "ot_mkdir": "mkdir: krei novan dosierujon",
"ot_md": "new-md: krei novan markdown-dosieron", "ot_md": "new-file: krei novan tekstodosieron", //m
"ot_msg": "msg: sendi mesaĝon al servila protokolo", "ot_msg": "msg: sendi mesaĝon al servila protokolo",
"ot_mp": "agordoj de medialudilo", "ot_mp": "agordoj de medialudilo",
"ot_cfg": "aliaj agordoj", "ot_cfg": "aliaj agordoj",
@ -125,7 +125,7 @@ Ls.epo = {
"ot_noie": 'Bonvolu uzi retumilojn Chrome / Firefox / Edge', "ot_noie": 'Bonvolu uzi retumilojn Chrome / Firefox / Edge',
"ab_mkdir": "krei dosierujon", "ab_mkdir": "krei dosierujon",
"ab_mkdoc": "krei markdown-dosieron", "ab_mkdoc": "krei tekstodosieron", //m
"ab_msg": "sendi mesaĝon al protokolo", "ab_msg": "sendi mesaĝon al protokolo",
"ay_path": "iri al dosierujoj", "ay_path": "iri al dosierujoj",
@ -204,7 +204,7 @@ Ls.epo = {
"u_nav_b": '<a href="#" id="modal-ok">Dosierojn</a><a href="#" id="modal-ng">Unu dosierujo</a>', "u_nav_b": '<a href="#" id="modal-ok">Dosierojn</a><a href="#" id="modal-ng">Unu dosierujo</a>',
"cl_opts": "ŝaltiloj", "cl_opts": "ŝaltiloj",
"cl_hfsz": "Dosiergrando", //m "cl_hfsz": "dosiergrando",
"cl_themes": "etoso", "cl_themes": "etoso",
"cl_langs": "lingvo", "cl_langs": "lingvo",
"cl_ziptype": "elŝutado de dosieroj", "cl_ziptype": "elŝutado de dosieroj",
@ -437,6 +437,8 @@ Ls.epo = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopii</a><a href="#" id="modal-ng">Alŝuti</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopii</a><a href="#" id="modal-ng">Alŝuti</a>',
"mk_noname": "tajpu nomon en tekstokampo maldekstre antaŭ vi faras ĉi tion :p", "mk_noname": "tajpu nomon en tekstokampo maldekstre antaŭ vi faras ĉi tion :p",
"nmd_i1": "vi povas aldoni la deziratan sufikson, ekzemple <code>.txt</code>", //m
"nmd_i2": "vi povas krei nur <code>.md</code>-dosierojn ĉar vi ne havas forigan permeson", //m
"tv_load": "Ŝargado de teksto-dokumento:\n\n{0}\n\n{1}% ({2} da {3} MiB ŝargita)", "tv_load": "Ŝargado de teksto-dokumento:\n\n{0}\n\n{1}% ({2} da {3} MiB ŝargita)",
"tv_xe1": "ne povas ŝargi teksto-dosieron:\n\neraro ", "tv_xe1": "ne povas ŝargi teksto-dosieron:\n\neraro ",

View file

@ -116,7 +116,7 @@ Ls.fin = {
"ot_unpost": "unpost: poista viimeaikaiset tai keskeytä keskeneräiset lataukset", "ot_unpost": "unpost: poista viimeaikaiset tai keskeytä keskeneräiset lataukset",
"ot_bup": "bup: tiedostojen 'perus'lähetysohjelma, tukee jopa netscape 4.0", "ot_bup": "bup: tiedostojen 'perus'lähetysohjelma, tukee jopa netscape 4.0",
"ot_mkdir": "mkdir: luo uusi hakemisto", "ot_mkdir": "mkdir: luo uusi hakemisto",
"ot_md": "new-md: luo uusi markdown-dokumentti", "ot_md": "new-file: luo uusi tekstitiedosto", //m
"ot_msg": "msg: lähetä viesti palvelinlokiin", "ot_msg": "msg: lähetä viesti palvelinlokiin",
"ot_mp": "mediasoittimen asetukset", "ot_mp": "mediasoittimen asetukset",
"ot_cfg": "asetukset", "ot_cfg": "asetukset",
@ -125,7 +125,7 @@ Ls.fin = {
"ot_noie": 'Suosittelemme käyttämään uudempaa selainta.', "ot_noie": 'Suosittelemme käyttämään uudempaa selainta.',
"ab_mkdir": "luo hakemisto", "ab_mkdir": "luo hakemisto",
"ab_mkdoc": "luo markdown-tiedosto", "ab_mkdoc": "luo tekstitiedosto", //m
"ab_msg": "lähetä viesti palvelinlokiin", "ab_msg": "lähetä viesti palvelinlokiin",
"ay_path": "siirry hakemistoihin", "ay_path": "siirry hakemistoihin",
@ -437,6 +437,8 @@ Ls.fin = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopioi</a><a href="#" id="modal-ng">Lähetä</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopioi</a><a href="#" id="modal-ng">Lähetä</a>',
"mk_noname": "kirjoita nimi vasemmalla olevaan tekstikenttään ennen kuin teet tuon :p", "mk_noname": "kirjoita nimi vasemmalla olevaan tekstikenttään ennen kuin teet tuon :p",
"nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi <code>.txt</code>", //m
"nmd_i2": "voit luoda vain <code>.md</code>-tiedostoja, koska sinulla ei ole poistolupaa", //m
"tv_load": "Ladataan tekstidokumenttia:\n\n{0}\n\n{1}% ({2} / {3} Mt ladattu)", "tv_load": "Ladataan tekstidokumenttia:\n\n{0}\n\n{1}% ({2} / {3} Mt ladattu)",
"tv_xe1": "tekstitiedoston lataaminen epäonnistui:\n\nvirhe ", "tv_xe1": "tekstitiedoston lataaminen epäonnistui:\n\nvirhe ",

View file

@ -116,7 +116,7 @@ Ls.fra = {
"ot_unpost": "unpost: supprimer vos téléchargements récents, ou annuler ceux en cours", "ot_unpost": "unpost: supprimer vos téléchargements récents, ou annuler ceux en cours",
"ot_bup": "bup: téléverseur de base, prend même en charge netscape 4.0", "ot_bup": "bup: téléverseur de base, prend même en charge netscape 4.0",
"ot_mkdir": "mkdir: créer un nouveau répertoire", "ot_mkdir": "mkdir: créer un nouveau répertoire",
"ot_md": "new-md: créer un nouveau document markdown", "ot_md": "new-file: créer un nouveau fichier texte", //m
"ot_msg": "msg: envoyer un message au journal du serveur", "ot_msg": "msg: envoyer un message au journal du serveur",
"ot_mp": "options du lecteur multimedia", "ot_mp": "options du lecteur multimedia",
"ot_cfg": "options de configuration", "ot_cfg": "options de configuration",
@ -125,7 +125,7 @@ Ls.fra = {
"ot_noie": 'Utilisez Chrome / Firefox / Edge', "ot_noie": 'Utilisez Chrome / Firefox / Edge',
"ab_mkdir": "créer un nouveau répertoire", "ab_mkdir": "créer un nouveau répertoire",
"ab_mkdoc": "faire un nouveau document markdown", "ab_mkdoc": "nouveau fichier texte", //m
"ab_msg": "envoyer un message au journal du serveur", "ab_msg": "envoyer un message au journal du serveur",
"ay_path": "passer aux dossiers", "ay_path": "passer aux dossiers",
@ -437,6 +437,8 @@ Ls.fra = {
"fcp_both_b": '<a href="#" id="modal-ok">Copier</a><a href="#" id="modal-ng">Téléverser</a>', "fcp_both_b": '<a href="#" id="modal-ok">Copier</a><a href="#" id="modal-ng">Téléverser</a>',
"mk_noname": "entrez un nom dans le champ de texte à gauche avant de faire ça :p", "mk_noname": "entrez un nom dans le champ de texte à gauche avant de faire ça :p",
"nmd_i1": "ajoutez aussi lextension souhaitée, par exemple <code>.txt</code>", //m
"nmd_i2": "vous ne pouvez créer que des fichiers <code>.md</code> car vous navez pas la permission deffacer", //m
"tv_load": "Chargement du document texte:\n\n{0}\n\n{1}% ({2} de {3} MiB chargés)", "tv_load": "Chargement du document texte:\n\n{0}\n\n{1}% ({2} de {3} MiB chargés)",
"tv_xe1": "impossible de charger le fichier texte:\n\nerreur", "tv_xe1": "impossible de charger le fichier texte:\n\nerreur",

View file

@ -116,7 +116,7 @@ Ls.grc = {
"ot_unpost": "unpost: διαγραφή πρόσφατων μεταφορτώσεων ή ακύρωση ανολοκλήρωτων", "ot_unpost": "unpost: διαγραφή πρόσφατων μεταφορτώσεων ή ακύρωση ανολοκλήρωτων",
"ot_bup": "bup: βασικός uploader, υποστηρίζει μέχρι και netscape 4.0", "ot_bup": "bup: βασικός uploader, υποστηρίζει μέχρι και netscape 4.0",
"ot_mkdir": "mkdir: δημιουργία νέου φακέλου", "ot_mkdir": "mkdir: δημιουργία νέου φακέλου",
"ot_md": "new-md: δημιουργία νέου markdown εγγράφου", "ot_md": "new-file: δημιουργία νέου αρχείου κειμένου", //m
"ot_msg": "msg: αποστολή μηνύματος στο server log", "ot_msg": "msg: αποστολή μηνύματος στο server log",
"ot_mp": "επιλογές media player", "ot_mp": "επιλογές media player",
"ot_cfg": "επιλογές ρυθμίσεων", "ot_cfg": "επιλογές ρυθμίσεων",
@ -125,7 +125,7 @@ Ls.grc = {
"ot_noie": 'Χρησιμοποίησε Chrome / Firefox / Edge', "ot_noie": 'Χρησιμοποίησε Chrome / Firefox / Edge',
"ab_mkdir": "δημιουργία φακέλου", "ab_mkdir": "δημιουργία φακέλου",
"ab_mkdoc": "νέο markdown έγγραφο", "ab_mkdoc": "νέο αρχείο κειμένου", //m
"ab_msg": "στείλε μήνυμα στο server log", "ab_msg": "στείλε μήνυμα στο server log",
"ay_path": "πήγαινε σε φακέλους", "ay_path": "πήγαινε σε φακέλους",
@ -437,6 +437,8 @@ Ls.grc = {
"fcp_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", "mk_noname": "γράψε ένα όνομα στο πεδίο κειμένου αριστερά πριν το κάνεις :p",
"nmd_i1": "μπορείτε επίσης να προσθέσετε την κατάληξη που θέλετε, όπως <code>.txt</code>", //m
"nmd_i2": "μπορείτε να δημιουργήσετε μόνο αρχεία <code>.md</code> επειδή δεν έχετε δικαίωμα διαγραφής", //m
"tv_load": "Φόρτωση αρχείου κειμένου:\n\n{0}\n\n{1}% ({2} από {3} MiB φορτωμένα)", "tv_load": "Φόρτωση αρχείου κειμένου:\n\n{0}\n\n{1}% ({2} από {3} MiB φορτωμένα)",
"tv_xe1": "αδυναμία φόρτωσης αρχείου κειμένου:\n\nσφάλμα ", "tv_xe1": "αδυναμία φόρτωσης αρχείου κειμένου:\n\nσφάλμα ",

View file

@ -116,7 +116,7 @@ Ls.ita = {
"ot_unpost": "unpost: elimina i tuoi caricamenti recenti, o interrompi quelli non completati", "ot_unpost": "unpost: elimina i tuoi caricamenti recenti, o interrompi quelli non completati",
"ot_bup": "bup: uploader di base, supporta anche netscape 4.0", "ot_bup": "bup: uploader di base, supporta anche netscape 4.0",
"ot_mkdir": "mkdir: crea una nuova directory", "ot_mkdir": "mkdir: crea una nuova directory",
"ot_md": "new-md: crea un nuovo documento markdown", "ot_md": "new-file: crea un nuovo file di testo", //m
"ot_msg": "msg: invia un messaggio al log del server", "ot_msg": "msg: invia un messaggio al log del server",
"ot_mp": "opzioni lettore multimediale", "ot_mp": "opzioni lettore multimediale",
"ot_cfg": "opzioni di configurazione", "ot_cfg": "opzioni di configurazione",
@ -125,7 +125,7 @@ Ls.ita = {
"ot_noie": 'Perfavore usa Chrome / Firefox / Edge', "ot_noie": 'Perfavore usa Chrome / Firefox / Edge',
"ab_mkdir": "crea directory", "ab_mkdir": "crea directory",
"ab_mkdoc": "nuovo doc markdown", "ab_mkdoc": "nuovo file di testo", //m
"ab_msg": "invia msg al log srv", "ab_msg": "invia msg al log srv",
"ay_path": "salta alle cartelle", "ay_path": "salta alle cartelle",
@ -437,6 +437,8 @@ Ls.ita = {
"fcp_both_b": '<a href="#" id="modal-ok">Copia</a><a href="#" id="modal-ng">Carica</a>', "fcp_both_b": '<a href="#" id="modal-ok">Copia</a><a href="#" id="modal-ng">Carica</a>',
"mk_noname": "scrivi un nome nel campo di testo a sinistra prima di farlo :p", "mk_noname": "scrivi un nome nel campo di testo a sinistra prima di farlo :p",
"nmd_i1": "puoi anche aggiungere lestensione che vuoi, per esempio <code>.txt</code>", //m
"nmd_i2": "puoi creare solo file <code>.md</code> perché non hai il permesso di eliminare", //m
"tv_load": "Caricando documento di testo:\n\n{0}\n\n{1}% ({2} di {3} MiB caricati)", "tv_load": "Caricando documento di testo:\n\n{0}\n\n{1}% ({2} di {3} MiB caricati)",
"tv_xe1": "impossibile caricare file di testo:\n\nerrore ", "tv_xe1": "impossibile caricare file di testo:\n\nerrore ",

View file

@ -116,7 +116,7 @@ Ls.kor = {
"ot_unpost": "주워담기: 최근 업로드한 항목을 삭제하거나 미완료된 업로드를 중단합니다", "ot_unpost": "주워담기: 최근 업로드한 항목을 삭제하거나 미완료된 업로드를 중단합니다",
"ot_bup": "bup: 기본 업로더. 넷스케이프 4.0도 지원합니다", "ot_bup": "bup: 기본 업로더. 넷스케이프 4.0도 지원합니다",
"ot_mkdir": "mkdir: 새 디렉터리를 만듭니다", "ot_mkdir": "mkdir: 새 디렉터리를 만듭니다",
"ot_md": "new-md: 새 마크다운 문서를 만듭니다", "ot_md": "new-file: 새 텍스트 파일을 만듭니다", //m
"ot_msg": "msg: 서버 로그에 메시지를 보냅니다", "ot_msg": "msg: 서버 로그에 메시지를 보냅니다",
"ot_mp": "미디어 플레이어 옵션", "ot_mp": "미디어 플레이어 옵션",
"ot_cfg": "구성 옵션", "ot_cfg": "구성 옵션",
@ -125,7 +125,7 @@ Ls.kor = {
"ot_noie": 'Chrome / Firefox / Edge를 사용해주세요', "ot_noie": 'Chrome / Firefox / Edge를 사용해주세요',
"ab_mkdir": "디렉터리 만들기", "ab_mkdir": "디렉터리 만들기",
"ab_mkdoc": "새 마크다운 문서", "ab_mkdoc": "새 텍스트 파일", //m
"ab_msg": "서버 로그에 메시지 보내기", "ab_msg": "서버 로그에 메시지 보내기",
"ay_path": "폴더로 건너뛰기", "ay_path": "폴더로 건너뛰기",
@ -437,6 +437,8 @@ Ls.kor = {
"fcp_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", "mk_noname": "왼쪽 텍스트 필드에 이름을 먼저 입력해주세요 :p",
"nmd_i1": "원하는 파일 확장자를 추가할 수 있습니다. 예: <code>.txt</code>", //m
"nmd_i2": "삭제 권한이 없어서 <code>.md</code> 파일만 만들 수 있습니다", //m
"tv_load": "텍스트 문서 불러오는 중:\n\n{0}\n\n{1}% ({3} MiB 중 {2} MiB 로드됨)", "tv_load": "텍스트 문서 불러오는 중:\n\n{0}\n\n{1}% ({3} MiB 중 {2} MiB 로드됨)",
"tv_xe1": "텍스트 파일을 불러올 수 없습니다:\n\n오류 ", "tv_xe1": "텍스트 파일을 불러올 수 없습니다:\n\n오류 ",

View file

@ -116,7 +116,7 @@ Ls.nld = {
"ot_unpost": "unpost: verwijder je recente uploads, of onvoltooide uploads afbreken", "ot_unpost": "unpost: verwijder je recente uploads, of onvoltooide uploads afbreken",
"ot_bup": "bup: Basisuploader, supports zelfs netscape 4.0", "ot_bup": "bup: Basisuploader, supports zelfs netscape 4.0",
"ot_mkdir": "mkdir: Maak een nieuwe map", "ot_mkdir": "mkdir: Maak een nieuwe map",
"ot_md": "new-md: Maak een nieuwe markdown bestand", "ot_md": "new-file: Maak een nieuw tekstbestand", //m
"ot_msg": "msg: Verstuur een bericht naar de server logs", "ot_msg": "msg: Verstuur een bericht naar de server logs",
"ot_mp": "Media speler opties", "ot_mp": "Media speler opties",
"ot_cfg": "Configuratie opties", "ot_cfg": "Configuratie opties",
@ -125,7 +125,7 @@ Ls.nld = {
"ot_noie": 'Gebruik alstublieft Chrome / Firefox / Edge', "ot_noie": 'Gebruik alstublieft Chrome / Firefox / Edge',
"ab_mkdir": "maak map", "ab_mkdir": "maak map",
"ab_mkdoc": "nieuw markdown doc", "ab_mkdoc": "nieuw tekstbestand", //m
"ab_msg": "verstuur msg naar srv log", "ab_msg": "verstuur msg naar srv log",
"ay_path": "skip naar mappen", "ay_path": "skip naar mappen",
@ -437,6 +437,8 @@ Ls.nld = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopieer</a><a href="#" id="modal-ng">Upload</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopieer</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "Voer een naam in het tekstveld aan de linkerkant voordat je verder gaat :p", "mk_noname": "Voer een naam in het tekstveld aan de linkerkant voordat je verder gaat :p",
"nmd_i1": "Voeg ook de gewenste extensie toe, bijvoorbeeld <code>.txt</code>", //m
"nmd_i2": "Je kunt alleen <code>.md</code>-bestanden maken omdat je geen verwijderrechten hebt", //m
"tv_load": "Tekstdocument laden:\n\n{0}\n\n{1}% ({2} van de {3} MiB geladen)", "tv_load": "Tekstdocument laden:\n\n{0}\n\n{1}% ({2} van de {3} MiB geladen)",
"tv_xe1": "Kon tekstbestand niet laden:\n\nfout ", "tv_xe1": "Kon tekstbestand niet laden:\n\nfout ",

View file

@ -114,7 +114,7 @@ Ls.nno = {
"ot_unpost": "unpost: slett filer som du nyleg har lastet opp; «angre-knappen»", "ot_unpost": "unpost: slett filer som du nyleg har lastet opp; «angre-knappen»",
"ot_bup": "bup: tradisjonell / primitiv filopplasting,$N$Nfungerar i om lag samtlege nettlesarar", "ot_bup": "bup: tradisjonell / primitiv filopplasting,$N$Nfungerar i om lag samtlege nettlesarar",
"ot_mkdir": "mkdir: lag ei ny mappe", "ot_mkdir": "mkdir: lag ei ny mappe",
"ot_md": "new-md: lag eit nytt markdown-dokument", "ot_md": "new-file: lag ein ny tekstfil",
"ot_msg": "msg: send ein beskjed åt serverloggen", "ot_msg": "msg: send ein beskjed åt serverloggen",
"ot_mp": "musikkspelarinstillinger", "ot_mp": "musikkspelarinstillinger",
"ot_cfg": "andre innstillinger", "ot_cfg": "andre innstillinger",
@ -123,7 +123,7 @@ Ls.nno = {
"ot_noie": 'Fungerer mye betre i Chrome / Firefox / Edge', "ot_noie": 'Fungerer mye betre i Chrome / Firefox / Edge',
"ab_mkdir": "lag mappe", "ab_mkdir": "lag mappe",
"ab_mkdoc": "nytt dokument", "ab_mkdoc": "ny tekstfil",
"ab_msg": "send melding", "ab_msg": "send melding",
"ay_path": "gå videre åt mapper", "ay_path": "gå videre åt mapper",
@ -435,6 +435,8 @@ Ls.nno = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
"mk_noname": "skriv inn eit namn i tekstboksa åt venstre først :p", "mk_noname": "skriv inn eit namn i tekstboksa åt venstre først :p",
"nmd_i1": "leggja også til filendinga du vil, til dømes <code>.txt</code>", //m
"nmd_i2": "du kan berre laga <code>.md</code>-filer fordi du ikkje har delete-tilgang", //m
"tv_load": "Lastar inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lasta ned)", "tv_load": "Lastar inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lasta ned)",
"tv_xe1": "kunne ikkje laste tekstfil:\n\nfeil ", "tv_xe1": "kunne ikkje laste tekstfil:\n\nfeil ",

View file

@ -114,7 +114,7 @@ Ls.nor = {
"ot_unpost": "unpost: slett filer som du nylig har lastet opp; «angre-knappen»", "ot_unpost": "unpost: slett filer som du nylig har lastet opp; «angre-knappen»",
"ot_bup": "bup: tradisjonell / primitiv filopplastning,$N$Nfungerer i omtrent samtlige nettlesere", "ot_bup": "bup: tradisjonell / primitiv filopplastning,$N$Nfungerer i omtrent samtlige nettlesere",
"ot_mkdir": "mkdir: lag en ny mappe", "ot_mkdir": "mkdir: lag en ny mappe",
"ot_md": "new-md: lag et nytt markdown-dokument", "ot_md": "new-file: lag en ny tekstfil",
"ot_msg": "msg: send en beskjed til serverloggen", "ot_msg": "msg: send en beskjed til serverloggen",
"ot_mp": "musikkspiller-instillinger", "ot_mp": "musikkspiller-instillinger",
"ot_cfg": "andre innstillinger", "ot_cfg": "andre innstillinger",
@ -123,7 +123,7 @@ Ls.nor = {
"ot_noie": 'Fungerer mye bedre i Chrome / Firefox / Edge', "ot_noie": 'Fungerer mye bedre i Chrome / Firefox / Edge',
"ab_mkdir": "lag mappe", "ab_mkdir": "lag mappe",
"ab_mkdoc": "nytt dokument", "ab_mkdoc": "ny tekstfil",
"ab_msg": "send melding", "ab_msg": "send melding",
"ay_path": "gå videre til mapper", "ay_path": "gå videre til mapper",
@ -435,6 +435,8 @@ Ls.nor = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
"mk_noname": "skriv inn et navn i tekstboksen til venstre først :p", "mk_noname": "skriv inn et navn i tekstboksen til venstre først :p",
"nmd_i1": "legg også til ønsket filtype, for eksempel <code>.txt</code>", //m
"nmd_i2": "du kan bare lage <code>.md</code>-filer fordi du ikke har delete-tilgang", //m
"tv_load": "Laster inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lastet ned)", "tv_load": "Laster inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lastet ned)",
"tv_xe1": "kunne ikke laste tekstfil:\n\nfeil ", "tv_xe1": "kunne ikke laste tekstfil:\n\nfeil ",

View file

@ -119,7 +119,7 @@ Ls.pol = {
"ot_unpost": "unpost: usuń ostatnio przesłane pliki lub przerwij przesyłanie", "ot_unpost": "unpost: usuń ostatnio przesłane pliki lub przerwij przesyłanie",
"ot_bup": "bup: podstawowe przesyłanie danych, wspiera nawet netscape 4.0", "ot_bup": "bup: podstawowe przesyłanie danych, wspiera nawet netscape 4.0",
"ot_mkdir": "mkdir: tworzy nowy folder", "ot_mkdir": "mkdir: tworzy nowy folder",
"ot_md": "new-md: tworzy nowy dokument markdown", "ot_md": "new-file: tworzy nowy plik tekstowy", //m
"ot_msg": "msg: wysyła wiadomość do loga serwera", "ot_msg": "msg: wysyła wiadomość do loga serwera",
"ot_mp": "opcje odtwarzacza multimediów", "ot_mp": "opcje odtwarzacza multimediów",
"ot_cfg": "opcje konfiguracji", "ot_cfg": "opcje konfiguracji",
@ -128,7 +128,7 @@ Ls.pol = {
"ot_noie": 'Użyj przeglądarki Chrome / Firefox / Edge', "ot_noie": 'Użyj przeglądarki Chrome / Firefox / Edge',
"ab_mkdir": "stwórz folder", "ab_mkdir": "stwórz folder",
"ab_mkdoc": "stwórz dok. markdown", "ab_mkdoc": "nowy plik tekstowy", //m
"ab_msg": "wyślij wiad. do logów serwera", "ab_msg": "wyślij wiad. do logów serwera",
"ay_path": "przejdź do folderów", "ay_path": "przejdź do folderów",
@ -440,6 +440,8 @@ Ls.pol = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiuj</a><a href="#" id="modal-ng">Prześlij</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopiuj</a><a href="#" id="modal-ng">Prześlij</a>',
"mk_noname": "wpisz nazwę do pola po lewej zanim to zrobisz :p", "mk_noname": "wpisz nazwę do pola po lewej zanim to zrobisz :p",
"nmd_i1": "możesz też dodać wybrane rozszerzenie, np. <code>.txt</code>", //m
"nmd_i2": "możesz tworzyć tylko pliki <code>.md</code>, ponieważ nie masz uprawnień do usuwania", //m
"tv_load": "Wczytywanie pliku tekstowego:\n\n{0}\n\n{1}% (wczytano {2} z {3} MiB)", "tv_load": "Wczytywanie pliku tekstowego:\n\n{0}\n\n{1}% (wczytano {2} z {3} MiB)",
"tv_xe1": "nie udało się wczytać pliku:\n\nbłąd ", "tv_xe1": "nie udało się wczytać pliku:\n\nbłąd ",

View file

@ -116,7 +116,7 @@ Ls.por = {
"ot_unpost": "despublicar: excluir seus uploads recentes, ou abortar os que não foram concluídos", "ot_unpost": "despublicar: excluir seus uploads recentes, ou abortar os que não foram concluídos",
"ot_bup": "bup: uploader básico, até suporta netscape 4.0", "ot_bup": "bup: uploader básico, até suporta netscape 4.0",
"ot_mkdir": "mkdir: criar um novo diretório", "ot_mkdir": "mkdir: criar um novo diretório",
"ot_md": "new-md: criar um novo documento markdown", "ot_md": "new-file: criar um novo ficheiro de texto", //m
"ot_msg": "msg: enviar uma mensagem para o log do servidor", "ot_msg": "msg: enviar uma mensagem para o log do servidor",
"ot_mp": "opções do reprodutor de mídia", "ot_mp": "opções do reprodutor de mídia",
"ot_cfg": "opções de configuração", "ot_cfg": "opções de configuração",
@ -125,7 +125,7 @@ Ls.por = {
"ot_noie": 'Por favor, use Chrome / Firefox / Edge', "ot_noie": 'Por favor, use Chrome / Firefox / Edge',
"ab_mkdir": "criar diretório", "ab_mkdir": "criar diretório",
"ab_mkdoc": "novo documento markdown", "ab_mkdoc": "novo ficheiro de texto", //m
"ab_msg": "enviar msg para o log do srv", "ab_msg": "enviar msg para o log do srv",
"ay_path": "pular para pastas", "ay_path": "pular para pastas",
@ -437,6 +437,8 @@ Ls.por = {
"fcp_both_b": '<a href="#" id="modal-ok">Copiar</a><a href="#" id="modal-ng">Enviar</a>', "fcp_both_b": '<a href="#" id="modal-ok">Copiar</a><a href="#" id="modal-ng">Enviar</a>',
"mk_noname": "digite um nome no campo de texto à esquerda antes de fazer isso :p", "mk_noname": "digite um nome no campo de texto à esquerda antes de fazer isso :p",
"nmd_i1": "também pode adicionar a extensão desejada, por exemplo <code>.txt</code>", //m
"nmd_i2": "só pode criar ficheiros <code>.md</code> porque não tem permissão para apagar", //m
"tv_load": "Carregando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB carregados)", "tv_load": "Carregando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB carregados)",
"tv_xe1": "não foi possível carregar o arquivo de texto:\n\nerro ", "tv_xe1": "não foi possível carregar o arquivo de texto:\n\nerro ",

View file

@ -116,7 +116,7 @@ Ls.rus = {
"ot_unpost": "unpost: удалить ваши недавние загрузки и отменить незавершённые", "ot_unpost": "unpost: удалить ваши недавние загрузки и отменить незавершённые",
"ot_bup": "bup: легковесный загрузчик файлов, поддерживает даже netscape 4.0", "ot_bup": "bup: легковесный загрузчик файлов, поддерживает даже netscape 4.0",
"ot_mkdir": "mkdir: создать новую папку", "ot_mkdir": "mkdir: создать новую папку",
"ot_md": "new-md: создать новый markdown-документ", "ot_md": "new-file: создать новый текстовый файл", //m
"ot_msg": "msg: отправить сообщение в лог сервера", "ot_msg": "msg: отправить сообщение в лог сервера",
"ot_mp": "настройка медиаплеера", "ot_mp": "настройка медиаплеера",
"ot_cfg": "остальные настройки", "ot_cfg": "остальные настройки",
@ -125,7 +125,7 @@ Ls.rus = {
"ot_noie": 'Пожалуйста, используйте Chrome / Firefox / Edge', "ot_noie": 'Пожалуйста, используйте Chrome / Firefox / Edge',
"ab_mkdir": "создать папку", "ab_mkdir": "создать папку",
"ab_mkdoc": "создать markdown-документ", "ab_mkdoc": "создать текстовый файл", //m
"ab_msg": "отправить сообщение в лог сервера", "ab_msg": "отправить сообщение в лог сервера",
"ay_path": "перейти к папкам", "ay_path": "перейти к папкам",
@ -437,6 +437,8 @@ Ls.rus = {
"fcp_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", "mk_noname": "введите имя в текстовое поле слева перед тем, как это делать :p",
"nmd_i1": "вы также можете указать нужное расширение, например <code>.txt</code>", //m
"nmd_i2": "вы можете создавать только файлы <code>.md</code>, так как у вас нет разрешения на удаление", //m
"tv_load": "Загружаю текстовый документ:\n\n{0}\n\n{1}% ({2} из {3} МиБ загружено)", "tv_load": "Загружаю текстовый документ:\n\n{0}\n\n{1}% ({2} из {3} МиБ загружено)",
"tv_xe1": "не удалось загрузить текстовый файл:\n\nошибка ", "tv_xe1": "не удалось загрузить текстовый файл:\n\nошибка ",

View file

@ -116,7 +116,7 @@ Ls.spa = {
"ot_unpost": "dessubir: elimina tus subidas recientes, o aborta las inacabadas", "ot_unpost": "dessubir: elimina tus subidas recientes, o aborta las inacabadas",
"ot_bup": "bup: uploader básico, soporta hasta netscape 4.0", "ot_bup": "bup: uploader básico, soporta hasta netscape 4.0",
"ot_mkdir": "mkdir: crear un nuevo directorio", "ot_mkdir": "mkdir: crear un nuevo directorio",
"ot_md": "new-md: crear un nuevo documento markdown", "ot_md": "new-file: crear un nuevo archivo de texto", //m
"ot_msg": "msg: enviar un mensaje al registro del servidor", "ot_msg": "msg: enviar un mensaje al registro del servidor",
"ot_mp": "opciones del reproductor multimedia", "ot_mp": "opciones del reproductor multimedia",
"ot_cfg": "opciones de configuración", "ot_cfg": "opciones de configuración",
@ -125,7 +125,7 @@ Ls.spa = {
"ot_noie": "Por favor, usa Chrome / Firefox / Edge", "ot_noie": "Por favor, usa Chrome / Firefox / Edge",
"ab_mkdir": "crear directorio", "ab_mkdir": "crear directorio",
"ab_mkdoc": "nuevo documento markdown", "ab_mkdoc": "nuevo archivo de texto", //m
"ab_msg": "enviar msg al registro del servidor", "ab_msg": "enviar msg al registro del servidor",
"ay_path": "saltar a carpetas", "ay_path": "saltar a carpetas",
@ -436,6 +436,8 @@ Ls.spa = {
"fcp_both_b": "<a href=\"#\" id=\"modal-ok\">Copiar</a><a href=\"#\" id=\"modal-ng\">Subir</a>", "fcp_both_b": "<a href=\"#\" id=\"modal-ok\">Copiar</a><a href=\"#\" id=\"modal-ng\">Subir</a>",
"mk_noname": "escribe un nombre en el campo de texto de la izquierda antes de hacer eso :p", "mk_noname": "escribe un nombre en el campo de texto de la izquierda antes de hacer eso :p",
"nmd_i1": "también puedes añadir la extensión que quieras, por ejemplo <code>.txt</code>", //m
"nmd_i2": "solo puedes crear archivos <code>.md</code> porque no tienes permiso para borrar", //m
"tv_load": "Cargando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB cargados)", "tv_load": "Cargando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB cargados)",
"tv_xe1": "no se pudo cargar el archivo de texto:\n\nerror ", "tv_xe1": "no se pudo cargar el archivo de texto:\n\nerror ",

View file

@ -116,7 +116,7 @@ Ls.swe = {
"ot_unpost": "unpost: radera dina senaste uppladdningar, eller avbryt pågående sådana", "ot_unpost": "unpost: radera dina senaste uppladdningar, eller avbryt pågående sådana",
"ot_bup": "bup: enkel uppladdare, stödjer t o m netscape 4.0", "ot_bup": "bup: enkel uppladdare, stödjer t o m netscape 4.0",
"ot_mkdir": "mkdir: skapa en ny mapp", "ot_mkdir": "mkdir: skapa en ny mapp",
"ot_md": "new-md: skapa ett nytt markdown-dokument", "ot_md": "new-file: skapa en ny textfil",
"ot_msg": "msg: skicka ett meddelande till serverloggen", "ot_msg": "msg: skicka ett meddelande till serverloggen",
"ot_mp": "mediaspelarinställningar", "ot_mp": "mediaspelarinställningar",
"ot_cfg": "konfigurationsinställningar", "ot_cfg": "konfigurationsinställningar",
@ -125,7 +125,7 @@ Ls.swe = {
"ot_noie": 'Var vänlig använd Chrome / Firefox / Edge', "ot_noie": 'Var vänlig använd Chrome / Firefox / Edge',
"ab_mkdir": "skapa mapp", "ab_mkdir": "skapa mapp",
"ab_mkdoc": "nytt markdown-dokument", "ab_mkdoc": "ny textfil",
"ab_msg": "skicka medd. till serverlogg", "ab_msg": "skicka medd. till serverlogg",
"ay_path": "hoppa till mappar", "ay_path": "hoppa till mappar",
@ -437,6 +437,8 @@ Ls.swe = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopiera</a><a href="#" id="modal-ng">Ladda upp</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopiera</a><a href="#" id="modal-ng">Ladda upp</a>',
"mk_noname": "skriv ett namn i fältet till vänster först :p", "mk_noname": "skriv ett namn i fältet till vänster först :p",
"nmd_i1": "lägg också till filändelsen du vill ha, till exempel <code>.txt</code>", //m
"nmd_i2": "du kan bara skapa <code>.md</code>-filer eftersom du inte har borttagningsbehörighet", //m
"tv_load": "Laddar textfil:\n\n{0}\n\n{1}% ({2} av {3} MiB laddat)", "tv_load": "Laddar textfil:\n\n{0}\n\n{1}% ({2} av {3} MiB laddat)",
"tv_xe1": "kunde ej ladda textfil:\n\nfel ", "tv_xe1": "kunde ej ladda textfil:\n\nfel ",

View file

@ -116,7 +116,7 @@ Ls.tur = {
"ot_unpost": "unpost: son yüklemelerinizi silin veya tamamlanmamış olanları iptal edin", "ot_unpost": "unpost: son yüklemelerinizi silin veya tamamlanmamış olanları iptal edin",
"ot_bup": "bup: temel yükleyici, hatta netscape 4.0'ı destekler", "ot_bup": "bup: temel yükleyici, hatta netscape 4.0'ı destekler",
"ot_mkdir": "mkdir: yeni bir dizin oluştur", "ot_mkdir": "mkdir: yeni bir dizin oluştur",
"ot_md": "new-md: yeni bir markdown belgesi oluştur", "ot_md": "new-file: yeni bir metin dosyası oluştur", //m
"ot_msg": "msg: sunucu günlüğüne bir mesaj gönder", "ot_msg": "msg: sunucu günlüğüne bir mesaj gönder",
"ot_mp": "medya oynatıcı seçenekleri", "ot_mp": "medya oynatıcı seçenekleri",
"ot_cfg": "konfigürasyon seçenekleri", "ot_cfg": "konfigürasyon seçenekleri",
@ -125,7 +125,7 @@ Ls.tur = {
"ot_noie": 'Lütfen Chrome / Firefox / Edge kullanın', "ot_noie": 'Lütfen Chrome / Firefox / Edge kullanın',
"ab_mkdir": "dizin oluştur", "ab_mkdir": "dizin oluştur",
"ab_mkdoc": "yeni markdown belgesi", "ab_mkdoc": "yeni metin dosyası", //m
"ab_msg": "sunucu günlüğüne mesaj gönder", "ab_msg": "sunucu günlüğüne mesaj gönder",
"ay_path": "klasörlere atla", "ay_path": "klasörlere atla",
@ -437,6 +437,8 @@ Ls.tur = {
"fcp_both_b": '<a href="#" id="modal-ok">Kopyala</a><a href="#" id="modal-ng">Yükle</a>', "fcp_both_b": '<a href="#" id="modal-ok">Kopyala</a><a href="#" id="modal-ng">Yükle</a>',
"mk_noname": "bunu yapmadan önce soldaki boşluğa bir şeyler yazsana :p", "mk_noname": "bunu yapmadan önce soldaki boşluğa bir şeyler yazsana :p",
"nmd_i1": "ayrıca istediğin dosya uzantısını ekleyebilirsin, örneğin <code>.txt</code>", //m
"nmd_i2": "silme iznin olmadığı için yalnızca <code>.md</code> dosyaları oluşturabilirsin", //m
"tv_load": "Metin belgesi yükleniyor:\n\n{0}\n\n{1}% ({2} of {3} MiB yüklendi)", "tv_load": "Metin belgesi yükleniyor:\n\n{0}\n\n{1}% ({2} of {3} MiB yüklendi)",
"tv_xe1": "metin dosyası yüklenemedi:\n\nhata ", "tv_xe1": "metin dosyası yüklenemedi:\n\nhata ",

View file

@ -116,7 +116,7 @@ Ls.ukr = {
"ot_unpost": "скасувати: видалити недавні завантаження або перервати незавершені", "ot_unpost": "скасувати: видалити недавні завантаження або перервати незавершені",
"ot_bup": "bup: основний завантажувач, підтримує навіть netscape 4.0", "ot_bup": "bup: основний завантажувач, підтримує навіть netscape 4.0",
"ot_mkdir": "mkdir: створити нову папку", "ot_mkdir": "mkdir: створити нову папку",
"ot_md": "new-md: створити новий markdown документ", "ot_md": "new-file: створити новий текстовий файл", //m
"ot_msg": "msg: надіслати повідомлення в лог сервера", "ot_msg": "msg: надіслати повідомлення в лог сервера",
"ot_mp": "налаштування медіаплеєра", "ot_mp": "налаштування медіаплеєра",
"ot_cfg": "параметри конфігурації", "ot_cfg": "параметри конфігурації",
@ -125,7 +125,7 @@ Ls.ukr = {
"ot_noie": 'Будь ласка, використовуйте Chrome / Firefox / Edge', "ot_noie": 'Будь ласка, використовуйте Chrome / Firefox / Edge',
"ab_mkdir": "створити папку", "ab_mkdir": "створити папку",
"ab_mkdoc": "новий markdown документ", "ab_mkdoc": "новий текстовий файл", //m
"ab_msg": "надіслати повідомлення в лог сервера", "ab_msg": "надіслати повідомлення в лог сервера",
"ay_path": "перейти до папок", "ay_path": "перейти до папок",
@ -437,6 +437,8 @@ Ls.ukr = {
"fcp_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", "mk_noname": "введіть ім'я в текстове поле зліва перед тим, як робити це :p",
"nmd_i1": "ви також можете додати потрібне розширення, наприклад <code>.txt</code>", //m
"nmd_i2": "ви можете створювати тільки файли <code>.md</code>, оскільки не маєте дозволу на видалення", //m
"tv_load": "Завантаження текстового документа:\n\n{0}\n\n{1}% ({2} з {3} MiB завантажено)", "tv_load": "Завантаження текстового документа:\n\n{0}\n\n{1}% ({2} з {3} MiB завантажено)",
"tv_xe1": "не вдалося завантажити текстовий файл:\n\nпомилка ", "tv_xe1": "не вдалося завантажити текстовий файл:\n\nпомилка ",

View file

@ -1999,6 +1999,18 @@ function up2k_init(subtle) {
if (pvis.act == 'bz') if (pvis.act == 'bz')
pvis.changecard('bz'); pvis.changecard('bz');
var n = st.files.length - 1,
f = n >= 0 && st.files[n];
if (f && !f.srch) {
var xhr = new XHR(),
ct = 'application/x-www-form-urlencoded;charset=UTF-8';
xhr.open('POST', f.purl, true);
xhr.setRequestHeader('Content-Type', ct);
if (xhr.overrideMimeType)
xhr.overrideMimeType('Content-Type', ct);
xhr.send('msg=upload-queue-empty;' + uricom_enc(f.name));
}
} }
function chill(t) { function chill(t) {
@ -2671,8 +2683,6 @@ function up2k_init(subtle) {
f2f(spd1, 2), !isNum(spd2) ? '--' : f2f(spd2, 2))); f2f(spd1, 2), !isNum(spd2) ? '--' : f2f(spd2, 2)));
pvis.move(t.n, 'ok'); pvis.move(t.n, 'ok');
if (!pvis.ctr.bz && !pvis.ctr.q)
uptoast();
} }
else { else {
if (t.t_uploaded) if (t.t_uploaded)

View file

@ -1487,7 +1487,7 @@ var tt = (function () {
var r = { var r = {
"tt": mknod("div", 'tt'), "tt": mknod("div", 'tt'),
"th": mknod("div", 'tth'), "th": mknod("div", 'tth'),
"en": true, "en": !window.notooltips,
"el": null, "el": null,
"skip": false, "skip": false,
"lvis": 0 "lvis": 0

View file

@ -1,3 +1,82 @@
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1025-1918 `v1.19.19` copyparty.eu マークII
## 🩹 bugfixes
* fix building the archlinux package e3524d85
* otherwise identical to [v1.19.18](https://github.com/9001/copyparty/releases/tag/v1.19.18)
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1025-1434 `v1.19.18` copyparty.eu
## 🧪 new features
* #949 when all uploads have finished, the client (both the browser and u2c) sends a message to the server saying it's done db87ea5c
* #941 [copyparty-en.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.pyz), yet another copyparty variant, with enterprise-friendly tweaks:
* does not include the smb-server, so antivirus doesn't think it's malware 7f5810f1
* english-only, because antivirus apparently hates certain translations too 7f5810f1
* renamed the webdav-config `.bat` to `.txt` because clearly only one of those are "dangerous" b624a387
* show volumes with permssion `h` in the navpane fff7291d
* #937 global-option `--notooltips` to default-disable tooltips a325353b
## 🩹 bugfixes
* #948 fix the u2c `--dr` option when the server is running on windows d3dd3456
* fix crash on startup when using volflags `unlistc*` and the parent folder is not a volume cdd5e78a
* `og` / opengraph / discord-embed fixes:
* using the `h` permission could result in unexpected 404 c9e45c12
* a single-file volume could make filenames in its parent volume unintentionally visible 36ab77e0
* this would only happen when combined with `--og`
* fix some harmless warnings from single-file volumes b1efc006
* fix filesize-colors in selected rows 1c17b63b
## 🔧 other changes
* releases can now also be downloaded from https://copyparty.eu/ 547a7ab1
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1017-2313 `v1.19.17` read:cbz + re:ftp
## 🧪 new features
* #916 view cbz manga/comics in the browser (thx @Scotsguy!) 8ef6dda7
* #845 users/groups can be subtracted from a broader access grant b4fda5f1
* for example `*,-@acct` hides a volume from everyone who's logged in
* [reflink dedup](https://github.com/9001/copyparty/#file-deduplication) is now available in most python versions, not just 3.14 and newer f2caab61
* much better and safer than symlink/hardlink-based dedup, but only works with a few filesystems
* #905 option to magnify images/videos to fill the screen 66dc8b5c
* #921 #685 `xm` hooks can see the selected files (thx @carson-coder!) 6c024dbf 33644488
* #927 textfiles can now be viewed with the `?doc=` suffix with just the `g` permission dbb78705
* #742 new volflag `nodupem` to prevent dupes from being moved into a volume; the stronger alternative to `nodupe` which only prevents uploads f55d8341
* audioplayer: show embedded coverart as fallback for cover.jpg in OS widgets 9746b4e2
* #928 option to [hide certain ui-elements](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#hide-ui-elements), either with volflags or url-params 98da5cc5
* #911 users can now avoid autoban according to permissions 6f02812a
* verbosity and permssion options for `?stack` 677fd8ee
* default is now admin-only; previously it was "admin or read+write"
## 🩹 bugfixes
* #914 ftp-server: resuming interrupted uploads (thx @Audionut!) 33b0cd5a
* race-the-beam didn't work in non-toplevel shares d9cd7ec3
## 🔧 other changes
* #904 new example hook [wget-i.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/wget-i.py); import-safe fork of [wget.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/wget.py) dbd8f837
* hide the search-ui while viewing a share because searching in shares is not possible cca1f9b2
* config-parser now prevents invalid values for the lifetime volflag 5d96862c
* translations are now [separate files](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl) instead of all chilling inside browser.js d099e5e8 d6433b78 a7840beb a7cdc5de 98086948 a85ad201 c2e03bf6 b9d7ede3 5a29df6b 52446bb5 bb166c98 0fa862e1 6de6aa4b 748aaa95 07ace416 b61b910e 28b93238 14bd4cf5 50109f76 3b009d97 f5425a88 5232ce6a 02ba9ea7 ff01723c d099e5e8
## 🌠 fun facts
* looks like i'll be in Japan november 726 and then at CCC for newyears!
* wait, I never made stickers... orz
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-1005-2259 `v1.19.16` FULLBURST # 2025-1005-2259 `v1.19.16` FULLBURST

View file

@ -96,7 +96,8 @@ copyparty = [
"web/*.xml", "web/*.xml",
"web/tl/*.js", "web/tl/*.js",
"web/tl/*.gz", "web/tl/*.gz",
"web/a/*.bat", "web/a/*.txt",
"web/a/*.gz",
"web/deps/*.gz", "web/deps/*.gz",
"web/deps/*.woff*", "web/deps/*.woff*",
] ]

View file

@ -33,10 +33,11 @@ i'm not very familiar with containers, so let me know if this section could be b
the container has the same default config as the sfx and the pypi module, meaning it will listen on port 3923 and share the "current folder" (`/w` inside the container) as read-write for anyone the container has the same default config as the sfx and the pypi module, meaning it will listen on port 3923 and share the "current folder" (`/w` inside the container) as read-write for anyone
the recommended way to configure copyparty inside a container is to mount a folder which has one or more [config files](https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf) inside; `-v /your/config/folder:/cfg` the recommended way to configure copyparty inside a container is to mount a folder which has one or more [config files](https://github.com/9001/copyparty/blob/hovudstraum/docs/examples/docker/basic-docker-compose/copyparty.conf) inside; `-v /your/config/folder:/cfg`
* but you can also provide arguments to the docker command if you prefer that * but you can also provide arguments to the docker command if you prefer that
* config files must be named `something.conf` to get picked up * config files must be named `something.conf` to get picked up
* there are [more extensive config examples](https://github.com/9001/copyparty/blob/hovudstraum/docs/example.conf) but those are not made for docker so the paths are wrong (`/home/ed/Music` should be `/w/something` and so on)
also see [docker-specific recommendations](#docker-specific-recommendations) also see [docker-specific recommendations](#docker-specific-recommendations)

View file

@ -85,6 +85,11 @@ filt=
wget https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py -O $fp wget https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py -O $fp
} }
# enable arm32 crossbuild from aarch64 (macbook or whatever)
[ $(uname -m) = aarch64 ] && [ ! -e /proc/sys/fs/binfmt_misc/qemu-arm ] &&
echo ":qemu-arm:M:0:\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:F" |
sudo tee >/dev/null /proc/sys/fs/binfmt_misc/register
# kill abandoned builders # kill abandoned builders
ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9 ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9

View file

@ -10,6 +10,7 @@ gtar=$(command -v gtar || command -v gnutar) || true
sed() { gsed "$@"; } sed() { gsed "$@"; }
find() { gfind "$@"; } find() { gfind "$@"; }
sort() { gsort "$@"; } sort() { gsort "$@"; }
nproc() { gnproc; }
command -v grealpath >/dev/null && command -v grealpath >/dev/null &&
realpath() { grealpath "$@"; } realpath() { grealpath "$@"; }
} }

View file

@ -41,8 +41,10 @@ rm -rf pyz
mkdir -p pyz mkdir -p pyz
cd pyz cd pyz
cp -pR ../sfx/{copyparty,partftpy} . cp -pR ../sfx/copyparty .
cp -pR ../sfx/{ftp,j2}/* . cp -pR ../sfx/j2/* .
[ -e ../sfx/partftpy ] && cp -pR ../sfx/partftpy .
[ -e ../sfx/ftp ] && cp -pR ../sfx/ftp/* .
true && { true && {
rm -rf copyparty/web/mde.* copyparty/web/deps/easymde* rm -rf copyparty/web/mde.* copyparty/web/deps/easymde*

View file

@ -445,14 +445,27 @@ iawk '/^class _Base/{s=1}!s' ftp/pyftpdlib/authorizers.py
iawk '/^ {0,4}[a-zA-Z]/{s=0}/^ {4}def (serve_forever|_loop)/{s=1}!s' ftp/pyftpdlib/servers.py iawk '/^ {0,4}[a-zA-Z]/{s=0}/^ {4}def (serve_forever|_loop)/{s=1}!s' ftp/pyftpdlib/servers.py
rm -f ftp/pyftpdlib/{__main__,prefork}.py rm -f ftp/pyftpdlib/{__main__,prefork}.py
[ $no_ftp ] && unhelp() {
iawk '!/add_argument\("--'$1'/{print;next}
/ent\("--'$1'"/{print gensub(/(help=")[^"]+/,"\\1not available in this build","1");next}
{sub(/help=.*/,"help=argparse.SUPPRESS)")}1' copyparty/__main__.py
}
[ $no_ftp ] && {
unhelp ftp
rm -rf copyparty/ftpd.py ftp rm -rf copyparty/ftpd.py ftp
}
[ $no_tfp ] && [ $no_tfp ] && {
unhelp tftp
rm -rf copyparty/tftpd.py partftpy rm -rf copyparty/tftpd.py partftpy
}
[ $no_smb ] && [ $no_smb ] && {
unhelp smb
rm -f copyparty/smbd.py rm -f copyparty/smbd.py
ised 's/^( {8}elif )record\.name.*"impacket".*/\10:/' copyparty/util.py
}
[ $no_zm ] && [ $no_zm ] &&
rm -rf copyparty/mdns.py copyparty/stolen/dnslib rm -rf copyparty/mdns.py copyparty/stolen/dnslib
@ -571,7 +584,7 @@ gzres() {
$pk "$f" & $pk "$f" &
done < <( done < <(
find -printf '%s %p\n' | find -printf '%s %p\n' |
grep -E '\.(js|css)$' | grep -E '\.(js|css)$|/web/a/.*\.txt$' |
grep -vF /deps/ | grep -vF /deps/ |
sort -nr sort -nr
) )

View file

@ -4,7 +4,7 @@ set -e
# general housekeeping before a release # general housekeeping before a release
self=$(cd -- "$(dirname "$BASH_SOURCE")"; pwd -P) self=$(cd -- "$(dirname "$BASH_SOURCE")"; pwd -P)
ver=$(awk '/^VERSION/{gsub(/[^0-9]/," ");printf "%d.%d.%d\n",$1,$2,$3}' copyparty/__version__.py) ver=$(awk '/^VERSION/{gsub(/[^0-9]/," ");printf "%d.%d.%d\n",$1,$2,$3}' $self/../copyparty/__version__.py)
update_arch_pkgbuild() { update_arch_pkgbuild() {
cd "$self/../contrib/package/arch" cd "$self/../contrib/package/arch"

View file

@ -49,14 +49,23 @@ while [ "$1" ]; do
done done
./make-pyz.sh ./make-pyz.sh
mv ../dist/copyparty{,-int}.pyz
./make-sfx.sh re lang eng "$@" ./make-sfx.sh re lang eng "$@"
mv ../dist/copyparty-{sfx,en}.py mv ../dist/copyparty-{sfx,en}.py
rm -rf /tmp/pe-copyparty* ../sfx
../dist/copyparty-en.py --version >/dev/null 2>&1
./make-sfx.sh re no-smb "$@"
./make-pyz.sh
mv ../dist/copyparty{,-en}.pyz
mv ../dist/copyparty{-int,}.pyz
mv ../dist/copyparty-{int,sfx}.py mv ../dist/copyparty-{int,sfx}.py
./genhelp.sh ./genhelp.sh
[ $rls ] || exit # ---------------------------------------------------- [ $rls ] || exit 0 # ----------------------------------------------------
./prep.sh ./prep.sh
git add ../contrib/package/arch/PKGBUILD ../contrib/package/makedeb-mpr/PKGBUILD ../contrib/package/nix/copyparty/pin.json git add ../contrib/package/arch/PKGBUILD ../contrib/package/makedeb-mpr/PKGBUILD ../contrib/package/nix/copyparty/pin.json

View file

@ -66,7 +66,7 @@ copyparty/web/a,
copyparty/web/a/__init__.py, copyparty/web/a/__init__.py,
copyparty/web/a/partyfuse.py, copyparty/web/a/partyfuse.py,
copyparty/web/a/u2c.py, copyparty/web/a/u2c.py,
copyparty/web/a/webdav-cfg.bat, copyparty/web/a/webdav-cfg.txt,
copyparty/web/baguettebox.js, copyparty/web/baguettebox.js,
copyparty/web/browser.css, copyparty/web/browser.css,
copyparty/web/browser.html, copyparty/web/browser.html,

View file

@ -145,7 +145,7 @@ Ls.hmn = {
"ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones", "ot_unpost": "unpost: delete your recent uploads, or abort unfinished ones",
"ot_bup": "bup: basic uploader, even supports netscape 4.0", "ot_bup": "bup: basic uploader, even supports netscape 4.0",
"ot_mkdir": "mkdir: create a new directory", "ot_mkdir": "mkdir: create a new directory",
"ot_md": "new-md: create a new markdown document", "ot_md": "new-file: create a new textfile",
"ot_msg": "msg: send a message to the server log", "ot_msg": "msg: send a message to the server log",
"ot_mp": "media player options", "ot_mp": "media player options",
"ot_cfg": "configuration options", "ot_cfg": "configuration options",
@ -154,7 +154,7 @@ Ls.hmn = {
"ot_noie": 'Please use Chrome / Firefox / Edge', "ot_noie": 'Please use Chrome / Firefox / Edge',
"ab_mkdir": "make directory", "ab_mkdir": "make directory",
"ab_mkdoc": "new markdown doc", "ab_mkdoc": "new textfile",
"ab_msg": "send msg to srv log", "ab_msg": "send msg to srv log",
"ay_path": "skip to folders", "ay_path": "skip to folders",
@ -466,6 +466,8 @@ Ls.hmn = {
"fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>', "fcp_both_b": '<a href="#" id="modal-ok">Copy</a><a href="#" id="modal-ng">Upload</a>',
"mk_noname": "type a name into the text field on the left before you do that :p", "mk_noname": "type a name into the text field on the left before you do that :p",
"nmd_i1": "also add the file extension you want, for example <code>.md</code>",
"nmd_i2": "you can only create <code>.md</code> files because you don't have the delete-permission",
"tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)", "tv_load": "Loading text document:\n\n{0}\n\n{1}% ({2} of {3} MiB loaded)",
"tv_xe1": "could not load textfile:\n\nerror ", "tv_xe1": "could not load textfile:\n\nerror ",

View file

@ -187,9 +187,9 @@ class TestVFS(unittest.TestCase):
self.assertEqual(n.realpath, os.path.join(td, "a")) self.assertEqual(n.realpath, os.path.join(td, "a"))
self.assertAxs(n.axs.uread, ["*", "k"]) self.assertAxs(n.axs.uread, ["*", "k"])
self.assertAxs(n.axs.uwrite, []) self.assertAxs(n.axs.uwrite, [])
perm_na = (False, False, False, False, False, False, False, False) perm_na = (False, False, False, False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False, False, False) perm_rw = (True, True, False, False, False, False, False, False, False)
perm_ro = (True, False, False, False, False, False, False, False) perm_ro = (True, False, False, False, False, False, False, False, False)
self.assertEqual(vfs.can_access("/", "*"), perm_na) self.assertEqual(vfs.can_access("/", "*"), perm_na)
self.assertEqual(vfs.can_access("/", "k"), perm_rw) self.assertEqual(vfs.can_access("/", "k"), perm_rw)
self.assertEqual(vfs.can_access("/a", "*"), perm_ro) self.assertEqual(vfs.can_access("/a", "*"), perm_ro)