copyparty 1.16.12__py3-none-any.whl → 1.16.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- copyparty/__main__.py +4 -1
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +47 -6
- copyparty/cfg.py +37 -1
- copyparty/httpcli.py +66 -19
- copyparty/svchub.py +1 -1
- copyparty/up2k.py +2 -2
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/METADATA +4 -1
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/RECORD +15 -15
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/LICENSE +0 -0
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/WHEEL +0 -0
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.12.dist-info → copyparty-1.16.13.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -1175,6 +1175,7 @@ def add_webdav(ap):
|
|
1175
1175
|
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)")
|
1176
1176
|
ap2.add_argument("--dav-rt", action="store_true", help="show symlink-destination's lastmodified instead of the link itself; always enabled for recursive listings (volflag=davrt)")
|
1177
1177
|
ap2.add_argument("--dav-auth", action="store_true", help="force auth for all folders (required by davfs2 when only some folders are world-readable) (volflag=davauth)")
|
1178
|
+
ap2.add_argument("--dav-ua1", metavar="PTN", type=u, default=r" kioworker/", help="regex of tricky user-agents which expect 401 from GET requests; disable with [\033[32mno\033[0m] or blank")
|
1178
1179
|
|
1179
1180
|
|
1180
1181
|
def add_tftp(ap):
|
@@ -1387,7 +1388,7 @@ def add_transcoding(ap):
|
|
1387
1388
|
|
1388
1389
|
def add_rss(ap):
|
1389
1390
|
ap2 = ap.add_argument_group('RSS options')
|
1390
|
-
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental)")
|
1391
|
+
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental) (volflag=rss)")
|
1391
1392
|
ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
|
1392
1393
|
ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
|
1393
1394
|
ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files")
|
@@ -1478,7 +1479,9 @@ def add_ui(ap, retry):
|
|
1478
1479
|
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
|
1479
1480
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1480
1481
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
1482
|
+
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
1481
1483
|
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
1484
|
+
ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
|
1482
1485
|
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
|
1483
1486
|
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
1484
1487
|
ap2.add_argument("--js-other", metavar="L", type=u, default="", help="URL to additional JS to include in all other pages")
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -1282,10 +1282,10 @@ class AuthSrv(object):
|
|
1282
1282
|
# one or more bools before the final flag; eat them
|
1283
1283
|
n1, uname = uname.split(",", 1)
|
1284
1284
|
for _, vp, _, _ in vols:
|
1285
|
-
self._read_volflag(flags[vp], n1, True, False)
|
1285
|
+
self._read_volflag(vp, flags[vp], n1, True, False)
|
1286
1286
|
|
1287
1287
|
for _, vp, _, _ in vols:
|
1288
|
-
self._read_volflag(flags[vp], uname, cval, False)
|
1288
|
+
self._read_volflag(vp, flags[vp], uname, cval, False)
|
1289
1289
|
|
1290
1290
|
return
|
1291
1291
|
|
@@ -1372,20 +1372,34 @@ class AuthSrv(object):
|
|
1372
1372
|
|
1373
1373
|
def _read_volflag(
|
1374
1374
|
self,
|
1375
|
+
vpath ,
|
1375
1376
|
flags ,
|
1376
1377
|
name ,
|
1377
1378
|
value ,
|
1378
1379
|
is_list ,
|
1379
1380
|
) :
|
1381
|
+
if name not in flagdescs:
|
1382
|
+
name = name.lower()
|
1383
|
+
|
1384
|
+
# volflags are snake_case, but a leading dash is the removal operator
|
1385
|
+
if name not in flagdescs and "-" in name[1:]:
|
1386
|
+
name = name[:1] + name[1:].replace("-", "_")
|
1387
|
+
|
1380
1388
|
desc = flagdescs.get(name.lstrip("-"), "?").replace("\n", " ")
|
1381
1389
|
|
1390
|
+
if not name:
|
1391
|
+
self._e("└─unreadable-line")
|
1392
|
+
t = "WARNING: the config for volume [/%s] indicated that a volflag was to be defined, but the volflag name was blank"
|
1393
|
+
self.log(t % (vpath,), 3)
|
1394
|
+
return
|
1395
|
+
|
1382
1396
|
if re.match("^-[^-]+$", name):
|
1383
1397
|
t = "└─unset volflag [{}] ({})"
|
1384
1398
|
self._e(t.format(name[1:], desc))
|
1385
1399
|
flags[name] = True
|
1386
1400
|
return
|
1387
1401
|
|
1388
|
-
zs = "mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1402
|
+
zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1389
1403
|
if name not in zs.split():
|
1390
1404
|
if value is True:
|
1391
1405
|
t = "└─add volflag [{}] = {} ({})"
|
@@ -1550,6 +1564,17 @@ class AuthSrv(object):
|
|
1550
1564
|
vol.all_vps.sort(key=lambda x: len(x[0]), reverse=True)
|
1551
1565
|
vol.root = vfs
|
1552
1566
|
|
1567
|
+
zs = "neversymlink"
|
1568
|
+
k_ign = set(zs.split())
|
1569
|
+
for vol in vfs.all_vols.values():
|
1570
|
+
unknown_flags = set()
|
1571
|
+
for k, v in vol.flags.items():
|
1572
|
+
if k not in flagdescs and k not in k_ign:
|
1573
|
+
unknown_flags.add(k)
|
1574
|
+
if unknown_flags:
|
1575
|
+
t = "WARNING: the config for volume [/%s] has unrecognized volflags; will ignore: '%s'"
|
1576
|
+
self.log(t % (vol.vpath, "', '".join(unknown_flags)), 3)
|
1577
|
+
|
1553
1578
|
enshare = self.args.shr
|
1554
1579
|
shr = enshare[1:-1]
|
1555
1580
|
shrs = enshare[1:]
|
@@ -1967,8 +1992,10 @@ class AuthSrv(object):
|
|
1967
1992
|
|
1968
1993
|
# append additive args from argv to volflags
|
1969
1994
|
hooks = "xbu xau xiu xbc xac xbr xar xbd xad xm xban".split()
|
1970
|
-
for name in "mtp on404 on403".split() + hooks:
|
1971
|
-
self._read_volflag(
|
1995
|
+
for name in "ext_th mtp on404 on403".split() + hooks:
|
1996
|
+
self._read_volflag(
|
1997
|
+
vol.vpath, vol.flags, name, getattr(self.args, name), True
|
1998
|
+
)
|
1972
1999
|
|
1973
2000
|
for hn in hooks:
|
1974
2001
|
cmds = vol.flags.get(hn)
|
@@ -1996,6 +2023,16 @@ class AuthSrv(object):
|
|
1996
2023
|
ncmds.append(ocmd)
|
1997
2024
|
vol.flags[hn] = ncmds
|
1998
2025
|
|
2026
|
+
ext_th = vol.flags["ext_th_d"] = {}
|
2027
|
+
etv = "(?)"
|
2028
|
+
try:
|
2029
|
+
for etv in vol.flags.get("ext_th") or []:
|
2030
|
+
k, v = etv.split("=")
|
2031
|
+
ext_th[k] = v
|
2032
|
+
except:
|
2033
|
+
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
|
2034
|
+
self.log(t % (vol.vpath, etv), 3)
|
2035
|
+
|
1999
2036
|
# d2d drops all database features for a volume
|
2000
2037
|
for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
|
2001
2038
|
if not vol.flags.get(grp, False):
|
@@ -2347,6 +2384,7 @@ class AuthSrv(object):
|
|
2347
2384
|
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
|
2348
2385
|
}
|
2349
2386
|
js_htm = {
|
2387
|
+
"SPINNER": self.args.spinner,
|
2350
2388
|
"s_name": self.args.bname,
|
2351
2389
|
"have_up2k_idx": "e2d" in vf,
|
2352
2390
|
"have_acode": not self.args.no_acode,
|
@@ -2356,6 +2394,7 @@ class AuthSrv(object):
|
|
2356
2394
|
"have_del": not self.args.no_del,
|
2357
2395
|
"have_unpost": int(self.args.unpost),
|
2358
2396
|
"have_emp": self.args.emp,
|
2397
|
+
"ext_th": vf.get("ext_th_d") or {},
|
2359
2398
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
2360
2399
|
"sba_md": vf.get("md_sba") or "",
|
2361
2400
|
"sba_lg": vf.get("lg_sba") or "",
|
@@ -2759,7 +2798,9 @@ class AuthSrv(object):
|
|
2759
2798
|
zs = "c ihead ohead mtm mtp on403 on404 xac xad xar xau xiu xban xbc xbd xbr xbu xm"
|
2760
2799
|
lst = set(zs.split())
|
2761
2800
|
askip = set("a v c vc cgen exp_lg exp_md theme".split())
|
2762
|
-
|
2801
|
+
|
2802
|
+
t = "exp_lg exp_md ext_th_d mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
|
2803
|
+
fskip = set(t.split())
|
2763
2804
|
|
2764
2805
|
# keymap from argv to vflag
|
2765
2806
|
amap = vf_bmap()
|
copyparty/cfg.py
CHANGED
@@ -5,6 +5,9 @@ from __future__ import print_function, unicode_literals
|
|
5
5
|
zs = "a c e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vp e2vu ed emp i j lo mcr mte mth mtm mtp nb nc nid nih nth nw p q s ss sss v z zv"
|
6
6
|
onedash = set(zs.split())
|
7
7
|
|
8
|
+
# verify that all volflags are documented here:
|
9
|
+
# grep volflag= __main__.py | sed -r 's/.*volflag=//;s/\).*//' | sort | uniq | while IFS= read -r x; do grep -E "\"$x(=[^ \"]+)?\": \"" cfg.py || printf '%s\n' "$x"; done
|
10
|
+
|
8
11
|
|
9
12
|
def vf_bmap() :
|
10
13
|
"""argv-to-volflag: simple bools"""
|
@@ -106,6 +109,7 @@ def vf_cmap() :
|
|
106
109
|
for k in (
|
107
110
|
"exp_lg",
|
108
111
|
"exp_md",
|
112
|
+
"ext_th",
|
109
113
|
"mte",
|
110
114
|
"mth",
|
111
115
|
"mtp",
|
@@ -179,8 +183,11 @@ flagcats = {
|
|
179
183
|
"e2dsa": "scans all folders for new files on startup; also sets -e2d",
|
180
184
|
"e2t": "enable multimedia indexing; makes it possible to search for tags",
|
181
185
|
"e2ts": "scan existing files for tags on startup; also sets -e2t",
|
182
|
-
"
|
186
|
+
"e2tsr": "delete all metadata from DB (full rescan); also sets -e2ts",
|
183
187
|
"d2ts": "disables metadata collection for existing files",
|
188
|
+
"e2v": "verify integrity on startup by hashing files and comparing to db",
|
189
|
+
"e2vu": "when e2v fails, update the db (assume on-disk files are good)",
|
190
|
+
"e2vp": "when e2v fails, panic and quit copyparty",
|
184
191
|
"d2ds": "disables onboot indexing, overrides -e2ds*",
|
185
192
|
"d2t": "disables metadata collection, overrides -e2t*",
|
186
193
|
"d2v": "disables file verification, overrides -e2v*",
|
@@ -200,6 +207,8 @@ flagcats = {
|
|
200
207
|
"srch_excl": "exclude search results with URL matching this regex",
|
201
208
|
},
|
202
209
|
'database, audio tags\n"mte", "mth", "mtp", "mtm" all work the same as -mte, -mth, ...': {
|
210
|
+
"mte=artist,title": "media-tags to index/display",
|
211
|
+
"mth=fmt,res,ac": "media-tags to hide by default",
|
203
212
|
"mtp=.bpm=f,audio-bpm.py": 'uses the "audio-bpm.py" program to\ngenerate ".bpm" tags from uploads (f = overwrite tags)',
|
204
213
|
"mtp=ahash,vhash=media-hash.py": "collects two tags at once",
|
205
214
|
},
|
@@ -213,6 +222,7 @@ flagcats = {
|
|
213
222
|
"crop": "center-cropping (y/n/fy/fn)",
|
214
223
|
"th3x": "3x resolution (y/n/fy/fn)",
|
215
224
|
"convt": "conversion timeout in seconds",
|
225
|
+
"ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s",
|
216
226
|
},
|
217
227
|
"handlers\n(better explained in --help-handlers)": {
|
218
228
|
"on404=PY": "handle 404s by executing PY file",
|
@@ -235,8 +245,12 @@ flagcats = {
|
|
235
245
|
"grid": "show grid/thumbnails by default",
|
236
246
|
"gsel": "select files in grid by ctrl-click",
|
237
247
|
"sort": "default sort order",
|
248
|
+
"nsort": "natural-sort of leading digits in filenames",
|
249
|
+
"hsortn": "number of sort-rules to add to media URLs",
|
238
250
|
"unlist": "dont list files matching REGEX",
|
239
251
|
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
252
|
+
"tcolor=#fc0": "theme color (a hint for webbrowsers, discord, etc.)",
|
253
|
+
"nodirsz": "don't show total folder size",
|
240
254
|
"robots": "allows indexing by search engines (default)",
|
241
255
|
"norobots": "kindly asks search engines to leave",
|
242
256
|
"no_sb_md": "disable js sandbox for markdown files",
|
@@ -249,12 +263,33 @@ flagcats = {
|
|
249
263
|
"lg_sba": "value of iframe allow-prop for *logue-sandbox",
|
250
264
|
"nohtml": "return html and markdown as text/html",
|
251
265
|
},
|
266
|
+
"opengraph (discord embeds)": {
|
267
|
+
"og": "enable OG (disables hotlinking)",
|
268
|
+
"og_site": "sitename; defaults to --name, disable with '-'",
|
269
|
+
"og_desc": "description text for all files; disable with '-'",
|
270
|
+
"og_th=jf": "thumbnail format; j / jf / jf3 / w / w3 / ...",
|
271
|
+
"og_title_a": "audio title format; default: {{ artist }} - {{ title }}",
|
272
|
+
"og_title_v": "video title format; default: {{ title }}",
|
273
|
+
"og_title_i": "image title format; default: {{ title }}",
|
274
|
+
"og_title=foo": "fallback title if there's nothing in the db",
|
275
|
+
"og_s_title": "force default title; do not read from tags",
|
276
|
+
"og_tpl": "custom html; see --og-tpl in --help",
|
277
|
+
"og_no_head": "you want to add tags manually with og_tpl",
|
278
|
+
"og_ua": "if defined: only send OG html if useragent matches this regex",
|
279
|
+
},
|
280
|
+
"textfiles": {
|
281
|
+
"exp": "enable textfile expansion; see --help-exp",
|
282
|
+
"exp_md": "placeholders to expand in markdown files; see --help",
|
283
|
+
"exp_lg": "placeholders to expand in prologue/epilogue; see --help",
|
284
|
+
},
|
252
285
|
"others": {
|
253
286
|
"dots": "allow all users with read-access to\nenable the option to show dotfiles in listings",
|
254
287
|
"fk=8": 'generates per-file accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes',
|
255
288
|
"fka=8": 'generates slightly weaker per-file accesskeys,\nwhich are then required at the "g" permission;\nnot affected by filesize or inode numbers',
|
289
|
+
"rss": "allow '?rss' URL suffix (experimental)",
|
256
290
|
"ups_who=2": "restrict viewing the list of recent uploads",
|
257
291
|
"zip_who=2": "restrict access to download-as-zip/tar",
|
292
|
+
"nopipe": "disable race-the-beam (download unfinished uploads)",
|
258
293
|
"mv_retry": "ms-windows: timeout for renaming busy files",
|
259
294
|
"rm_retry": "ms-windows: timeout for deleting busy files",
|
260
295
|
"davauth": "ask webdav clients to login for all folders",
|
@@ -264,3 +299,4 @@ flagcats = {
|
|
264
299
|
|
265
300
|
|
266
301
|
flagdescs = {k.split("=")[0]: v for tab in flagcats.values() for k, v in tab.items()}
|
302
|
+
|
copyparty/httpcli.py
CHANGED
@@ -186,7 +186,7 @@ class HttpCli(object):
|
|
186
186
|
self.is_vproxied = False
|
187
187
|
self.in_hdr_recv = True
|
188
188
|
self.headers = {}
|
189
|
-
self.mode = " "
|
189
|
+
self.mode = " " # http verb
|
190
190
|
self.req = " "
|
191
191
|
self.http_ver = ""
|
192
192
|
self.hint = ""
|
@@ -726,10 +726,10 @@ class HttpCli(object):
|
|
726
726
|
return self.handle_unlock() and self.keepalive
|
727
727
|
elif self.mode == "MKCOL":
|
728
728
|
return self.handle_mkcol() and self.keepalive
|
729
|
-
elif self.mode
|
730
|
-
return self.
|
729
|
+
elif self.mode in ("MOVE", "COPY"):
|
730
|
+
return self.handle_cpmv() and self.keepalive
|
731
731
|
else:
|
732
|
-
raise Pebkac(400, 'invalid HTTP
|
732
|
+
raise Pebkac(400, 'invalid HTTP verb "{0}"'.format(self.mode))
|
733
733
|
|
734
734
|
except Exception as ex:
|
735
735
|
if not isinstance(ex, Pebkac):
|
@@ -1768,6 +1768,12 @@ class HttpCli(object):
|
|
1768
1768
|
if "%" in self.req:
|
1769
1769
|
self.log(" `-- %r" % (self.vpath,))
|
1770
1770
|
|
1771
|
+
if self.args.no_dav:
|
1772
|
+
raise Pebkac(405, "WebDAV is disabled in server config")
|
1773
|
+
|
1774
|
+
if not self.can_write:
|
1775
|
+
raise Pebkac(401, "authenticate")
|
1776
|
+
|
1771
1777
|
try:
|
1772
1778
|
return self._mkdir(self.vpath, True)
|
1773
1779
|
except Pebkac as ex:
|
@@ -1777,14 +1783,35 @@ class HttpCli(object):
|
|
1777
1783
|
self.reply(b"", ex.code)
|
1778
1784
|
return True
|
1779
1785
|
|
1780
|
-
def
|
1786
|
+
def handle_cpmv(self) :
|
1781
1787
|
dst = self.headers["destination"]
|
1782
|
-
|
1788
|
+
|
1789
|
+
# dolphin (kioworker/6.10) "webdav://127.0.0.1:3923/a/b.txt"
|
1790
|
+
dst = re.sub("^[a-zA-Z]+://[^/]+", "", dst).lstrip()
|
1791
|
+
|
1792
|
+
if self.is_vproxied and dst.startswith(self.args.SRS):
|
1793
|
+
dst = dst[len(self.args.RS) :]
|
1794
|
+
|
1795
|
+
if self.do_log:
|
1796
|
+
self.log("%s %s --//> %s @%s" % (self.mode, self.req, dst, self.uname))
|
1797
|
+
if "%" in self.req:
|
1798
|
+
self.log(" `-- %r" % (self.vpath,))
|
1799
|
+
|
1800
|
+
if self.args.no_dav:
|
1801
|
+
raise Pebkac(405, "WebDAV is disabled in server config")
|
1802
|
+
|
1783
1803
|
dst = unquotep(dst)
|
1784
|
-
if not self._mv(self.vpath, dst.lstrip("/")):
|
1785
|
-
return False
|
1786
1804
|
|
1787
|
-
|
1805
|
+
# overwrite=True is default; rfc4918 9.8.4
|
1806
|
+
overwrite = self.headers.get("overwrite", "").lower() != "f"
|
1807
|
+
|
1808
|
+
try:
|
1809
|
+
fun = self._cp if self.mode == "COPY" else self._mv
|
1810
|
+
return fun(self.vpath, dst.lstrip("/"), overwrite)
|
1811
|
+
except Pebkac as ex:
|
1812
|
+
if ex.code == 403:
|
1813
|
+
ex.code = 401
|
1814
|
+
raise
|
1788
1815
|
|
1789
1816
|
def _applesan(self) :
|
1790
1817
|
if self.args.dav_mac or "Darwin/" not in self.ua:
|
@@ -4788,9 +4815,12 @@ class HttpCli(object):
|
|
4788
4815
|
# that the client is not a graphical browser
|
4789
4816
|
if (
|
4790
4817
|
rc == 403
|
4791
|
-
and
|
4792
|
-
and not self.ua.startswith("Mozilla/")
|
4818
|
+
and self.uname == "*"
|
4793
4819
|
and "sec-fetch-site" not in self.headers
|
4820
|
+
and (
|
4821
|
+
not self.ua.startswith("Mozilla/")
|
4822
|
+
or (self.args.dav_ua1 and self.args.dav_ua1.search(self.ua))
|
4823
|
+
)
|
4794
4824
|
):
|
4795
4825
|
rc = 401
|
4796
4826
|
self.out_headers["WWW-Authenticate"] = 'Basic realm="a"'
|
@@ -5419,6 +5449,8 @@ class HttpCli(object):
|
|
5419
5449
|
|
5420
5450
|
def handle_rm(self, req ) :
|
5421
5451
|
if not req and not self.can_delete:
|
5452
|
+
if self.mode == "DELETE" and self.uname == "*":
|
5453
|
+
raise Pebkac(401, "authenticate") # webdav
|
5422
5454
|
raise Pebkac(403, "'delete' not allowed for user " + self.uname)
|
5423
5455
|
|
5424
5456
|
if self.args.no_del:
|
@@ -5453,14 +5485,22 @@ class HttpCli(object):
|
|
5453
5485
|
if not dst:
|
5454
5486
|
raise Pebkac(400, "need dst vpath")
|
5455
5487
|
|
5456
|
-
return self._mv(self.vpath, dst.lstrip("/"))
|
5488
|
+
return self._mv(self.vpath, dst.lstrip("/"), False)
|
5457
5489
|
|
5458
|
-
def _mv(self, vsrc , vdst ) :
|
5490
|
+
def _mv(self, vsrc , vdst , overwrite ) :
|
5459
5491
|
if self.args.no_mv:
|
5460
5492
|
raise Pebkac(403, "the rename/move feature is disabled in server config")
|
5461
5493
|
|
5462
|
-
|
5463
|
-
self.asrv.vfs.get(
|
5494
|
+
# `handle_cpmv` will catch 403 from these and raise 401
|
5495
|
+
svn, srem = self.asrv.vfs.get(vsrc, self.uname, True, False, True)
|
5496
|
+
dvn, drem = self.asrv.vfs.get(vdst, self.uname, False, True)
|
5497
|
+
|
5498
|
+
if overwrite:
|
5499
|
+
dabs = dvn.canonical(drem)
|
5500
|
+
if bos.path.exists(dabs):
|
5501
|
+
self.log("overwriting %s" % (dabs,))
|
5502
|
+
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5503
|
+
wunlink(self.log, dabs, dvn.flags)
|
5464
5504
|
|
5465
5505
|
x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst)
|
5466
5506
|
self.loud_reply(x.get(), status=201)
|
@@ -5476,14 +5516,21 @@ class HttpCli(object):
|
|
5476
5516
|
if not dst:
|
5477
5517
|
raise Pebkac(400, "need dst vpath")
|
5478
5518
|
|
5479
|
-
return self._cp(self.vpath, dst.lstrip("/"))
|
5519
|
+
return self._cp(self.vpath, dst.lstrip("/"), False)
|
5480
5520
|
|
5481
|
-
def _cp(self, vsrc , vdst ) :
|
5521
|
+
def _cp(self, vsrc , vdst , overwrite ) :
|
5482
5522
|
if self.args.no_cp:
|
5483
5523
|
raise Pebkac(403, "the copy feature is disabled in server config")
|
5484
5524
|
|
5485
|
-
self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5486
|
-
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5525
|
+
svn, srem = self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5526
|
+
dvn, drem = self.asrv.vfs.get(vdst, self.uname, False, True)
|
5527
|
+
|
5528
|
+
if overwrite:
|
5529
|
+
dabs = dvn.canonical(drem)
|
5530
|
+
if bos.path.exists(dabs):
|
5531
|
+
self.log("overwriting %s" % (dabs,))
|
5532
|
+
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5533
|
+
wunlink(self.log, dabs, dvn.flags)
|
5487
5534
|
|
5488
5535
|
x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst)
|
5489
5536
|
self.loud_reply(x.get(), status=201)
|
copyparty/svchub.py
CHANGED
@@ -759,7 +759,7 @@ class SvcHub(object):
|
|
759
759
|
vs = os.path.expandvars(os.path.expanduser(vs))
|
760
760
|
setattr(al, k, vs)
|
761
761
|
|
762
|
-
for k in "sus_urls nonsus_urls".split(" "):
|
762
|
+
for k in "dav_ua1 sus_urls nonsus_urls".split(" "):
|
763
763
|
vs = getattr(al, k)
|
764
764
|
if not vs or vs == "no":
|
765
765
|
setattr(al, k, None)
|
copyparty/up2k.py
CHANGED
@@ -4610,7 +4610,7 @@ class Up2k(object):
|
|
4610
4610
|
|
4611
4611
|
cur = self.cur.get(ptop)
|
4612
4612
|
if not cur:
|
4613
|
-
return None, None, None, None,
|
4613
|
+
return None, None, None, None, "", None
|
4614
4614
|
|
4615
4615
|
rd, fn = vsplit(vrem)
|
4616
4616
|
q = "select w, mt, sz, ip, at from up where rd=? and fn=? limit 1"
|
@@ -4623,7 +4623,7 @@ class Up2k(object):
|
|
4623
4623
|
if hit:
|
4624
4624
|
wark, ftime, fsize, ip, at = hit
|
4625
4625
|
return cur, wark, ftime, fsize, ip, at
|
4626
|
-
return cur, None, None, None,
|
4626
|
+
return cur, None, None, None, "", None
|
4627
4627
|
|
4628
4628
|
def _forget_file(
|
4629
4629
|
self,
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.13
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
6
6
|
License: MIT
|
@@ -721,6 +721,8 @@ enabling `multiselect` lets you click files to select them, and then shift-click
|
|
721
721
|
* `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
|
722
722
|
* the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`
|
723
723
|
|
724
|
+
to show `/icons/exe.png` as the thumbnail for all .exe files, `--ext-th=exe=/icons/exe.png` (optionally as a volflag)
|
725
|
+
|
724
726
|
config file example:
|
725
727
|
|
726
728
|
```yaml
|
@@ -735,6 +737,7 @@ config file example:
|
|
735
737
|
flags:
|
736
738
|
dthumb # disable ALL thumbnails and audio transcoding
|
737
739
|
dvthumb # only disable video thumbnails
|
740
|
+
ext-th: exe=/ico/exe.png # /ico/exe.png is the thumbnail of *.exe
|
738
741
|
th-covers: folder.png,folder.jpg,cover.png,cover.jpg # the default
|
739
742
|
```
|
740
743
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
copyparty/__init__.py,sha256=VR6ZZhB9IxaK5TDXDTBM_OIP5ydkrdbaEnstktLM__s,2649
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256
|
2
|
+
copyparty/__main__.py,sha256=Tcvi7uIGA37yGVXJbbhn-Cr0OBtt81aRF2Or0ZHXz6U,116449
|
3
|
+
copyparty/__version__.py,sha256=Kh8nmhFwgLz9KpLvVPB-drOjIKb4uSXTYfW7c0xGr3E,252
|
4
|
+
copyparty/authsrv.py,sha256=-oy1PtcMONLcrMllSY2aPuyHWQmE0Ot_EQTX7UJ5tuc,107035
|
5
5
|
copyparty/broker_mp.py,sha256=QdOXXvV2Xn6J0CysEqyY3GZbqxQMyWnTpnba-a5lMc0,4987
|
6
6
|
copyparty/broker_mpw.py,sha256=PpSS4SK3pItlpfD8OwVr3QmJEPKlUgaf2nuMOozixgU,3347
|
7
7
|
copyparty/broker_thr.py,sha256=fjoYtpSscUA7-nMl4r1n2R7UK3J9lrvLS3rUZ-iJzKQ,1721
|
8
8
|
copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
|
9
9
|
copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
|
10
|
-
copyparty/cfg.py,sha256
|
10
|
+
copyparty/cfg.py,sha256=-Cbva1shfXQVGFl8Xbo-bkUYjkpU6amPZpTyPJcE7M0,12926
|
11
11
|
copyparty/dxml.py,sha256=vu5uZQtwvwoqnFHbULs2Zh_y2DETu0T-ENpMZ1i2CV4,2505
|
12
12
|
copyparty/fsutil.py,sha256=IVOFG8zBQPMQDDv7RIStSJHwHiAnVNROZS37O5k465A,4524
|
13
13
|
copyparty/ftpd.py,sha256=T97SFS7JFtvRLbJX8C4fJSYwe13vhN3-E6emtlVmqLA,17608
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=Dr776ra7_wTCghymLOymy_Bpv8A15Qmgt9ERe-8kZ5A,219131
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
16
|
copyparty/httpsrv.py,sha256=pxH_Eh8ElBLvOEDejgpP9Bvk65HNEou-03aYIcgXhrs,18090
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
@@ -24,14 +24,14 @@ copyparty/smbd.py,sha256=dixFl2wlWymq_Cycc8a4cVB4gY8RSg2e3tE7Xr-aDa0,14614
|
|
24
24
|
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=6zEEGl4hRe6bTB83Y_RtnBGxr2JcUa__GdiAMqNJZnY,3208
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=gBp7x1hGF4b6_nanW5QUQcz8UmMCad6DzdE2KD5cLvE,41462
|
28
28
|
copyparty/szip.py,sha256=HFtnwOiBgx0HMLUf-h_T84zSlRijPxmhRo5PM613kRA,8602
|
29
29
|
copyparty/tcpsrv.py,sha256=2q18dGR8jnezA4SMfUXa-wrGRGX3nHIwkxkWvkTzF2A,19889
|
30
30
|
copyparty/tftpd.py,sha256=PXgG4rTmiaU_TavSyZWD5cFphdfChs9YvNY21qfExt8,13611
|
31
31
|
copyparty/th_cli.py,sha256=PxDAmUvO_8Vm5edXiWtsCft0Fw69QL9rCHf9zLmUNeA,4800
|
32
32
|
copyparty/th_srv.py,sha256=tHbh_Ve3v8tYclWH2thLs5oFufeXgJi1duUMveKIx9k,30725
|
33
33
|
copyparty/u2idx.py,sha256=G6MDbD4I_sJSOwaNFZ6XLTQhnEDrB12pVKuKhzQ_leE,13676
|
34
|
-
copyparty/up2k.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=yli2ALT61o1sPta4ckJu4v7hwUrV7IAJSsJo1-OEZWY,175423
|
35
35
|
copyparty/util.py,sha256=Y_znSn3hBNYaaduwcCB7mmBYsi6vv9CYC1zJ9rq9yeQ,99435
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
@@ -55,9 +55,9 @@ copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUu
|
|
55
55
|
copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43YukGs,6206
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
57
|
copyparty/web/baguettebox.js.gz,sha256=r2c_hOZV_RTyl4CqWWX14FDWP8nnDVwGkDl4Sfk0rU4,8239
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
58
|
+
copyparty/web/browser.css.gz,sha256=A44DddZBf-PEEMOj-u5YF_JrSk5DZYf-8f_J5jqC2is,11651
|
59
59
|
copyparty/web/browser.html,sha256=auvhLVE_t0aIN0q-nk0zOWFqITgDhroMAAviBNLoFfc,4788
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=Ec5tzZutoI8daxzHdfRfDP7L9ovDTY10j2g-hkeRlmA,91598
|
61
61
|
copyparty/web/browser2.html,sha256=NRUZ08GH-e2YcGXcoz0UjYg6JIVF42u4IMX4HHwWTmg,1587
|
62
62
|
copyparty/web/cf.html,sha256=lJThtNFNAQT1ClCHHlivAkDGE0LutedwopXD62Z8Nys,589
|
63
63
|
copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
|
@@ -109,9 +109,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
109
109
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
110
110
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
111
111
|
copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
|
112
|
-
copyparty-1.16.
|
113
|
-
copyparty-1.16.
|
114
|
-
copyparty-1.16.
|
115
|
-
copyparty-1.16.
|
116
|
-
copyparty-1.16.
|
117
|
-
copyparty-1.16.
|
112
|
+
copyparty-1.16.13.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
113
|
+
copyparty-1.16.13.dist-info/METADATA,sha256=hm4IR2Xqc3bnZLZuEE3MPuNqwi73-IXzd_WqnVo_2NI,154166
|
114
|
+
copyparty-1.16.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
115
|
+
copyparty-1.16.13.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
116
|
+
copyparty-1.16.13.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
117
|
+
copyparty-1.16.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|