Compare commits

..

10 Commits

Author SHA1 Message Date
ed
c7770904e6 v1.9.25 2023-12-01 23:26:16 +00:00
ed
1690889ed8 remember scroll position when leaving the textfile viewer 2023-12-01 23:15:48 +00:00
ed
842817d9e3 improve handling of malicious clients;
* start banning malicious clients according to --ban-422
* reply with a blank 500 to stop firefox from retrying like 20 times
* allow Cc's in a few specific URL params (filenames, dirnames)
2023-12-01 23:08:16 +00:00
ed
5fc04152bd also handle NumpadEnter 2023-12-01 21:10:51 +00:00
ed
1be85bdb26 fix modal focus even more (now works on phones too) 2023-12-01 21:02:05 +00:00
ed
2eafaa88a2 update pkgs to 1.9.24 2023-12-01 02:16:24 +00:00
ed
900cc463c3 v1.9.24 2023-12-01 02:10:20 +00:00
ed
97b999c463 update pkgs to 1.9.23 2023-12-01 01:54:23 +00:00
ed
a7cef91b8b v1.9.23 2023-12-01 00:39:49 +00:00
ed
a4a112c0ee update pkgs to 1.9.22 2023-12-01 01:14:18 +00:00
12 changed files with 137 additions and 39 deletions

View File

@@ -1,6 +1,6 @@
# Maintainer: icxes <dev.null@need.moe>
pkgname=copyparty
pkgver="1.9.21"
pkgver="1.9.24"
pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, 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=("1ef2685aa8804ebab64c05e3e9d95ace47279f497284c3783e668d096490a730")
sha256sums=("759fbc7b3063c5e39c49a9524200568ca30c664c8323b1e396d5e58378dc8b80")
build() {
cd "${srcdir}/${pkgname}-${pkgver}"

View File

@@ -1,5 +1,5 @@
{
"url": "https://github.com/9001/copyparty/releases/download/v1.9.21/copyparty-sfx.py",
"version": "1.9.21",
"hash": "sha256-UI1gvNUUNIcBzTj/OTr7/kskUVh/x3D+xtZHpvUU2AI="
"url": "https://github.com/9001/copyparty/releases/download/v1.9.24/copyparty-sfx.py",
"version": "1.9.24",
"hash": "sha256-GuKeNrH9rcsUlM7c/cQ0QKlpteu+cGfGmzxRp4ACclw="
}

View File

@@ -1,6 +1,6 @@
# coding: utf-8
VERSION = (1, 9, 22)
VERSION = (1, 9, 25)
CODENAME = "prometheable"
BUILD_DT = (2023, 12, 1)

View File

@@ -957,7 +957,6 @@ class AuthSrv(object):
err = ""
try:
self._l(ln, 5, "volume access config:")
assert vp
sk, sv = ln.split(":")
if re.sub("[rwmdgGha]", "", sk) or not sk:
err = "invalid accs permissions list; "
@@ -975,7 +974,6 @@ class AuthSrv(object):
err = ""
try:
self._l(ln, 6, "volume-specific config:")
assert vp
zd = split_cfg_ln(ln)
fstr = ""
for sk, sv in zd.items():

View File

@@ -135,7 +135,7 @@ class HttpCli(object):
self.headers: dict[str, str] = {}
self.mode = " "
self.req = " "
self.http_ver = " "
self.http_ver = ""
self.hint = ""
self.host = " "
self.ua = " "
@@ -238,6 +238,7 @@ class HttpCli(object):
if self.args.ipa_re and not self.args.ipa_re.match(self.conn.addr[0]):
self.log("client rejected (--ipa)", 3)
self.terse_reply(b"", 500)
return False
try:
@@ -372,22 +373,33 @@ class HttpCli(object):
self.trailing_slash = vpath.endswith("/")
vpath = undot(vpath)
zs = unquotep(arglist)
m = self.conn.hsrv.ptn_cc.search(zs)
if m:
hit = zs[m.span()[0] :]
t = "malicious user; Cc in query [{}] => [{!r}]"
self.log(t.format(self.req, hit), 1)
return False
ptn = self.conn.hsrv.ptn_cc
for k in arglist.split("&"):
if "=" in k:
k, zs = k.split("=", 1)
# x-www-form-urlencoded (url query part) uses
# either + or %20 for 0x20 so handle both
uparam[k.lower()] = unquotep(zs.strip().replace("+", " "))
sv = unquotep(zs.strip().replace("+", " "))
else:
uparam[k.lower()] = ""
sv = ""
k = k.lower()
uparam[k] = sv
if k in ("doc", "move", "tree"):
continue
zs = "%s=%s" % (k, sv)
m = ptn.search(zs)
if not m:
continue
hit = zs[m.span()[0] :]
t = "malicious user; Cc in query [{}] => [{!r}]"
self.log(t.format(self.req, hit), 1)
self.cbonk(self.conn.hsrv.gmal, self.req, "cc_q", "Cc in query")
self.terse_reply(b"", 500)
return False
if self.is_vproxied:
if vpath.startswith(self.args.R):
@@ -426,7 +438,7 @@ class HttpCli(object):
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
self.log("invalid relpath [{}]".format(self.vpath))
self.cbonk(self.conn.hsrv.g422, self.vpath, "bad_vp", "invalid relpaths")
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
return self.tx_404() and self.keepalive
zso = self.headers.get("authorization")
@@ -542,6 +554,7 @@ class HttpCli(object):
try:
if pex.code == 999:
self.terse_reply(b"", 500)
return False
post = self.mode in ["POST", "PUT"] or "content-length" in self.headers
@@ -626,8 +639,7 @@ class HttpCli(object):
return False
self.log("banned for {:.0f} sec".format(rt), 6)
zb = b"HTTP/1.0 403 Forbidden\r\n\r\nthank you for playing"
self.s.sendall(zb)
self.terse_reply(b"thank you for playing", 403)
return True
def permit_caching(self) -> None:
@@ -681,6 +693,7 @@ class HttpCli(object):
hit = zs[m.span()[0] :]
t = "malicious user; Cc in out-hdr {!r} => [{!r}]"
self.log(t.format(zs, hit), 1)
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
raise Pebkac(999)
try:
@@ -757,6 +770,19 @@ class HttpCli(object):
self.log(body.rstrip())
self.reply(body.encode("utf-8") + b"\r\n", *list(args), **kwargs)
def terse_reply(self, body: bytes, status: int = 200) -> None:
self.keepalive = False
lines = [
"%s %s %s" % (self.http_ver or "HTTP/1.1", status, HTTPCODE[status]),
"Connection: Close",
]
if body:
lines.append("Content-Length: " + unicode(len(body)))
self.s.sendall("\r\n".join(lines).encode("utf-8") + b"\r\n\r\n" + body)
def urlq(self, add: dict[str, str], rm: list[str]) -> str:
"""
generates url query based on uparam (b, pw, all others)
@@ -926,6 +952,7 @@ class HttpCli(object):
if not static_path.startswith(path_base):
t = "malicious user; attempted path traversal [{}] => [{}]"
self.log(t.format(self.vpath, static_path), 1)
self.cbonk(self.conn.hsrv.gmal, self.req, "trav", "path traversal")
self.tx_404()
return False

View File

@@ -109,6 +109,7 @@ class HttpSrv(object):
self.g404 = Garda(self.args.ban_404)
self.g403 = Garda(self.args.ban_403)
self.g422 = Garda(self.args.ban_422, False)
self.gmal = Garda(self.args.ban_422)
self.gurl = Garda(self.args.ban_url)
self.bans: dict[str, int] = {}
self.aclose: dict[str, int] = {}

View File

@@ -138,7 +138,8 @@ class SvcHub(object):
self.gpwd = Garda(self.args.ban_pw)
self.g404 = Garda(self.args.ban_404)
self.g403 = Garda(self.args.ban_403)
self.g422 = Garda(self.args.ban_422)
self.g422 = Garda(self.args.ban_422, False)
self.gmal = Garda(self.args.ban_422)
self.gurl = Garda(self.args.ban_url)
self.log_div = 10 ** (6 - args.log_tdec)

View File

@@ -3656,7 +3656,7 @@ var fileman = (function () {
(function (a) {
f[a].inew.onkeydown = function (e) {
rn_ok(a, true);
if (e.key == 'Enter')
if (e.key.endsWith('Enter'))
return rn_apply();
};
QS('.rn_dec' + k).onclick = function (e) {
@@ -3751,7 +3751,7 @@ var fileman = (function () {
if (e.key == 'Escape')
return rn_cancel();
if (e.key == 'Enter')
if (e.key.endsWith('Enter'))
return rn_apply();
};
@@ -4462,6 +4462,7 @@ var thegrid = (function () {
filecols.uivis();
aligngriditems();
restore_scroll();
};
r.setdirty = function () {
@@ -4999,7 +5000,7 @@ document.onkeydown = function (e) {
}
}
if (k == 'Enter' && ae && (ae.onclick || ae.hasAttribute('tabIndex')))
if (k.endsWith('Enter') && ae && (ae.onclick || ae.hasAttribute('tabIndex')))
return ev(e) && ae.click() || true;
if (aet && aet != 'a' && aet != 'tr' && aet != 'pre')
@@ -5197,7 +5198,7 @@ document.onkeydown = function (e) {
}
function ev_search_keydown(e) {
if (e.key == 'Enter')
if (e.key.endsWith('Enter'))
do_search();
}
@@ -7767,10 +7768,36 @@ ebi('path').onclick = function (e) {
};
var scroll_y = -1;
var scroll_vp = '\n';
var scroll_obj = null;
function persist_scroll() {
var obj = scroll_obj;
if (!obj) {
var o1 = document.getElementsByTagName('html')[0];
var o2 = document.body;
obj = o1.scrollTop > o2.scrollTop ? o1 : o2;
}
var y = obj.scrollTop;
if (y > 0)
scroll_obj = obj;
scroll_y = y;
scroll_vp = get_evpath();
}
function restore_scroll() {
if (get_evpath() == scroll_vp && scroll_obj && scroll_obj.scrollTop < 1)
scroll_obj.scrollTop = scroll_y;
}
ebi('files').onclick = ebi('docul').onclick = function (e) {
if (!treectl.csel && e && (ctrl(e) || e.shiftKey))
return true;
if (!showfile.active())
persist_scroll();
var tgt = e.target.closest('a[id]');
if (tgt && tgt.getAttribute('id').indexOf('f-') === 0 && tgt.textContent.endsWith('/')) {
var el = treectl.find(tgt.textContent.slice(0, -1));

View File

@@ -933,8 +933,10 @@ var set_lno = (function () {
var keydown = function (ev) {
if (!ev && window.event) {
ev = window.event;
toast.warn(10, 'hello from fallback code ;_;\ncheck console trace');
console.error('using window.event');
if (localStorage.dev_fbw == 1) {
toast.warn(10, 'hello from fallback code ;_;\ncheck console trace');
console.error('using window.event');
}
}
var kc = ev.code || ev.keyCode || ev.which,
editing = document.activeElement == dom_src;
@@ -1007,7 +1009,7 @@ var set_lno = (function () {
md_home(ev.shiftKey);
return false;
}
if (!ev.shiftKey && (ev.code == "Enter" || kc == 13)) {
if (!ev.shiftKey && (ev.code.endsWith("Enter") || kc == 13)) {
return md_newline();
}
if (!ev.shiftKey && kc == 8) {

View File

@@ -279,8 +279,10 @@ function anymod(e, shift_ok) {
function ev(e) {
if (!e && window.event) {
e = window.event;
toast.warn(10, 'hello from fallback code ;_;\ncheck console trace');
console.error('using window.event');
if (localStorage.dev_fbw == 1) {
toast.warn(10, 'hello from fallback code ;_;\ncheck console trace');
console.error('using window.event');
}
}
if (!e)
return;
@@ -1480,6 +1482,7 @@ var modal = (function () {
a.onclick = ng;
a = ebi('modal-ok');
a.addEventListener('blur', onblur);
a.onclick = ok;
var inp = ebi('modali');
@@ -1498,6 +1501,10 @@ var modal = (function () {
r.hide = function () {
timer.rm(onfocus);
try {
ebi('modal-ok').removeEventListener('blur', onblur);
}
catch (ex) { }
document.removeEventListener('selectionchange', onselch);
document.removeEventListener('focus', onfocus);
document.removeEventListener('keydown', onkey);
@@ -1520,24 +1527,35 @@ var modal = (function () {
cb_ng(null);
}
var onselch = function (e) {
r.nofocus = 30;
var onselch = function () {
try {
if (window.getSelection() + '')
r.nofocus = 15;
}
catch (ex) { }
};
var onblur = function () {
r.nofocus = 3;
};
var onfocus = function (e) {
if (MOBILE)
return;
var ctr = ebi('modalc');
if (!ctr || !ctr.contains || !document.activeElement || ctr.contains(document.activeElement))
return;
setTimeout(function () {
if (--r.nofocus > 0)
if (--r.nofocus >= 0)
return;
if (ctr = ebi('modal-ok'))
ctr.focus();
}, 20);
ev(e);
}
};
var onkey = function (e) {
var k = e.code,
@@ -1548,7 +1566,7 @@ var modal = (function () {
if (k == 'Space' && ae && (ae === eok || ae === eng))
k = 'Enter';
if (k == 'Enter') {
if (k.endsWith('Enter')) {
if (ae && ae == eng)
return ng(e);

View File

@@ -1,3 +1,27 @@
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2023-1201-0210 `v1.9.24` header auth
## new features
* initial work on #62 (support identity providers, oauth/SSO/...); see [readme](https://github.com/9001/copyparty#identity-providers)
* only authentication so far; no authorization yet, and users must exist in the copyparty config with bogus passwords
* new option `--ipa` rejects connections from clients outside of a given allowlist of IP prefixes
* environment variables can be used almost everywhere that takes a filesystem path; should make it way more comfy to write configs for docker / systemd
* #59 added a basic [docker-compose yaml](https://github.com/9001/copyparty/blob/hovudstraum/docs/examples/docker/basic-docker-compose) and an example config
* probably much room for improvement on everything docker still
## bugfixes
* the nftables-based port-forwarding in the [systemd example](https://github.com/9001/copyparty/tree/hovudstraum/contrib/systemd) was buggy; replaced with CAP_NET_BIND_SERVICE
* palemoon-specific js crash if a text selection was dragged
* text selection in messageboxes was jank
## other changes
* improved [systemd example](https://github.com/9001/copyparty/tree/hovudstraum/contrib/systemd) with hardening and a better example config
* logfiles are flushed for every line written; can be disabled with `--no-logflush` for ~3% more performance best-case
* iphones probably won't broadcast cover-art to car stereos over bluetooth anymore since the thingamajig in iOS that's in charge of that doesn't have cookie-access, and strapping in the auth is too funky so let's stop doing that b7723ac245b8b3e38d6410891ef1aa92d4772114
* can be remedied by enabling filekeys and granting unauthenticated people access that way, but that's too much effort for anyone to bother with I'm sure
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2023-1125-1417 `v1.9.21` in a bind

View File

@@ -115,7 +115,7 @@ class Cfg(Namespace):
ex = "dotpart no_rescan no_sendfile no_voldump plain_ip"
ka.update(**{k: True for k in ex.split()})
ex = "ah_cli ah_gen css_browser hist js_browser no_forget no_hash no_idx nonsus_urls"
ex = "ah_cli ah_gen css_browser hist ipa_re js_browser no_forget no_hash no_idx nonsus_urls"
ka.update(**{k: None for k in ex.split()})
ex = "s_thead s_tbody th_convt"
@@ -124,7 +124,7 @@ class Cfg(Namespace):
ex = "df loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp theme themes turbo"
ka.update(**{k: 0 for k in ex.split()})
ex = "ah_alg bname doctitle favico html_head lg_sbf log_fk md_sbf name textfiles unlist vname R RS SR"
ex = "ah_alg bname doctitle favico hdr_au_usr html_head lg_sbf log_fk md_sbf name textfiles unlist vname R RS SR"
ka.update(**{k: "" for k in ex.split()})
ex = "on403 on404 xad xar xau xban xbd xbr xbu xiu xm"