mirror of
https://github.com/9001/copyparty.git
synced 2026-08-13 13:53:06 -06:00
Compare commits
18 commits
v1.20.19
...
hovudstrau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbcb773307 | ||
|
|
3eb7b46d83 | ||
|
|
4f20738b71 | ||
|
|
ef75220faf | ||
|
|
a3acbfcd5b | ||
|
|
fdf52a8c4e | ||
|
|
b9405a3e25 | ||
|
|
6f924829ee | ||
|
|
24a4090fc7 | ||
|
|
c0c638c917 | ||
|
|
2c6497b644 | ||
|
|
2c4033831f | ||
|
|
2880117265 | ||
|
|
63c61b6729 | ||
|
|
5c157be955 | ||
|
|
9f5f9d8884 | ||
|
|
4003a47723 | ||
|
|
398fcf1d18 |
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
*.png binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.gz binary
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -137,6 +137,7 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
|
|||
* [client-side](#client-side) - when uploading files
|
||||
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
|
||||
* [gotchas](#gotchas) - behavior that might be unexpected
|
||||
* [csp nonce](#csp-nonce) - unauthorized javascript is blocked
|
||||
* [cors](#cors) - cross-site request config
|
||||
* [filekeys](#filekeys) - prevent filename bruteforcing
|
||||
* [dirkeys](#dirkeys) - share specific folders in a volume
|
||||
|
|
@ -3107,6 +3108,23 @@ behavior that might be unexpected
|
|||
* or eliminate the problem entirely by only giving write-access to trustworthy people :^)
|
||||
|
||||
|
||||
## csp nonce
|
||||
|
||||
unauthorized javascript is blocked by means of the default [content-security-policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) which enforces "script nonces", to help prevent XSS and similar
|
||||
|
||||
usually this Just Works and is correctly configured by default; you can stop reading this section and move on, nothing to see here
|
||||
|
||||
but if you are the unlucky 0.1% with a hosting-setup funky enough for this to not work as intended, then you will also be seeing this message: `Looks like javascript is broken; see "csp nonce" in the readme`
|
||||
|
||||
there is two possible reasons why this might happen:
|
||||
|
||||
1. your browser is simply too old to understand the javascript, which is not likely, because just about every browser is supported, even internet explorer 11
|
||||
|
||||
2. something in your hosting-setup is tampering with the responses from copyparty and destroying this security feature; the culprit is probably a misconfigured reverseproxy, a CDN, a WAF, or some other mitm, but a popular offender is **"cloudflare rocket loader"**
|
||||
|
||||
you can try to **temporarily** set global-options `--csp-ui no` and `--csp-dl no` and restart copyparty to check whether it is due to reason #2. Remember to refresh the webbrowser with CTRL-SHIFT-R to check properly. If that makes the problem go away, then turn them back off (**never** keep them enabled), restart copyparty, open the `Network` tab in the webbrowser, and refresh the website; you should see the `content-security-policy` header including a nonce, for example `nonce-BxcJbvdu1z3S/ycPBjVWuA==` which should also appear inside the HTML of that response like this: `<script nonce="BxcJbvdu1z3S/ycPBjVWuA==">` -- if it doesn't then that's the problem; something in your hosting stack is dangerously misconfigured
|
||||
|
||||
|
||||
## cors
|
||||
|
||||
cross-site request config
|
||||
|
|
|
|||
|
|
@ -17,8 +17,12 @@ defaults
|
|||
|
||||
listen foo1
|
||||
bind *:8081
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
http-request add-header X-Forwarded-Proto http unless { ssl_fc }
|
||||
server srv1 127.0.0.1:3923 maxconn 512
|
||||
|
||||
listen foo2
|
||||
bind *:8082
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
http-request add-header X-Forwarded-Proto http unless { ssl_fc }
|
||||
server srv1 /dev/shm/party.sock maxconn 512
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ in
|
|||
|
||||
environment = {
|
||||
PYTHONUNBUFFERED = "true";
|
||||
NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
XDG_CONFIG_HOME = externalStateDir;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.20.18"
|
||||
pkgver="1.20.20"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
|
@ -24,7 +24,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}/copyparty.conf" )
|
||||
sha256sums=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||
sha256sums=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.20.18
|
||||
pkgver=1.20.20
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
|
@ -21,7 +21,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("/etc/${pkgname}.d/init" )
|
||||
sha256sums=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||
sha256sums=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.18/copyparty-1.20.18.tar.gz",
|
||||
"version": "1.20.18",
|
||||
"hash": "sha256-6hNHouMLQmEoKsRgnJuER2ipJe7XF6yK8f+HqKMmUI4="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.20/copyparty-1.20.20.tar.gz",
|
||||
"version": "1.20.20",
|
||||
"hash": "sha256-ox+eSkPlZgh2XhCDUxQHhNUBgcN+cF6c6O2fOJn1ffo="
|
||||
}
|
||||
|
|
@ -1587,6 +1587,7 @@ def add_wopi(ap):
|
|||
ap2.add_argument("--wopi", action="store_true", help="enable integration with office suites using WOPI")
|
||||
ap2.add_argument("--wopi-api", metavar="URL", type=u, default="", help="URL that the WOPI-client should use to communicate with copyparty; default is same as user's webbrowser. Example: [\033[32mhttps://party.example.com/\033[0m]")
|
||||
ap2.add_argument("--wopi-url", metavar="URL", type=u, default="", help="URL to your WOPI client; the host of e.g. Collabora Online. Example: [\033[32mhttps://code.example.com/\033[0m]")
|
||||
ap2.add_argument("--wopi-urls", metavar="HOST=URL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m maps http \033[33mHOST\033[0m copyparty being accessed by to specific WOPI client instance \033[33mURL\033[0m; falls back to \033[33m--wopi-url\033[0m; examples: [\033[32mparty.public.com=https://office.public.com/\033[0m], [\033[32mparty.internal.net:8443=https://office.internal.net:8443/\033[0m]")
|
||||
ap2.add_argument("--wopi-crt", metavar="TXT", type=u, default="", help="if \033[33m--wopi-url\033[0m is selfsigned: path to ca.pem or cert.pem to expect/verify (can be [\033[32mno\033[0m] for full-yolo)")
|
||||
ap2.add_argument("--wopi-crt-icn", action="store_true", help="if \033[33m--wopi-url\033[0m is selfsigned: ignore the CN (server ip/name) in cert")
|
||||
ap2.add_argument("--wopi-ttl", metavar="SEC", type=int, default=1800, help="session lifetime; allow editing for this many seconds (default is 30 min)")
|
||||
|
|
@ -1832,14 +1833,14 @@ def add_thumbnail(ap):
|
|||
# https://github.com/libvips/libvips
|
||||
# https://stackoverflow.com/a/47612661
|
||||
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
||||
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
||||
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,jxl,kra,ora,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
||||
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="3fr,arw,avif,cr2,cr3,crw,dcr,dng,erf,exr,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,jp2,jpeg,jpg,jpx,jxl,k25,kdc,mdc,mef,mrw,nef,nii,nrw,orf,pfm,pgm,png,ppm,raf,raw,rw2,sr2,srf,srw,svg,tif,tiff,webp,x3f", help="image formats to decode using pyvips")
|
||||
ap2.add_argument("--th-r-raw", metavar="T,T", type=u, default="3fr,arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,raf,raw,rw2,sr2,srf,srw,x3f", help="image formats to decode using rawpy (if available) or libraw's dcraw_emu")
|
||||
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,kra,ora,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bcstm,bfstm,brstm,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,m4b,m4r,mdgz,mdxz,mdz,mka,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
||||
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub", help="audio/image formats to decompress before passing to ffmpeg")
|
||||
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub, kra=png.kra, ora=png.ora", help="audio/image formats to decompress before passing to ffmpeg")
|
||||
|
||||
|
||||
def add_transcoding(ap):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 20, 19)
|
||||
VERSION = (1, 20, 20)
|
||||
CODENAME = "sftp is fine too"
|
||||
BUILD_DT = (2026, 7, 27)
|
||||
BUILD_DT = (2026, 8, 4)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
|||
|
|
@ -2660,10 +2660,11 @@ class AuthSrv(object):
|
|||
if head_s and not head_s.endswith("\n"):
|
||||
head_s += "\n"
|
||||
|
||||
zs2 = "Content-Security-Policy: %s\r\n"
|
||||
zs = vol.flags.get("csp_ui", "")
|
||||
csp_ui = "Content-Security-Policy: %s\r\n" % (zs,) if zs else ""
|
||||
csp_ui = zs2 % (zs,) if zs.lower() not in ("", "no") else ""
|
||||
zs = vol.flags.get("csp_dl", "")
|
||||
csp_dl = "Content-Security-Policy: %s\r\n" % (zs,) if zs else ""
|
||||
csp_dl = zs2 % (zs,) if zs.lower() not in ("", "no") else ""
|
||||
|
||||
zs = "X-Content-Type-Options: nosniff\r\n"
|
||||
if "norobots" in vol.flags:
|
||||
|
|
@ -2761,6 +2762,7 @@ class AuthSrv(object):
|
|||
|
||||
for k in drop:
|
||||
t = 'cannot enable [%s] for volume "/%s" because this requires one of the following: e2d / e2ds / e2dsa (either as volflag or global-option)'
|
||||
if not (enshare and vp.startswith(shrs)):
|
||||
self.log(t % (k, vol.vpath), 1)
|
||||
vol.flags.pop(k)
|
||||
|
||||
|
|
|
|||
|
|
@ -1673,14 +1673,18 @@ class HttpCli(object):
|
|||
|
||||
uo_kw["context"] = ctx
|
||||
|
||||
url = self.args.wopi_url.rstrip("/") + "/hosting/discovery"
|
||||
wopi_urls = dict(x.lower().split("=", 1) for x in self.args.wopi_urls or [])
|
||||
url = wopi_urls.get(self.host.lower(), self.args.wopi_url).rstrip("/")
|
||||
url += "/hosting/discovery"
|
||||
buf = urlopen(url, **uo_kw).read()
|
||||
xml = buf.decode("ascii", "replace").lower()
|
||||
enc = self.get_xml_enc(xml)
|
||||
xml = buf.decode(enc, "replace")
|
||||
xroot = parse_xml(xml)
|
||||
ext = vpath.split(".")[-1]
|
||||
url = xroot.find(".//action[@ext='%s'][@urlsrc]" % (ext,)).get("urlsrc")
|
||||
url = xroot.find(
|
||||
".//action[@ext='%s'][@name='edit'][@urlsrc]" % (ext,)
|
||||
).get("urlsrc")
|
||||
if not url.endswith(("?", "&")):
|
||||
url += "&" if "?" in url else "?"
|
||||
url += "WOPISrc="
|
||||
|
|
@ -7441,19 +7445,17 @@ class HttpCli(object):
|
|||
dirs = []
|
||||
files = []
|
||||
ptn_hr = RE_HR
|
||||
use_abs_url = is_opds or (
|
||||
vpath and not is_ls and not is_js and not self.trailing_slash
|
||||
)
|
||||
for fn in ls_names:
|
||||
|
||||
base = ""
|
||||
href = fn
|
||||
if use_abs_url:
|
||||
if is_opds or (vpath and not (is_ls or is_js or self.trailing_slash)):
|
||||
if is_opds:
|
||||
base = self.args.SRS
|
||||
if vpath:
|
||||
base += vpath + "/"
|
||||
else:
|
||||
base = "/" + vpath + "/"
|
||||
|
||||
for fn in ls_names:
|
||||
href = base + fn
|
||||
|
||||
if fn in vfs_virt:
|
||||
|
|
@ -7624,6 +7626,26 @@ class HttpCli(object):
|
|||
(fe["sz"], fe["tags"][".files"]) = hit
|
||||
except:
|
||||
pass # 404 or mojibake
|
||||
if vfs_virt:
|
||||
q = "select sz, nf from ds where rd='' limit 1"
|
||||
try:
|
||||
for fe in [x for x in dirs if x["name"] in vfs_virt]:
|
||||
if ".files" not in fe["tags"]:
|
||||
fe["tags"][".files"] = 0
|
||||
vols = [vn.nodes[fe["name"]]]
|
||||
while vols:
|
||||
vn2 = vols.pop()
|
||||
if self.uname not in vn2.axs.uread:
|
||||
continue
|
||||
vols += list(vn2.nodes.values())
|
||||
if vn2.dbv not in (vn2, None):
|
||||
continue
|
||||
hit = idx.get_cur(vn2).execute(q).fetchone()
|
||||
if hit:
|
||||
fe["sz"] += hit[0]
|
||||
fe["tags"][".files"] += hit[1]
|
||||
except:
|
||||
pass
|
||||
|
||||
taglist = [k for k in lmte if k in tagset]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -194,6 +194,12 @@ def au_unpk(
|
|||
log(t)
|
||||
fi = zf.open(using)
|
||||
|
||||
elif pk == "kra" or pk == "ora":
|
||||
import zipfile
|
||||
|
||||
zf = zipfile.ZipFile(abspath, "r")
|
||||
fi = zf.open("mergedimage.png")
|
||||
|
||||
elif pk == "epub":
|
||||
fi = get_cover_from_epub(log, abspath)
|
||||
assert fi # !rm
|
||||
|
|
|
|||
|
|
@ -2150,7 +2150,8 @@ class MultipartParser(object):
|
|||
boundary = get_boundary(self.headers)
|
||||
if boundary.startswith('"') and boundary.endswith('"'):
|
||||
boundary = boundary[1:-1] # dillo uses quotes
|
||||
self.log("boundary=%r" % (boundary,))
|
||||
if len(boundary) > 72: # rfc-2046 <=70
|
||||
raise Pebkac(400, "boundary 2big: %d" % (len(boundary),))
|
||||
|
||||
# spec says there might be junk before the first boundary,
|
||||
# can't have the leading \r\n if that's not the case
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@
|
|||
|
||||
<h2 id="wfp"><a href="{{ r }}/?h" id="goh">control-panel</a></h2>
|
||||
|
||||
<p id="js_bork">Looks like javascript is broken; see "csp nonce" section in readme</p>
|
||||
|
||||
<a href="#" id="repl">π</a>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ if (1)
|
|||
["ctrl-V", "paste (move/copy) here"],
|
||||
["Y", "download selected"],
|
||||
["F2", "rename selected"],
|
||||
["F4", "update/refresh"],
|
||||
|
||||
"file-list-sel",
|
||||
["space", "toggle file selection"],
|
||||
|
|
@ -6438,6 +6439,9 @@ var ahotkeys = function (e) {
|
|||
if (k == 'F2')
|
||||
return fileman.rename();
|
||||
|
||||
if (k == 'F4')
|
||||
return treectl.goto();
|
||||
|
||||
if (!treectl.hidden && (!sh || !thegrid.en)) {
|
||||
if (kl == 'a')
|
||||
return QS('#twig').click();
|
||||
|
|
@ -7786,6 +7790,7 @@ var treectl = (function () {
|
|||
|
||||
r.hydrate = function () {
|
||||
qsr('#bbsw');
|
||||
qsr('#js_bork');
|
||||
srvinf = ebi('srv_info').innerHTML.slice(6, -7);
|
||||
if (ls0 === null) {
|
||||
r.ls_cb = showfile.addlinks;
|
||||
|
|
@ -8872,7 +8877,7 @@ var msel = (function () {
|
|||
r.evsel = function (e, fun) {
|
||||
ev(e);
|
||||
r.so = r.pr = null;
|
||||
var trs = QSA('#files tbody tr');
|
||||
var trs = QSA('#files tbody tr' + (ebi('unsearch') ? ':not(.srch_hdr)' : ''));
|
||||
for (var a = 0, aa = trs.length; a < aa; a++)
|
||||
clmod(trs[a], 'sel', fun);
|
||||
r.selui();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.chi = {
|
|||
["ctrl-V", "粘贴(移动/复制)到此处"],
|
||||
["Y", "下载选中项"],
|
||||
["F2", "重命名选中项"],
|
||||
["F4", "刷新"], //m
|
||||
|
||||
"文件列表选择",
|
||||
["space", "切换文件选择"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.cze = {
|
|||
["ctrl-V", "vložit (přesunout/kopírovat) zde"],
|
||||
["Y", "stáhnout vybrané"],
|
||||
["F2", "přejmenovat vybrané"],
|
||||
["F4", "obnovit"], //m
|
||||
|
||||
"výběr souborů",
|
||||
["space", "přepnout výběr souboru"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.deu = {
|
|||
["STRG-V", "Zwischenablage hier einfügen"],
|
||||
["Y", "Auswahl herunterladen"],
|
||||
["F2", "Auswahl umbenennen"],
|
||||
["F4", "Aktualisieren"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["LEER", "Dateiauswahl aktivieren"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.epo = {
|
|||
["stir-V", "alglui (movi/kopii) ĉi tien"],
|
||||
["Y", "elŝuti elektitajn"],
|
||||
["F2", "alinomi elektitajn"],
|
||||
["F4", "refreŝigi"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spacoklavo", "baskuli elektadon de dosieroj"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.fin = {
|
|||
["ctrl-V", "siirrä tai kopioi tähän"],
|
||||
["Y", "lataa valitut"],
|
||||
["F2", "uudelleennimeä valitut"],
|
||||
["F4", "päivitä"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "vaihda tiedostonvalintatilaan"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.fra = {
|
|||
["ctrl-V", "coller (déplacer/copier) ici"],
|
||||
["Y", "télécharger la sélection"],
|
||||
["F2", "renomer la sélection"],
|
||||
["F4", "actualiser"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["Espace", "activer la sélection de fichiers"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.grc = {
|
|||
["ctrl-V", "επικόλληση (μετακίνηση/αντιγραφή) εδώ"],
|
||||
["Y", "λήψη επιλεγμένων"],
|
||||
["F2", "μετονομασία επιλεγμένων"],
|
||||
["F4", "ανανέωση"], //m
|
||||
|
||||
"λίστα αρχείων",
|
||||
["space", "εναλλαγή επιλογής αρχείου"],
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Ls.hun = {
|
|||
['ctrl-V', 'beillesztés ide'],
|
||||
['Y', 'kijelöltek letöltése'],
|
||||
['F2', 'átnevezés'],
|
||||
["F4", "frissítés"], //m
|
||||
|
||||
'kijelölés',
|
||||
['space', 'fájl kijelölése'],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.ita = {
|
|||
["ctrl-V", "incolla (sposta/copia) qui"],
|
||||
["Y", "scarica selezionati"],
|
||||
["F2", "rinomina selezionati"],
|
||||
["F4", "aggiorna"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spazio", "alterna selezione file"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.jpn = {
|
|||
["ctrl-V", "ここに貼り付け(移動/コピー)"],
|
||||
["Y", "選択した項目をダウンロード"],
|
||||
["F2", "選択した項目の名前を変更"],
|
||||
["F4", "更新"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "ファイル選択の切り替え"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.kor = {
|
|||
["ctrl-V", "여기에 붙여넣기 (이동/복사)"],
|
||||
["Y", "선택 항목 다운로드"],
|
||||
["F2", "선택 항목 이름 바꾸기"],
|
||||
["F4", "새로 고침"], //m
|
||||
|
||||
"파일 목록 선택",
|
||||
["space", "파일 선택/해제"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.nld = {
|
|||
["ctrl-V", "Hier plakken (verplaatsen/kopieëren)"],
|
||||
["Y", "Download geselecteerde"],
|
||||
["F2", "Hernoem geselecteerde"],
|
||||
["F4", "vernieuwen"], //m
|
||||
|
||||
"bestand-lijst-selectie",
|
||||
["space", "wissel bestand selectie"],
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Ls.nno = {
|
|||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||
["Y", "last ned valde"],
|
||||
["F2", "endre namn på valde"],
|
||||
["F4", "oppdater"],
|
||||
|
||||
"filmarkering",
|
||||
["space", "markér fil"],
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Ls.nor = {
|
|||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||
["Y", "last ned valgte"],
|
||||
["F2", "endre navn på valgte"],
|
||||
["F4", "oppdater"],
|
||||
|
||||
"filmarkering",
|
||||
["space", "marker fil"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.pol = {
|
|||
["ctrl-V", "wklej (przenieś/skopiuj) tutaj"],
|
||||
["Y", "pobierz zaznaczone"],
|
||||
["F2", "zmień nazwę zaznaczonych"],
|
||||
["F4", "odśwież"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spacja", "przełącz zaznaczanie plików"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.por = {
|
|||
["ctrl-V", "colar (mover/copiar) aqui"],
|
||||
["Y", "baixar selecionado"],
|
||||
["F2", "renomear selecionado"],
|
||||
["F4", "atualizar"], //m
|
||||
|
||||
"seleção de lista de arquivos",
|
||||
["espaço", "alternar seleção de arquivo"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.rus = {
|
|||
["ctrl-V", "вставить (переместить/копировать) сюда"],
|
||||
["Y", "скачать выделенное"],
|
||||
["F2", "переименовать выделенное"],
|
||||
["F4", "обновить"], //m
|
||||
|
||||
"выделение файлов",
|
||||
["пробел", "выделить/снять выделение с текущего файла"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.spa = {
|
|||
["ctrl-V", "pegar (mover/copiar) aquí"],
|
||||
["Y", "descargar seleccionados"],
|
||||
["F2", "renombrar seleccionados"],
|
||||
["F4", "actualizar"], //m
|
||||
|
||||
"selección en lista de archivos",
|
||||
["space", "alternar selección de archivo"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.swe = {
|
|||
["ctrl-V", "klistra in (kopiera/flytta) hit"],
|
||||
["Y", "ladda ner urval"],
|
||||
["F2", "byt namn på urval"],
|
||||
["F4", "uppdatera"], //m
|
||||
|
||||
"välja filer",
|
||||
["Blanksteg", "växla val av fil"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.tur = {
|
|||
["ctrl-V", "buraya yapıştır (taşı/kopyala)"],
|
||||
["Y", "seçileni indir"],
|
||||
["F2", "seçileni yeniden adlandır"],
|
||||
["F4", "yenile"], //m
|
||||
|
||||
"dosya yönetimi seçimleri",
|
||||
["boşluk", "seçimi değiştir"],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Ls.ukr = {
|
|||
["ctrl-V", "вставити (перемістити/копіювати) сюди"],
|
||||
["Y", "завантажити вибране"],
|
||||
["F2", "перейменувати вибране"],
|
||||
["F4", "оновити"], //m
|
||||
|
||||
"вибір файлів у списку",
|
||||
["space", "перемкнути вибір файлу"],
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ Ls.vie = {
|
|||
["ctrl-V", "dán (di chuyển/sao chép) tại đây"],
|
||||
["Y", "tải xuống mục đã chọn"],
|
||||
["F2", "đổi tên mục đã chọn"],
|
||||
["F4", "làm mới"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "chuyển đổi chọn tệp"],
|
||||
|
|
|
|||
|
|
@ -998,6 +998,14 @@ function up2k_init(subtle) {
|
|||
});
|
||||
}
|
||||
|
||||
function unqueue_up(t) {
|
||||
t.postlist = [];
|
||||
var arr = st.todo.upload;
|
||||
for (var a = arr.length - 1; a >= 0; a--)
|
||||
if (arr[a].nfile == t.n)
|
||||
arr.splice(a, 1);
|
||||
}
|
||||
|
||||
var pvis = new U2pvis("bz", '#u2cards', uc, st),
|
||||
donut = new Donut(uc, st);
|
||||
|
||||
|
|
@ -2512,6 +2520,7 @@ function up2k_init(subtle) {
|
|||
keepalive = t.keepalive,
|
||||
me = Date.now();
|
||||
|
||||
while (apop(st.todo.handshake, t));
|
||||
if (t.done)
|
||||
return console.log('done; skip hs', t.name, t);
|
||||
|
||||
|
|
@ -2630,7 +2639,7 @@ function up2k_init(subtle) {
|
|||
cbd.push(a == cdr_idx ? cdr_sz : chunksize);
|
||||
}
|
||||
|
||||
t.postlist = [];
|
||||
unqueue_up(t);
|
||||
t.wark = response.wark;
|
||||
var missing = response.hash;
|
||||
for (var a = 0; a < missing.length; a++) {
|
||||
|
|
@ -2919,8 +2928,7 @@ function up2k_init(subtle) {
|
|||
var txt = unpre((xhr.response && xhr.response.err) || xhr.responseText);
|
||||
if (txt.indexOf('upload blocked by x') + 1) {
|
||||
apop(st.busy.upload, upt);
|
||||
for (var a = pcar; a <= pcdr; a++)
|
||||
apop(t.postlist, a);
|
||||
unqueue_up(t);
|
||||
pvis.seth(t.n, 1, "ERROR");
|
||||
pvis.seth(t.n, 2, txt.split(/\n/)[0]);
|
||||
pvis.move(t.n, 'ng');
|
||||
|
|
@ -2943,8 +2951,10 @@ function up2k_init(subtle) {
|
|||
st.etac.u++;
|
||||
st.etac.t++;
|
||||
}
|
||||
else if (txt.indexOf('already got that') + 1 ||
|
||||
txt.indexOf('already being written') + 1) {
|
||||
else if (txt.indexOf('already got that') + 1) {
|
||||
unqueue_up(t);
|
||||
}
|
||||
else if (txt.indexOf('already being written') + 1) {
|
||||
t.nojoin = t.nojoin || t.postlist.length;
|
||||
console.log("ignoring dupe-segment with backoff", t.nojoin, t.name, t);
|
||||
if (!toast.visible && st.todo.upload.length < 4)
|
||||
|
|
@ -2952,6 +2962,10 @@ function up2k_init(subtle) {
|
|||
}
|
||||
else {
|
||||
xhrchk(xhr, L.u_cuerr2.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), "404, target folder not found (???)", "warn", t);
|
||||
if (txt.indexOf('unknown wark') == 0) {
|
||||
t.cooldown = t.coolmul = 0;
|
||||
unqueue_up(t);
|
||||
}
|
||||
chill(t);
|
||||
}
|
||||
orz2(xhr);
|
||||
|
|
@ -2962,9 +2976,11 @@ function up2k_init(subtle) {
|
|||
apop(t.postlist, a);
|
||||
if (!t.postlist.length) {
|
||||
t.t_uploaded = Date.now();
|
||||
if (!has(st.busy.handshake, t)) {
|
||||
pvis.seth(t.n, 1, 'verifying');
|
||||
st.todo.handshake.unshift(t);
|
||||
}
|
||||
}
|
||||
tasker();
|
||||
}
|
||||
function do_send() {
|
||||
|
|
@ -2998,6 +3014,7 @@ function up2k_init(subtle) {
|
|||
if (!toast.visible)
|
||||
toast.warn(9.98, L.u_cuerr.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), t);
|
||||
|
||||
unqueue_up(t); // maybe unknown wark (ff drops undrained rsp)
|
||||
t.nojoin = t.nojoin || t.postlist.length; // maybe rproxy postsize limit
|
||||
console.log('chunkpit onerror,', t.name, t);
|
||||
orz2(xhr);
|
||||
|
|
|
|||
|
|
@ -1199,8 +1199,10 @@ function has(haystack, needle) {
|
|||
|
||||
function apop(arr, v) {
|
||||
var ofs = arr.indexOf(v);
|
||||
if (ofs !== -1)
|
||||
if (ofs !== -1) {
|
||||
arr.splice(ofs, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1466,7 +1468,7 @@ function Debounce(delay) {
|
|||
};
|
||||
|
||||
r.rm = function (fun) {
|
||||
apop(r.q, fun);
|
||||
while (apop(r.q, fun));
|
||||
};
|
||||
|
||||
r.run = function () {
|
||||
|
|
@ -1526,7 +1528,7 @@ var timer = (function () {
|
|||
};
|
||||
|
||||
r.rm = function (fun) {
|
||||
apop(r.q, fun);
|
||||
while (apop(r.q, fun));
|
||||
};
|
||||
|
||||
var doevents = function () {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,52 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0727-2202 `v1.20.19` SECURITY: fix FTP upload outside volume
|
||||
|
||||
## ⚠️ ATTN: this release fixes a vulnerability in FTP and FTPS (not SFTP)
|
||||
|
||||
the FTP-server (default-disabled) would allow uploading to any folder that the copyparty process had permission to write to, but with certain limitations; see [GHSA-phv8-wgjp-g4p9](https://github.com/9001/copyparty/security/advisories/GHSA-phv8-wgjp-g4p9)
|
||||
|
||||
## recent important news
|
||||
|
||||
* [v1.20.19 (2026-07-27)](https://github.com/9001/copyparty/releases/tag/v1.20.19) fixed an FTP-server vuln (upload outside defined volumes)
|
||||
* [v1.20.17 (2026-07-06)](https://github.com/9001/copyparty/releases/tag/v1.20.17) fixed a vuln when a volume has both filekeys and dirkeys enabled
|
||||
* [v1.20.17 (2026-07-06)](https://github.com/9001/copyparty/releases/tag/v1.20.17) introduced csp nonces, possibly breaking some javascript-based plugins
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #1495 wopi integration -- edit office documents in the web-ui (thx @brandon-doornbos!) d57bb0c7 10db4236
|
||||
* currently only works with collabora online as wopi client #1574 and there's no docs/examples #1575
|
||||
* thumbnails: use embedded cover-image in videos when available 59524018
|
||||
* thumbnails: folder-thumbs can be disabled with `th-covers: no` (volflag or global) 856fada3
|
||||
* #1555 macos: add [--srch-nfkc](https://copyparty.eu/cli/#g-srch-nfkc) to fix searching for filenames / paths in CJK languages a7c99094
|
||||
* reduces search performance to around 30% when enabled
|
||||
* hooks: `xbr` / `xar` did not include old/new abspath as parameters; now they do c122e103
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* ftp: fix [GHSA-phv8-wgjp-g4p9](https://github.com/9001/copyparty/security/advisories/GHSA-phv8-wgjp-g4p9) b331bb1c
|
||||
* #1563 moving files between volumes could fail depending on OS and underlying filesystem c70dc7ac
|
||||
* fix drag-drop uploading in certain glitchy KDE environments (thx @tilse!) daf144af
|
||||
* hooks: `xiu` crashed if the fork-flag was set (thx @stackxp!) aa862353
|
||||
* hooks: `xau` without json-flag would be given the wark (file hash) instead of the abspath bae77b90
|
||||
* shares: fix markdown-viewer (`?v`) inside shares 6a9437b7
|
||||
* ftp: fix logging from `xbu` hooks 9912a951
|
||||
* fix slow boot if a volume had lots of files in its toplevel folder cdb474c8
|
||||
* python2.7: fix multithreaded file-hashing 0f2040c3
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* #1556 the libvips thumbnailer was demoted to last-fallback due to frequently using excessive amounts of ram ed0be42a
|
||||
* if [-lo](https://copyparty.eu/cli/#g-lo) points to an existing file, it will now be appended to instead of overwritten depending on [--rlo](https://copyparty.eu/cli/#g-help-rlo) b6abc33f
|
||||
* #1530 nixos: the nix package now uses `ffmpeg-headless` instead of `ffmpeg-full` (thx @nyakase!) fface524
|
||||
* slightly longer session cookie (was 20, now 24 chars) 537a99df
|
||||
* Windows-specific:
|
||||
* add a warning regarding the risks of DLL-hijacking when relevant bb40804f
|
||||
* fix some trivial PATH-related footguns cea97ac6
|
||||
* faster creation of sparse files bc45299b
|
||||
* fix detection of filesystem characteristics 6226858b
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0709-2254 `v1.20.18` bwrap off
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 555 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
|
@ -71,6 +71,7 @@ Ls.hmn = {
|
|||
["ctrl-V", "paste (move/copy) here"],
|
||||
["Y", "download selected"],
|
||||
["F2", "rename selected"],
|
||||
["F4", "update/refresh"],
|
||||
|
||||
"file-list-sel",
|
||||
["space", "toggle file selection"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue