From 835f8a20e6ebf602ff96672ab18d58445c52800f Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 23 Oct 2022 23:37:32 +0200 Subject: [PATCH] default-enable webdav --- README.md | 13 ++++++++----- copyparty/__main__.py | 12 +++++++++--- copyparty/authsrv.py | 9 +++++++-- copyparty/httpcli.py | 14 +++++++------- copyparty/svchub.py | 2 ++ copyparty/util.py | 4 +++- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9805e6bc..22b448bb 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ try the **[read-only demo server](https://a.ocv.me/pub/demo/)** đŸ‘€ running fro * [server config](#server-config) - using arguments or config files, or a mix of both * [qr-code](#qr-code) - print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/194728533-6f00849b-c6ac-43c6-9359-83e454d11e00.png) for quick access * [ftp server](#ftp-server) - an FTP server can be started using `--ftp 3921` - * [webdav server](#webdav-server) - enable with `--dav` + * [webdav server](#webdav-server) - with read-write support * [smb server](#smb-server) - unsafe, not recommended for wan * [file indexing](#file-indexing) - enables dedup and music search ++ * [exclude-patterns](#exclude-patterns) - to save some time @@ -715,7 +715,7 @@ an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit T ## webdav server -enable with `--dav`, supports winxp, win7/8/10 +with read-write support, supports winxp, win7/8/10, macos, nautilus/gvfs general usage: * login with any username + your password, or put your password in the username field and leave password empty @@ -730,10 +730,11 @@ on windows (xp or later), disable wpad for performance: * control panel -> [network and internet] -> [internet options] -> [connections] tab -> [lan settings] -> automatically detect settings: Nope known client bugs: -* win7/8/10 doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password +* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password * or just type your password into the username field instead to get around it entirely -* win7 cannot access servers which require authentication unless you use https or [enable basic auth](./contrib/webdav-basicauth.reg) for http -* win7 cannot download files larger than 47.6 MiB by default; [registry fix](./contrib/webdav-unlimit.bat) to allow files up to 4 GiB (actual absolute max on windows) +* win7+ cannot access servers which require authentication unless you use https or [enable basic auth](./contrib/webdav-basicauth.reg) for http +* win7+ cannot download files larger than 47.6 MiB by default; [registry fix](./contrib/webdav-unlimit.bat) to allow files up to 4 GiB (actual absolute max on windows) +* windows cannot access folders which contain filenames with invalid unicode or forbidden characters (`<>:"/\|?*`), or names ending with `.` * winxp cannot show unicode characters outside of *some range* * latin-1 is fine, hiragana is not (not even as shift-jis on japanese xp) @@ -1197,12 +1198,14 @@ some notes on hardening * `--unpost 0`, `--no-del`, `--no-mv` disables all move/delete support * `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance * however note if you edit one file it will also affect the other copies + * `--dav-nr` disables recursive webdav directory listings (cpu-intensive) * `--vague-401` returns a "404 not found" instead of "401 unauthorized" which is a common enterprise meme * `--ban-404=50,60,1440` ban client for 1440min (24h) if they hit 50 404's in 60min * **NB:** will ban anyone who enables up2k turbo * `--nih` removes the server hostname from directory listings * option `-sss` is a shortcut for the above plus: + * `--no-dav` disables webdav support * `-lo cpp-%Y-%m%d-%H%M%S.txt.xz` enables logging to disk * `-ls **,*,ln,p,r` does a scan on startup for any dangerous symlinks diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 5fece2c9..001032cd 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -29,6 +29,7 @@ from .util import ( JINJA_VER, PYFTPD_VER, SQLITE_VER, + UNPLICATIONS, align_tab, ansi_re, min_ex, @@ -633,7 +634,6 @@ def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Names ap2.add_argument("--ftp-pr", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example \033[32m12000-13000") ap2 = ap.add_argument_group('WebDAV options') - ap2.add_argument("--dav", action="store_true", help="enable webdav with limited write-support (most clients will fail to overwrite files)") ap2.add_argument("--daw", action="store_true", help="enable full write support. \033[1;31mNB!\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)") ap2.add_argument("--dav-nr", action="store_true", help="reject depth:infinite requests (recursive file listing); breaks spec compliance and some clients, which might be a good thing since depth:infinite is extremely server-heavy") ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)") @@ -648,6 +648,7 @@ def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Names ap2 = ap.add_argument_group('opt-outs') ap2.add_argument("-nw", action="store_true", help="never write anything to disk (debug/benchmark)") ap2.add_argument("--keep-qem", action="store_true", help="do not disable quick-edit-mode on windows (it is disabled to avoid accidental text selection which will deadlock copyparty)") + ap2.add_argument("--no-dav", action="store_true", help="disable webdav support") ap2.add_argument("--no-del", action="store_true", help="disable delete operations") ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations") ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI") @@ -657,8 +658,8 @@ def run_argparse(argv: list[str], formatter: Any, retry: bool) -> argparse.Names ap2 = ap.add_argument_group('safety options') ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js") - ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --no-dot-mv --no-dot-ren --unpost=0 --no-del --no-mv --hardlink --vague-403 --ban-404=50,60,1440 -nih") - ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r") + ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --no-dot-mv --no-dot-ren --unpost=0 --no-del --no-mv --hardlink --dav-nr --vague-403 --ban-404=50,60,1440 -nih") + ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r") ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m; example [\033[32m**,*,ln,p,r\033[0m]") ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt; used to generate unpredictable internal identifiers for uploads -- doesn't really matter") ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files -- this one DOES matter") @@ -919,6 +920,11 @@ def main(argv: Optional[list[str]] = None) -> None: if getattr(al, k1): setattr(al, k2, True) + # propagate unplications + for k1, k2 in UNPLICATIONS: + if getattr(al, k1): + setattr(al, k2, False) + al.i = al.i.split(",") try: if "-" in al.p: diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 01562bce..3c432966 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -18,6 +18,7 @@ from .util import ( IMPLICATIONS, META_NOBOTS, SQLITE_VER, + UNPLICATIONS, Pebkac, absreal, fsenc, @@ -1119,6 +1120,10 @@ class AuthSrv(object): if k1 in vol.flags: vol.flags[k2] = True + for k1, k2 in UNPLICATIONS: + if k1 in vol.flags: + vol.flags[k2] = False + # default tag cfgs if unset if "mte" not in vol.flags: vol.flags["mte"] = self.args.mte @@ -1222,8 +1227,8 @@ class AuthSrv(object): vol.flags["daw"] = True have_daw = True - if have_daw and not self.args.dav: - t = 'volume "/{}" has volflag "daw" (webdav write-access), but argument --dav is not set' + if have_daw and self.args.no_dav: + t = 'volume "/{}" has volflag "daw" (webdav write-access), but --no-dav is set' self.log(t, 1) errors = True diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 5a8f3ba9..4225d2a9 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -683,7 +683,7 @@ class HttpCli(object): if self.do_log: self.log("PFIND " + self.req) - if not self.args.dav: + if self.args.no_dav: raise Pebkac(405, "WebDAV is disabled in server config") if not self.can_read and not self.can_write and not self.can_get: @@ -837,7 +837,7 @@ class HttpCli(object): if self.do_log: self.log("PPATCH " + self.req) - if not self.args.dav: + if self.args.no_dav: raise Pebkac(405, "WebDAV is disabled in server config") if not self.can_write: @@ -894,7 +894,7 @@ class HttpCli(object): if self.do_log: self.log("LOCK " + self.req) - if not self.args.dav: + if self.args.no_dav: raise Pebkac(405, "WebDAV is disabled in server config") if not self.can_write: @@ -951,7 +951,7 @@ class HttpCli(object): if self.do_log: self.log("UNLOCK " + self.req) - if not self.args.dav: + if self.args.no_dav: raise Pebkac(405, "WebDAV is disabled in server config") if not self.can_write: @@ -1031,7 +1031,7 @@ class HttpCli(object): "Ms-Author-Via": "DAV", } - if self.args.dav: + if not self.args.no_dav: # PROPPATCH, LOCK, UNLOCK, COPY: noop (spec-must) zs = ", PROPFIND, PROPPATCH, LOCK, UNLOCK, MKCOL, COPY, MOVE, DELETE" ret["Allow"] += zs @@ -1052,7 +1052,7 @@ class HttpCli(object): t = "{} does not have write-access here" raise Pebkac(403, t.format(self.uname)) - if self.args.dav and self._applesan(): + if not self.args.no_dav and self._applesan(): return self.headers.get("content-length") == "0" if self.headers.get("expect", "").lower() == "100-continue": @@ -1236,7 +1236,7 @@ class HttpCli(object): if rnd and not self.args.nw: fn = self.rand_name(fdir, fn, rnd) - if is_put and self.args.dav: + if is_put and not self.args.no_dav: # allow overwrite if volflag daw is set, or all the following is true: # * file exists and is empty # * there is no .PARTIAL diff --git a/copyparty/svchub.py b/copyparty/svchub.py index a122da27..7d3e14bb 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -81,6 +81,7 @@ class SvcHub(object): if args.sss or args.s >= 3: args.ss = True + args.no_dav = True args.lo = args.lo or "cpp-%Y-%m%d-%H%M%S.txt.xz" args.ls = args.ls or "**,*,ln,p,r" @@ -92,6 +93,7 @@ class SvcHub(object): args.no_del = True args.no_mv = True args.hardlink = True + args.dav_nr = True args.vague_403 = True args.ban_404 = "50,60,1440" args.nih = True diff --git a/copyparty/util.py b/copyparty/util.py index 16dcda7f..a0a4708f 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -184,13 +184,15 @@ IMPLICATIONS = [ ["e2vu", "e2v"], ["e2vp", "e2v"], ["e2v", "e2d"], - ["daw", "dav"], ["smbw", "smb"], ["smb1", "smb"], ["smb_dbg", "smb"], ] +UNPLICATIONS = [["no_dav", "daw"]] + + MIMES = { "opus": "audio/ogg; codecs=opus", }