From d8dfc4ccb2f21508ad753871e0577c1b1df478f7 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 31 Jan 2023 18:53:38 +0000 Subject: [PATCH] support davfs2 LOCK (uploads) + misc windows support + logue filtering --- copyparty/ftpd.py | 19 +++++++++++++++++-- copyparty/httpcli.py | 26 +++++++++++++++++--------- copyparty/ssdp.py | 2 +- copyparty/szip.py | 2 +- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index 38b2a2ed..8adf6b40 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -13,9 +13,18 @@ from pyftpdlib.filesystems import AbstractedFS, FilesystemError from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer -from .__init__ import PY2, TYPE_CHECKING, E +from .__init__ import ANYWIN, PY2, TYPE_CHECKING, E from .bos import bos -from .util import Daemon, Pebkac, exclude_dotfiles, fsenc, ipnorm +from .util import ( + Daemon, + Pebkac, + exclude_dotfiles, + fsenc, + ipnorm, + relchk, + sanitize_fn, + vjoin, +) try: from pyftpdlib.ioloop import IOLoop @@ -125,6 +134,12 @@ class FtpFs(AbstractedFS): ) -> str: try: vpath = vpath.replace("\\", "/").lstrip("/") + rd, fn = os.path.split(vpath) + if ANYWIN and not relchk(rd): + raise FilesystemError("unsupported characters in filepath") + + fn = sanitize_fn(fn or "", "", [".prologue.html", ".epilogue.html"]) + vpath = vjoin(rd, fn) vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d) if not vfs.realpath: raise FilesystemError("no filesystem mounted at this path") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 9f500378..a90b80ab 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -51,6 +51,7 @@ from .util import ( guess_mime, gzip_orig_sz, hashcopy, + hidedir, html_bescape, html_escape, humansize, @@ -64,7 +65,6 @@ from .util import ( relchk, ren_open, runhook, - hidedir, s3enc, sanitize_fn, sendfile_kern, @@ -1057,11 +1057,14 @@ class HttpCli(object): lk = parse_xml(txt) assert lk.tag == "{DAV:}lockinfo" - if not lk.find(r"./{DAV:}depth"): - lk.append(mktnod("D:depth", "infinity")) + token = str(uuid.uuid4()) - lk.append(mkenod("D:timeout", mktnod("D:href", "Second-3310"))) - lk.append(mkenod("D:locktoken", mktnod("D:href", uuid.uuid4().urn))) + if not lk.find(r"./{DAV:}depth"): + depth = self.headers.get("depth", "infinity") + lk.append(mktnod("D:depth", depth)) + + lk.append(mktnod("D:timeout", "Second-3310")) + lk.append(mkenod("D:locktoken", mktnod("D:href", token))) lk.append( mkenod("D:lockroot", mktnod("D:href", quotep(self.args.SRS + self.vpath))) ) @@ -1074,11 +1077,13 @@ class HttpCli(object): ret = '\n'.format(uenc) ret += ET.tostring(xroot).decode("utf-8") + rc = 200 if self.can_write and not bos.path.isfile(abspath): with open(fsenc(abspath), "wb") as _: - pass + rc = 201 - self.reply(ret.encode(enc, "replace"), 200, "text/xml; charset=" + enc) + self.out_headers["Lock-Token"] = "<{}>".format(token) + self.reply(ret.encode(enc, "replace"), rc, "text/xml; charset=" + enc) return True def handle_unlock(self) -> bool: @@ -1388,8 +1393,11 @@ class HttpCli(object): params.update(open_ka) assert fn - if rnd and not self.args.nw: - fn = self.rand_name(fdir, fn, rnd) + if not self.args.nw: + if rnd: + fn = self.rand_name(fdir, fn, rnd) + + fn = sanitize_fn(fn or "", "", [".prologue.html", ".epilogue.html"]) path = os.path.join(fdir, fn) diff --git a/copyparty/ssdp.py b/copyparty/ssdp.py index ef23f156..1346802c 100644 --- a/copyparty/ssdp.py +++ b/copyparty/ssdp.py @@ -8,7 +8,7 @@ from email.utils import formatdate from .__init__ import TYPE_CHECKING from .multicast import MC_Sck, MCast -from .util import CachedSet, min_ex, html_escape +from .util import CachedSet, html_escape, min_ex if TYPE_CHECKING: from .broker_util import BrokerCli diff --git a/copyparty/szip.py b/copyparty/szip.py index 86615cf7..ff9f8337 100644 --- a/copyparty/szip.py +++ b/copyparty/szip.py @@ -2,8 +2,8 @@ from __future__ import print_function, unicode_literals import calendar -import time import stat +import time import zlib from .bos import bos