mirror of
https://github.com/9001/copyparty.git
synced 2025-11-02 04:53:15 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7770904e6 | ||
|
|
1690889ed8 | ||
|
|
842817d9e3 | ||
|
|
5fc04152bd | ||
|
|
1be85bdb26 | ||
|
|
2eafaa88a2 | ||
|
|
900cc463c3 | ||
|
|
97b999c463 | ||
|
|
a7cef91b8b | ||
|
|
a4a112c0ee |
@@ -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}"
|
||||
|
||||
@@ -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="
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 9, 22)
|
||||
VERSION = (1, 9, 25)
|
||||
CODENAME = "prometheable"
|
||||
BUILD_DT = (2023, 12, 1)
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] = {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user