copyparty 1.15.4__py3-none-any.whl → 1.15.6__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/__init__.py +0 -1
- copyparty/__main__.py +5 -5
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +1 -1
- copyparty/cfg.py +3 -1
- copyparty/httpcli.py +50 -23
- copyparty/mtag.py +1 -1
- copyparty/ssdp.py +1 -1
- copyparty/tcpsrv.py +1 -1
- copyparty/up2k.py +52 -29
- copyparty/util.py +13 -10
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/METADATA +62 -23
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/RECORD +19 -19
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/LICENSE +0 -0
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/WHEEL +0 -0
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.4.dist-info → copyparty-1.15.6.dist-info}/top_level.txt +0 -0
copyparty/__init__.py
CHANGED
copyparty/__main__.py
CHANGED
@@ -212,7 +212,6 @@ def init_E(EE ) :
|
|
212
212
|
|
213
213
|
raise Exception("could not find a writable path for config")
|
214
214
|
|
215
|
-
E.pkg = sys.modules[__package__]
|
216
215
|
E.mod = os.path.dirname(os.path.realpath(__file__))
|
217
216
|
if E.mod.endswith("__init__"):
|
218
217
|
E.mod = os.path.dirname(E.mod)
|
@@ -773,7 +772,7 @@ def get_sects():
|
|
773
772
|
dedent(
|
774
773
|
"""
|
775
774
|
specify --exp or the "exp" volflag to enable placeholder expansions
|
776
|
-
in README.md / .prologue.html / .epilogue.html
|
775
|
+
in README.md / PREADME.md / .prologue.html / .epilogue.html
|
777
776
|
|
778
777
|
--exp-md (volflag exp_md) holds the list of placeholders which can be
|
779
778
|
expanded in READMEs, and --exp-lg (volflag exp_lg) likewise for logues;
|
@@ -889,7 +888,7 @@ def get_sects():
|
|
889
888
|
dedent(
|
890
889
|
"""
|
891
890
|
the mDNS protocol is multicast-based, which means there are thousands
|
892
|
-
of fun and
|
891
|
+
of fun and interesting ways for it to break unexpectedly
|
893
892
|
|
894
893
|
things to check if it does not work at all:
|
895
894
|
|
@@ -998,6 +997,7 @@ def add_upload(ap):
|
|
998
997
|
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
999
998
|
ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
|
1000
999
|
ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
|
1000
|
+
ap2.add_argument("--no-clone", action="store_true", help="do not use existing data on disk to satisfy dupe uploads; reduces server HDD reads in exchange for much more network load (volflag=noclone)")
|
1001
1001
|
ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
|
1002
1002
|
ap2.add_argument("--snap-wri", metavar="SEC", type=int, default=300, help="write upload state to ./hist/up2k.snap every \033[33mSEC\033[0m seconds; allows resuming incomplete uploads after a server crash")
|
1003
1003
|
ap2.add_argument("--snap-drop", metavar="MIN", type=float, default=1440.0, help="forget unfinished uploads after \033[33mMIN\033[0m minutes; impossible to resume them after that (360=6h, 1440=24h)")
|
@@ -1247,7 +1247,7 @@ def add_safety(ap):
|
|
1247
1247
|
ap2.add_argument("--no-dot-mv", action="store_true", help="disallow moving dotfiles; makes it impossible to move folders containing dotfiles")
|
1248
1248
|
ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to turn something into a dotfile")
|
1249
1249
|
ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings")
|
1250
|
-
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme.md into directory listings")
|
1250
|
+
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme/preadme.md into directory listings")
|
1251
1251
|
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
|
1252
1252
|
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore \033[33m--no-robots\033[0m")
|
1253
1253
|
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything (volflag=norobots)")
|
@@ -1445,7 +1445,7 @@ def add_ui(ap, retry):
|
|
1445
1445
|
ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
1446
1446
|
ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox")
|
1447
1447
|
ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for prologue/epilogue docs (volflag=lg_sbf)")
|
1448
|
-
ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README.md documents (volflags: no_sb_md | sb_md)")
|
1448
|
+
ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
|
1449
1449
|
ap2.add_argument("--no-sb-lg", action="store_true", help="don't sandbox prologue/epilogue docs (volflags: no_sb_lg | sb_lg); enables non-js support")
|
1450
1450
|
|
1451
1451
|
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -917,7 +917,7 @@ class AuthSrv(object):
|
|
917
917
|
|
918
918
|
for un, gn in un_gn:
|
919
919
|
# if ap/vp has a user/group placeholder, make sure to keep
|
920
|
-
# track so the same user/
|
920
|
+
# track so the same user/group is mapped when setting perms;
|
921
921
|
# otherwise clear un/gn to indicate it's a regular volume
|
922
922
|
|
923
923
|
src1 = src0.replace("${u}", un or "\n")
|
copyparty/cfg.py
CHANGED
@@ -13,6 +13,7 @@ def vf_bmap() :
|
|
13
13
|
"dav_rt": "davrt",
|
14
14
|
"ed": "dots",
|
15
15
|
"hardlink_only": "hardlinkonly",
|
16
|
+
"no_clone": "noclone",
|
16
17
|
"no_dirsz": "nodirsz",
|
17
18
|
"no_dupe": "nodupe",
|
18
19
|
"no_forget": "noforget",
|
@@ -135,7 +136,8 @@ flagcats = {
|
|
135
136
|
"hardlink": "enable hardlink-based file deduplication,\nwith fallback on symlinks when that is impossible",
|
136
137
|
"hardlinkonly": "dedup with hardlink only, never symlink;\nmake a full copy if hardlink is impossible",
|
137
138
|
"safededup": "verify on-disk data before using it for dedup",
|
138
|
-
"
|
139
|
+
"noclone": "take dupe data from clients, even if available on HDD",
|
140
|
+
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
139
141
|
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
140
142
|
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
141
143
|
"nosub": "forces all uploads into the top folder of the vfs",
|
copyparty/httpcli.py
CHANGED
@@ -123,6 +123,10 @@ _ = (argparse, threading)
|
|
123
123
|
|
124
124
|
NO_CACHE = {"Cache-Control": "no-cache"}
|
125
125
|
|
126
|
+
LOGUES = [[0, ".prologue.html"], [1, ".epilogue.html"]]
|
127
|
+
|
128
|
+
READMES = [[0, ["preadme.md", "PREADME.md"]], [1, ["readme.md", "README.md"]]]
|
129
|
+
|
126
130
|
|
127
131
|
class HttpCli(object):
|
128
132
|
"""
|
@@ -2145,11 +2149,17 @@ class HttpCli(object):
|
|
2145
2149
|
except UnrecvEOF:
|
2146
2150
|
raise Pebkac(422, "client disconnected while posting JSON")
|
2147
2151
|
|
2148
|
-
self.log("decoding {} bytes of {} json".format(len(json_buf), enc))
|
2149
2152
|
try:
|
2150
2153
|
body = json.loads(json_buf.decode(enc, "replace"))
|
2154
|
+
try:
|
2155
|
+
zds = {k: v for k, v in body.items()}
|
2156
|
+
zds["hash"] = "%d chunks" % (len(body["hash"]))
|
2157
|
+
except:
|
2158
|
+
zds = body
|
2159
|
+
t = "POST len=%d type=%s ip=%s user=%s req=%r json=%s"
|
2160
|
+
self.log(t % (len(json_buf), enc, self.ip, self.uname, self.req, zds))
|
2151
2161
|
except:
|
2152
|
-
raise Pebkac(422, "you POSTed invalid json")
|
2162
|
+
raise Pebkac(422, "you POSTed %d bytes of invalid json" % (len(json_buf),))
|
2153
2163
|
|
2154
2164
|
# self.reply(b"cloudflare", 503)
|
2155
2165
|
# return True
|
@@ -2404,13 +2414,13 @@ class HttpCli(object):
|
|
2404
2414
|
finally:
|
2405
2415
|
if locked:
|
2406
2416
|
# now block until all chunks released+confirmed
|
2407
|
-
x = broker.ask("up2k.confirm_chunks", ptop, wark, locked)
|
2417
|
+
x = broker.ask("up2k.confirm_chunks", ptop, wark, written, locked)
|
2408
2418
|
num_left, t = x.get()
|
2409
2419
|
if num_left < 0:
|
2410
2420
|
self.loud_reply(t, status=500)
|
2411
2421
|
return False
|
2412
2422
|
t = "got %d more chunks, %d left"
|
2413
|
-
self.log(t % (len(
|
2423
|
+
self.log(t % (len(written), num_left), 6)
|
2414
2424
|
|
2415
2425
|
if num_left < 0:
|
2416
2426
|
raise Pebkac(500, "unconfirmed; see serverlog")
|
@@ -3022,7 +3032,7 @@ class HttpCli(object):
|
|
3022
3032
|
if ex.errno != errno.ENOENT:
|
3023
3033
|
raise
|
3024
3034
|
|
3025
|
-
# if file exists,
|
3035
|
+
# if file exists, check that timestamp matches the client's
|
3026
3036
|
if srv_lastmod >= 0:
|
3027
3037
|
same_lastmod = cli_lastmod3 in [-1, srv_lastmod3]
|
3028
3038
|
if not same_lastmod:
|
@@ -3233,7 +3243,7 @@ class HttpCli(object):
|
|
3233
3243
|
) :
|
3234
3244
|
logues = ["", ""]
|
3235
3245
|
if not self.args.no_logues:
|
3236
|
-
for n, fn in
|
3246
|
+
for n, fn in LOGUES:
|
3237
3247
|
if lnames is not None and fn not in lnames:
|
3238
3248
|
continue
|
3239
3249
|
fn = "%s/%s" % (abspath, fn)
|
@@ -3245,25 +3255,31 @@ class HttpCli(object):
|
|
3245
3255
|
logues[n], vn.flags.get("exp_lg") or []
|
3246
3256
|
)
|
3247
3257
|
|
3248
|
-
|
3249
|
-
if
|
3250
|
-
if
|
3251
|
-
|
3252
|
-
elif
|
3253
|
-
|
3258
|
+
readmes = ["", ""]
|
3259
|
+
for n, fns in [] if self.args.no_readme else READMES:
|
3260
|
+
if logues[n]:
|
3261
|
+
continue
|
3262
|
+
elif lnames is None:
|
3263
|
+
pass
|
3264
|
+
elif fns[0] in lnames:
|
3265
|
+
fns = [lnames[fns[0]]]
|
3254
3266
|
else:
|
3255
3267
|
fns = []
|
3256
3268
|
|
3269
|
+
txt = ""
|
3257
3270
|
for fn in fns:
|
3258
3271
|
fn = "%s/%s" % (abspath, fn)
|
3259
3272
|
if bos.path.isfile(fn):
|
3260
3273
|
with open(fsenc(fn), "rb") as f:
|
3261
|
-
|
3274
|
+
txt = f.read().decode("utf-8")
|
3262
3275
|
break
|
3263
|
-
if readme and "exp" in vn.flags:
|
3264
|
-
readme = self._expand(readme, vn.flags.get("exp_md") or [])
|
3265
3276
|
|
3266
|
-
|
3277
|
+
if txt and "exp" in vn.flags:
|
3278
|
+
txt = self._expand(txt, vn.flags.get("exp_md") or [])
|
3279
|
+
|
3280
|
+
readmes[n] = txt
|
3281
|
+
|
3282
|
+
return logues, readmes
|
3267
3283
|
|
3268
3284
|
def _expand(self, txt , phs ) :
|
3269
3285
|
for ph in phs:
|
@@ -4763,7 +4779,7 @@ class HttpCli(object):
|
|
4763
4779
|
|
4764
4780
|
fmt = fmt.format(len(nfmt.format(biggest)))
|
4765
4781
|
retl = [
|
4766
|
-
"#
|
4782
|
+
("# %s: %s" % (x, ls[x])).replace(r"</span> // <span>", " // ")
|
4767
4783
|
for x in ["acct", "perms", "srvinf"]
|
4768
4784
|
if x in ls
|
4769
4785
|
]
|
@@ -5116,9 +5132,9 @@ class HttpCli(object):
|
|
5116
5132
|
j2a["no_prism"] = True
|
5117
5133
|
|
5118
5134
|
if not self.can_read and not is_dk:
|
5119
|
-
logues,
|
5135
|
+
logues, readmes = self._add_logues(vn, abspath, None)
|
5120
5136
|
ls_ret["logues"] = j2a["logues"] = logues
|
5121
|
-
ls_ret["
|
5137
|
+
ls_ret["readmes"] = cgv["readmes"] = readmes
|
5122
5138
|
|
5123
5139
|
if is_ls:
|
5124
5140
|
return self.tx_ls(ls_ret)
|
@@ -5374,11 +5390,18 @@ class HttpCli(object):
|
|
5374
5390
|
else:
|
5375
5391
|
taglist = list(tagset)
|
5376
5392
|
|
5377
|
-
logues,
|
5393
|
+
logues, readmes = self._add_logues(vn, abspath, lnames)
|
5378
5394
|
ls_ret["logues"] = j2a["logues"] = logues
|
5379
|
-
ls_ret["
|
5395
|
+
ls_ret["readmes"] = cgv["readmes"] = readmes
|
5380
5396
|
|
5381
|
-
if
|
5397
|
+
if (
|
5398
|
+
not files
|
5399
|
+
and not dirs
|
5400
|
+
and not readmes[0]
|
5401
|
+
and not readmes[1]
|
5402
|
+
and not logues[0]
|
5403
|
+
and not logues[1]
|
5404
|
+
):
|
5382
5405
|
logues[1] = "this folder is empty"
|
5383
5406
|
|
5384
5407
|
if "descript.ion" in lnames and os.path.isfile(
|
@@ -5423,7 +5446,11 @@ class HttpCli(object):
|
|
5423
5446
|
if doc:
|
5424
5447
|
j2a["docname"] = doc
|
5425
5448
|
doctxt = None
|
5426
|
-
|
5449
|
+
dfn = lnames.get(doc.lower())
|
5450
|
+
if dfn and dfn != doc:
|
5451
|
+
# found Foo but want FOO
|
5452
|
+
dfn = next((x for x in files if x["name"] == doc), None)
|
5453
|
+
if dfn:
|
5427
5454
|
docpath = os.path.join(abspath, doc)
|
5428
5455
|
sz = bos.path.getsize(docpath)
|
5429
5456
|
if sz < 1024 * self.args.txt_max:
|
copyparty/mtag.py
CHANGED
@@ -467,7 +467,7 @@ class MTag(object):
|
|
467
467
|
sv = str(zv).split("/")[0].strip().lstrip("0")
|
468
468
|
ret[sk] = sv or 0
|
469
469
|
|
470
|
-
# normalize key notation to
|
470
|
+
# normalize key notation to rekobo
|
471
471
|
okey = ret.get("key")
|
472
472
|
if okey:
|
473
473
|
key = str(okey).replace(" ", "").replace("maj", "").replace("min", "m")
|
copyparty/ssdp.py
CHANGED
@@ -80,7 +80,7 @@ class SSDPr(object):
|
|
80
80
|
name = self.args.doctitle
|
81
81
|
zs = zs.strip().format(c(ubase), c(url), c(name), c(self.args.zsid))
|
82
82
|
hc.reply(zs.encode("utf-8", "replace"))
|
83
|
-
return False # close
|
83
|
+
return False # close connection
|
84
84
|
|
85
85
|
|
86
86
|
class SSDPd(MCast):
|
copyparty/tcpsrv.py
CHANGED
copyparty/up2k.py
CHANGED
@@ -1061,8 +1061,13 @@ class Up2k(object):
|
|
1061
1061
|
except:
|
1062
1062
|
pass
|
1063
1063
|
|
1064
|
+
if reg2 and "dwrk" not in reg2[next(iter(reg2))]:
|
1065
|
+
for job in reg2.values():
|
1066
|
+
job["dwrk"] = job["wark"]
|
1067
|
+
|
1064
1068
|
for k, job in reg2.items():
|
1065
|
-
|
1069
|
+
job["ptop"] = ptop
|
1070
|
+
fp = djoin(ptop, job["prel"], job["name"])
|
1066
1071
|
if bos.path.exists(fp):
|
1067
1072
|
reg[k] = job
|
1068
1073
|
if "done" in job:
|
@@ -1536,7 +1541,7 @@ class Up2k(object):
|
|
1536
1541
|
at = 0
|
1537
1542
|
|
1538
1543
|
# skip upload hooks by not providing vflags
|
1539
|
-
self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, "", "", ip, at)
|
1544
|
+
self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", "", ip, at)
|
1540
1545
|
db.n += 1
|
1541
1546
|
tfa += 1
|
1542
1547
|
td = time.time() - db.t
|
@@ -2764,9 +2769,10 @@ class Up2k(object):
|
|
2764
2769
|
|
2765
2770
|
cj["name"] = sanitize_fn(cj["name"], "")
|
2766
2771
|
cj["poke"] = now = self.db_act = self.vol_act[ptop] = time.time()
|
2767
|
-
wark = self._get_wark(cj)
|
2772
|
+
wark = dwark = self._get_wark(cj)
|
2768
2773
|
job = None
|
2769
2774
|
pdir = djoin(ptop, cj["prel"])
|
2775
|
+
inc_ap = djoin(pdir, cj["name"])
|
2770
2776
|
try:
|
2771
2777
|
dev = bos.stat(pdir).st_dev
|
2772
2778
|
except:
|
@@ -2781,6 +2787,7 @@ class Up2k(object):
|
|
2781
2787
|
reg = self.registry[ptop]
|
2782
2788
|
vfs = self.asrv.vfs.all_vols[cj["vtop"]]
|
2783
2789
|
n4g = bool(vfs.flags.get("noforget"))
|
2790
|
+
noclone = bool(vfs.flags.get("noclone"))
|
2784
2791
|
rand = vfs.flags.get("rand") or cj.get("rand")
|
2785
2792
|
lost = []
|
2786
2793
|
|
@@ -2790,6 +2797,12 @@ class Up2k(object):
|
|
2790
2797
|
vols = [(ptop, jcur)] if jcur else []
|
2791
2798
|
if vfs.flags.get("xlink"):
|
2792
2799
|
vols += [(k, v) for k, v in self.cur.items() if k != ptop]
|
2800
|
+
|
2801
|
+
if noclone:
|
2802
|
+
wark = up2k_wark_from_metadata(
|
2803
|
+
self.salt, cj["size"], cj["lmod"], cj["prel"], cj["name"]
|
2804
|
+
)
|
2805
|
+
|
2793
2806
|
if vfs.flags.get("up_ts", "") == "fu" or not cj["lmod"]:
|
2794
2807
|
# force upload time rather than last-modified
|
2795
2808
|
cj["lmod"] = int(time.time())
|
@@ -2802,10 +2815,10 @@ class Up2k(object):
|
|
2802
2815
|
|
2803
2816
|
if self.no_expr_idx:
|
2804
2817
|
q = r"select * from up where w = ?"
|
2805
|
-
argv = [
|
2818
|
+
argv = [dwark]
|
2806
2819
|
else:
|
2807
2820
|
q = r"select * from up where substr(w,1,16)=? and +w=?"
|
2808
|
-
argv = [
|
2821
|
+
argv = [dwark[:16], dwark]
|
2809
2822
|
|
2810
2823
|
c2 = cur.execute(q, tuple(argv))
|
2811
2824
|
for _, dtime, dsize, dp_dir, dp_fn, ip, at in c2:
|
@@ -2813,6 +2826,9 @@ class Up2k(object):
|
|
2813
2826
|
dp_dir, dp_fn = s3dec(dp_dir, dp_fn)
|
2814
2827
|
|
2815
2828
|
dp_abs = djoin(ptop, dp_dir, dp_fn)
|
2829
|
+
if noclone and dp_abs != inc_ap:
|
2830
|
+
continue
|
2831
|
+
|
2816
2832
|
try:
|
2817
2833
|
st = bos.stat(dp_abs)
|
2818
2834
|
if stat.S_ISLNK(st.st_mode):
|
@@ -2821,7 +2837,7 @@ class Up2k(object):
|
|
2821
2837
|
if st.st_size != dsize:
|
2822
2838
|
t = "candidate ignored (db/fs desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}"
|
2823
2839
|
t = t.format(
|
2824
|
-
|
2840
|
+
dwark, st.st_size, dsize, st.st_mtime, dtime, dp_abs
|
2825
2841
|
)
|
2826
2842
|
self.log(t)
|
2827
2843
|
raise Exception()
|
@@ -2868,7 +2884,6 @@ class Up2k(object):
|
|
2868
2884
|
alts.append((score, -len(alts), j, cur, dp_dir, dp_fn))
|
2869
2885
|
|
2870
2886
|
job = None
|
2871
|
-
inc_ap = djoin(cj["ptop"], cj["prel"], cj["name"])
|
2872
2887
|
for dupe in sorted(alts, reverse=True):
|
2873
2888
|
rj = dupe[2]
|
2874
2889
|
orig_ap = djoin(rj["ptop"], rj["prel"], rj["name"])
|
@@ -2878,11 +2893,11 @@ class Up2k(object):
|
|
2878
2893
|
break
|
2879
2894
|
else:
|
2880
2895
|
self.log("asserting contents of %s" % (orig_ap,))
|
2881
|
-
|
2882
|
-
|
2883
|
-
if
|
2896
|
+
hashes2, st = self._hashlist_from_file(orig_ap)
|
2897
|
+
wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
|
2898
|
+
if dwark != wark2:
|
2884
2899
|
t = "will not dedup (fs index desync): fs=%s, db=%s, file: %s"
|
2885
|
-
self.log(t % (
|
2900
|
+
self.log(t % (wark2, dwark, orig_ap))
|
2886
2901
|
lost.append(dupe[3:])
|
2887
2902
|
continue
|
2888
2903
|
data_ok = True
|
@@ -2946,11 +2961,11 @@ class Up2k(object):
|
|
2946
2961
|
|
2947
2962
|
elif inc_ap != orig_ap and not data_ok and "done" in reg[wark]:
|
2948
2963
|
self.log("asserting contents of %s" % (orig_ap,))
|
2949
|
-
|
2950
|
-
|
2951
|
-
if wark !=
|
2964
|
+
hashes2, _ = self._hashlist_from_file(orig_ap)
|
2965
|
+
wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
|
2966
|
+
if wark != wark2:
|
2952
2967
|
t = "will not dedup (fs index desync): fs=%s, idx=%s, file: %s"
|
2953
|
-
self.log(t % (
|
2968
|
+
self.log(t % (wark2, wark, orig_ap))
|
2954
2969
|
del reg[wark]
|
2955
2970
|
|
2956
2971
|
if job or wark in reg:
|
@@ -3007,6 +3022,7 @@ class Up2k(object):
|
|
3007
3022
|
|
3008
3023
|
job = deepcopy(job)
|
3009
3024
|
job["wark"] = wark
|
3025
|
+
job["dwrk"] = dwark
|
3010
3026
|
job["at"] = cj.get("at") or now
|
3011
3027
|
zs = "vtop ptop prel name lmod host user addr poke"
|
3012
3028
|
for k in zs.split():
|
@@ -3077,7 +3093,7 @@ class Up2k(object):
|
|
3077
3093
|
raise
|
3078
3094
|
|
3079
3095
|
if cur and not self.args.nw:
|
3080
|
-
zs = "prel name lmod size ptop vtop wark host user addr at"
|
3096
|
+
zs = "prel name lmod size ptop vtop wark dwrk host user addr at"
|
3081
3097
|
a = [job[x] for x in zs.split()]
|
3082
3098
|
self.db_add(cur, vfs.flags, *a)
|
3083
3099
|
cur.connection.commit()
|
@@ -3107,6 +3123,7 @@ class Up2k(object):
|
|
3107
3123
|
|
3108
3124
|
job = {
|
3109
3125
|
"wark": wark,
|
3126
|
+
"dwrk": dwark,
|
3110
3127
|
"t0": now,
|
3111
3128
|
"sprs": sprs,
|
3112
3129
|
"hash": deepcopy(cj["hash"]),
|
@@ -3149,6 +3166,7 @@ class Up2k(object):
|
|
3149
3166
|
"lmod": job["lmod"],
|
3150
3167
|
"sprs": job.get("sprs", sprs),
|
3151
3168
|
"hash": job["need"],
|
3169
|
+
"dwrk": dwark,
|
3152
3170
|
"wark": wark,
|
3153
3171
|
}
|
3154
3172
|
|
@@ -3175,7 +3193,7 @@ class Up2k(object):
|
|
3175
3193
|
):
|
3176
3194
|
sql = "update up set mt=? where substr(w,1,16)=? and +rd=? and +fn=?"
|
3177
3195
|
try:
|
3178
|
-
cur.execute(sql, (cj["lmod"],
|
3196
|
+
cur.execute(sql, (cj["lmod"], dwark[:16], job["prel"], job["name"]))
|
3179
3197
|
cur.connection.commit()
|
3180
3198
|
|
3181
3199
|
ap = djoin(job["ptop"], job["prel"], job["name"])
|
@@ -3416,19 +3434,19 @@ class Up2k(object):
|
|
3416
3434
|
self.mutex.release()
|
3417
3435
|
return -1, ""
|
3418
3436
|
try:
|
3419
|
-
return self._confirm_chunks(ptop, wark, chashes)
|
3437
|
+
return self._confirm_chunks(ptop, wark, chashes, chashes)
|
3420
3438
|
finally:
|
3421
3439
|
self.reg_mutex.release()
|
3422
3440
|
self.mutex.release()
|
3423
3441
|
|
3424
3442
|
def confirm_chunks(
|
3425
|
-
self, ptop , wark ,
|
3443
|
+
self, ptop , wark , written , locked
|
3426
3444
|
) :
|
3427
3445
|
with self.mutex, self.reg_mutex:
|
3428
|
-
return self._confirm_chunks(ptop, wark,
|
3446
|
+
return self._confirm_chunks(ptop, wark, written, locked)
|
3429
3447
|
|
3430
3448
|
def _confirm_chunks(
|
3431
|
-
self, ptop , wark ,
|
3449
|
+
self, ptop , wark , written , locked
|
3432
3450
|
) :
|
3433
3451
|
if True:
|
3434
3452
|
self.db_act = self.vol_act[ptop] = time.time()
|
@@ -3440,11 +3458,11 @@ class Up2k(object):
|
|
3440
3458
|
except Exception as ex:
|
3441
3459
|
return -2, "confirm_chunk, wark(%r)" % (ex,) # type: ignore
|
3442
3460
|
|
3443
|
-
for chash in
|
3461
|
+
for chash in locked:
|
3444
3462
|
job["busy"].pop(chash, None)
|
3445
3463
|
|
3446
3464
|
try:
|
3447
|
-
for chash in
|
3465
|
+
for chash in written:
|
3448
3466
|
job["need"].remove(chash)
|
3449
3467
|
except Exception as ex:
|
3450
3468
|
return -2, "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
|
@@ -3496,7 +3514,7 @@ class Up2k(object):
|
|
3496
3514
|
except:
|
3497
3515
|
self.log("failed to utime ({}, {})".format(dst, times))
|
3498
3516
|
|
3499
|
-
zs = "prel name lmod size ptop vtop wark host user addr"
|
3517
|
+
zs = "prel name lmod size ptop vtop wark dwrk host user addr"
|
3500
3518
|
z2 = [job[x] for x in zs.split()]
|
3501
3519
|
wake_sr = False
|
3502
3520
|
try:
|
@@ -3569,6 +3587,7 @@ class Up2k(object):
|
|
3569
3587
|
ptop ,
|
3570
3588
|
vtop ,
|
3571
3589
|
wark ,
|
3590
|
+
dwark ,
|
3572
3591
|
host ,
|
3573
3592
|
usr ,
|
3574
3593
|
ip ,
|
@@ -3591,6 +3610,7 @@ class Up2k(object):
|
|
3591
3610
|
ptop,
|
3592
3611
|
vtop,
|
3593
3612
|
wark,
|
3613
|
+
dwark,
|
3594
3614
|
host,
|
3595
3615
|
usr,
|
3596
3616
|
ip,
|
@@ -3604,7 +3624,7 @@ class Up2k(object):
|
|
3604
3624
|
raise
|
3605
3625
|
|
3606
3626
|
if "e2t" in self.flags[ptop]:
|
3607
|
-
self.tagq.put((ptop,
|
3627
|
+
self.tagq.put((ptop, dwark, rd, fn, sz, ip, at))
|
3608
3628
|
self.n_tagq += 1
|
3609
3629
|
|
3610
3630
|
return True
|
@@ -3631,6 +3651,7 @@ class Up2k(object):
|
|
3631
3651
|
ptop ,
|
3632
3652
|
vtop ,
|
3633
3653
|
wark ,
|
3654
|
+
dwark ,
|
3634
3655
|
host ,
|
3635
3656
|
usr ,
|
3636
3657
|
ip ,
|
@@ -3647,12 +3668,12 @@ class Up2k(object):
|
|
3647
3668
|
db_ip = "1.1.1.1" if self.args.no_db_ip else ip
|
3648
3669
|
|
3649
3670
|
sql = "insert into up values (?,?,?,?,?,?,?)"
|
3650
|
-
v = (
|
3671
|
+
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
3651
3672
|
try:
|
3652
3673
|
db.execute(sql, v)
|
3653
3674
|
except:
|
3654
3675
|
rd, fn = s3enc(self.mem_cur, rd, fn)
|
3655
|
-
v = (
|
3676
|
+
v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
3656
3677
|
db.execute(sql, v)
|
3657
3678
|
|
3658
3679
|
self.volsize[db] += sz
|
@@ -3696,10 +3717,10 @@ class Up2k(object):
|
|
3696
3717
|
for cd in cds:
|
3697
3718
|
# one for each unique cooldown duration
|
3698
3719
|
try:
|
3699
|
-
db.execute(q, (cd,
|
3720
|
+
db.execute(q, (cd, dwark[:16], rd, fn))
|
3700
3721
|
except:
|
3701
3722
|
rd, fn = s3enc(self.mem_cur, rd, fn)
|
3702
|
-
db.execute(q, (cd,
|
3723
|
+
db.execute(q, (cd, dwark[:16], rd, fn))
|
3703
3724
|
|
3704
3725
|
if self.xiu_asleep:
|
3705
3726
|
self.xiu_asleep = False
|
@@ -4153,6 +4174,7 @@ class Up2k(object):
|
|
4153
4174
|
dvn.realpath,
|
4154
4175
|
dvn.vpath,
|
4155
4176
|
w,
|
4177
|
+
w,
|
4156
4178
|
"",
|
4157
4179
|
"",
|
4158
4180
|
ip or "",
|
@@ -4834,6 +4856,7 @@ class Up2k(object):
|
|
4834
4856
|
ptop,
|
4835
4857
|
vtop,
|
4836
4858
|
wark,
|
4859
|
+
wark,
|
4837
4860
|
"",
|
4838
4861
|
usr,
|
4839
4862
|
ip,
|
copyparty/util.py
CHANGED
@@ -1082,7 +1082,7 @@ class Magician(object):
|
|
1082
1082
|
return ret
|
1083
1083
|
|
1084
1084
|
mime = magic.from_file(fpath, mime=True)
|
1085
|
-
mime = re.split("[; ]", mime, 1)[0]
|
1085
|
+
mime = re.split("[; ]", mime, maxsplit=1)[0]
|
1086
1086
|
try:
|
1087
1087
|
return EXTS[mime]
|
1088
1088
|
except:
|
@@ -3532,9 +3532,9 @@ def stat_resource(E , name ):
|
|
3532
3532
|
return None
|
3533
3533
|
|
3534
3534
|
|
3535
|
-
def _find_impresource(
|
3535
|
+
def _find_impresource(pkg , name ):
|
3536
3536
|
try:
|
3537
|
-
files = impresources.files(
|
3537
|
+
files = impresources.files(pkg)
|
3538
3538
|
except ImportError:
|
3539
3539
|
return None
|
3540
3540
|
|
@@ -3544,7 +3544,7 @@ def _find_impresource(E , name ):
|
|
3544
3544
|
_rescache_has = {}
|
3545
3545
|
|
3546
3546
|
|
3547
|
-
def _has_resource(
|
3547
|
+
def _has_resource(name ):
|
3548
3548
|
try:
|
3549
3549
|
return _rescache_has[name]
|
3550
3550
|
except:
|
@@ -3553,14 +3553,16 @@ def _has_resource(E , name ):
|
|
3553
3553
|
if len(_rescache_has) > 999:
|
3554
3554
|
_rescache_has.clear()
|
3555
3555
|
|
3556
|
+
pkg = sys.modules[__package__]
|
3557
|
+
|
3556
3558
|
if impresources:
|
3557
|
-
res = _find_impresource(
|
3559
|
+
res = _find_impresource(pkg, name)
|
3558
3560
|
if res and res.is_file():
|
3559
3561
|
_rescache_has[name] = True
|
3560
3562
|
return True
|
3561
3563
|
|
3562
3564
|
if pkg_resources:
|
3563
|
-
if _pkg_resource_exists(
|
3565
|
+
if _pkg_resource_exists(pkg.__name__, name):
|
3564
3566
|
_rescache_has[name] = True
|
3565
3567
|
return True
|
3566
3568
|
|
@@ -3569,14 +3571,14 @@ def _has_resource(E , name ):
|
|
3569
3571
|
|
3570
3572
|
|
3571
3573
|
def has_resource(E , name ):
|
3572
|
-
return _has_resource(
|
3574
|
+
return _has_resource(name) or os.path.exists(os.path.join(E.mod, name))
|
3573
3575
|
|
3574
3576
|
|
3575
3577
|
def load_resource(E , name , mode="rb") :
|
3576
3578
|
enc = None if "b" in mode else "utf-8"
|
3577
3579
|
|
3578
3580
|
if impresources:
|
3579
|
-
res = _find_impresource(
|
3581
|
+
res = _find_impresource(sys.modules[__package__], name)
|
3580
3582
|
if res and res.is_file():
|
3581
3583
|
if enc:
|
3582
3584
|
return res.open(mode, encoding=enc)
|
@@ -3585,8 +3587,9 @@ def load_resource(E , name , mode="rb") :
|
|
3585
3587
|
return res.open(mode)
|
3586
3588
|
|
3587
3589
|
if pkg_resources:
|
3588
|
-
|
3589
|
-
|
3590
|
+
pkg = sys.modules[__package__]
|
3591
|
+
if _pkg_resource_exists(pkg.__name__, name):
|
3592
|
+
stream = pkg_resources.resource_stream(pkg.__name__, name)
|
3590
3593
|
if enc:
|
3591
3594
|
stream = codecs.getreader(enc)(stream)
|
3592
3595
|
return stream
|
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.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.15.
|
3
|
+
Version: 1.15.6
|
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
|
@@ -137,9 +137,10 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
137
137
|
* [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
|
138
138
|
* [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
|
139
139
|
* [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
|
140
|
-
* [hiding from google](#hiding-from-google) - tell search engines you
|
140
|
+
* [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed
|
141
141
|
* [themes](#themes)
|
142
142
|
* [complete examples](#complete-examples)
|
143
|
+
* [listen on port 80 and 443](#listen-on-port-80-and-443) - become a *real* webserver
|
143
144
|
* [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
|
144
145
|
* [real-ip](#real-ip) - teaching copyparty how to see client IPs
|
145
146
|
* [prometheus](#prometheus) - metrics/stats can be enabled
|
@@ -168,7 +169,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
168
169
|
* [https](#https) - both HTTP and HTTPS are accepted
|
169
170
|
* [recovering from crashes](#recovering-from-crashes)
|
170
171
|
* [client crashes](#client-crashes)
|
171
|
-
* [
|
172
|
+
* [firefox wsod](#firefox-wsod) - firefox 87 can crash during uploads
|
172
173
|
* [HTTP API](#HTTP-API) - see [devnotes](./docs/devnotes.md#http-api)
|
173
174
|
* [dependencies](#dependencies) - mandatory deps
|
174
175
|
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
|
@@ -635,7 +636,7 @@ it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video f
|
|
635
636
|
* pyvips is 3x faster than Pillow, Pillow is 3x faster than FFmpeg
|
636
637
|
* disable thumbnails for specific volumes with volflag `dthumb` for all, or `dvthumb` / `dathumb` / `dithumb` for video/audio/images only
|
637
638
|
|
638
|
-
audio files are
|
639
|
+
audio files are converted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
|
639
640
|
|
640
641
|
images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
|
641
642
|
* the order is significant, so if both `cover.png` and `folder.jpg` exist in a folder, it will pick the first matching `--th-covers` entry (`folder.jpg`)
|
@@ -721,7 +722,7 @@ see [up2k](./docs/devnotes.md#up2k) for details on how it works, or watch a [dem
|
|
721
722
|
|
722
723
|
**protip:** if you enable `favicon` in the `[⚙️] settings` tab (by typing something into the textbox), the icon in the browser tab will indicate upload progress -- also, the `[🔔]` and/or `[🔊]` switches enable visible and/or audible notifications on upload completion
|
723
724
|
|
724
|
-
the up2k UI is the epitome of polished
|
725
|
+
the up2k UI is the epitome of polished intuitive experiences:
|
725
726
|
* "parallel uploads" specifies how many chunks to upload at the same time
|
726
727
|
* `[🏃]` analysis of other files should continue while one is uploading
|
727
728
|
* `[🥔]` shows a simpler UI for faster uploads from slow devices
|
@@ -770,7 +771,7 @@ you can unpost even if you don't have regular move/delete access, however only f
|
|
770
771
|
|
771
772
|
### self-destruct
|
772
773
|
|
773
|
-
uploads can be given a lifetime,
|
774
|
+
uploads can be given a lifetime, after which they expire / self-destruct
|
774
775
|
|
775
776
|
the feature must be enabled per-volume with the `lifetime` [upload rule](#upload-rules) which sets the upper limit for how long a file gets to stay on the server
|
776
777
|
|
@@ -797,7 +798,7 @@ the control-panel shows the ETA for all incoming files , but only for files bei
|
|
797
798
|
|
798
799
|
cut/paste, rename, and delete files/folders (if you have permission)
|
799
800
|
|
800
|
-
file selection: click somewhere on the line (not the link
|
801
|
+
file selection: click somewhere on the line (not the link itself), then:
|
801
802
|
* `space` to toggle
|
802
803
|
* `up/down` to move
|
803
804
|
* `shift-up/down` to move-and-select
|
@@ -831,6 +832,7 @@ semi-intentional limitations:
|
|
831
832
|
|
832
833
|
* cleanup of expired shares only works when global option `e2d` is set, and/or at least one volume on the server has volflag `e2d`
|
833
834
|
* only folders from the same volume are shared; if you are sharing a folder which contains other volumes, then the contents of those volumes will not be available
|
835
|
+
* related to [IdP volumes being forgotten on shutdown](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#idp-volumes-are-forgotten-on-shutdown), any shares pointing into a user's IdP volume will be unavailable until that user makes their first request after a restart
|
834
836
|
* no option to "delete after first access" because tricky
|
835
837
|
* when linking something to discord (for example) it'll get accessed by their scraper and that would count as a hit
|
836
838
|
* browsers wouldn't be able to resume a broken download unless the requester's IP gets allowlisted for X minutes (ref. tricky)
|
@@ -990,6 +992,8 @@ see [./srv/expand/](./srv/expand/) for usage and examples
|
|
990
992
|
|
991
993
|
* files named `README.md` / `readme.md` will be rendered after directory listings unless `--no-readme` (but `.epilogue.html` takes precedence)
|
992
994
|
|
995
|
+
* and `PREADME.md` / `preadme.md` is shown above directory listings unless `--no-readme` or `.prologue.html`
|
996
|
+
|
993
997
|
* `README.md` and `*logue.html` can contain placeholder values which are replaced server-side before embedding into directory listings; see `--help-exp`
|
994
998
|
|
995
999
|
|
@@ -1041,7 +1045,11 @@ uses [multicast dns](https://en.wikipedia.org/wiki/Multicast_DNS) to give copypa
|
|
1041
1045
|
|
1042
1046
|
all enabled services ([webdav](#webdav-server), [ftp](#ftp-server), [smb](#smb-server)) will appear in mDNS-aware file managers (KDE, gnome, macOS, ...)
|
1043
1047
|
|
1044
|
-
the domain will be
|
1048
|
+
the domain will be `partybox.local` if the machine's hostname is `partybox` unless `--name` specifies something else
|
1049
|
+
|
1050
|
+
and the web-UI will be available at http://partybox.local:3923/
|
1051
|
+
|
1052
|
+
* if you want to get rid of the `:3923` so you can use http://partybox.local/ instead then see [listen on port 80 and 443](#listen-on-port-80-and-443)
|
1045
1053
|
|
1046
1054
|
|
1047
1055
|
### ssdp
|
@@ -1067,7 +1075,7 @@ print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/
|
|
1067
1075
|
* `--qrz 1` forces 1x zoom instead of autoscaling to fit the terminal size
|
1068
1076
|
* 1x may render incorrectly on some terminals/fonts, but 2x should always work
|
1069
1077
|
|
1070
|
-
it uses the server hostname if [mdns](#mdns) is
|
1078
|
+
it uses the server hostname if [mdns](#mdns) is enabled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain
|
1071
1079
|
|
1072
1080
|
|
1073
1081
|
## ftp server
|
@@ -1092,7 +1100,7 @@ some recommended FTP / FTPS clients; `wark` = example password:
|
|
1092
1100
|
|
1093
1101
|
## webdav server
|
1094
1102
|
|
1095
|
-
with read-write support, supports winXP and later, macos, nautilus/gvfs ... a
|
1103
|
+
with read-write support, supports winXP and later, macos, nautilus/gvfs ... a great way to [access copyparty straight from the file explorer in your OS](#mount-as-drive)
|
1096
1104
|
|
1097
1105
|
click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to see connection instructions for windows, linux, macos
|
1098
1106
|
|
@@ -1196,8 +1204,8 @@ authenticate with one of the following:
|
|
1196
1204
|
tweaking the ui
|
1197
1205
|
|
1198
1206
|
* set default sort order globally with `--sort` or per-volume with the `sort` volflag; specify one or more comma-separated columns to sort by, and prefix the column name with `-` for reverse sort
|
1199
|
-
* the column names you can use are visible as tooltips when hovering over the column headers in the directory listing, for example `href ext sz ts tags/.up_at tags/
|
1200
|
-
* to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/
|
1207
|
+
* the column names you can use are visible as tooltips when hovering over the column headers in the directory listing, for example `href ext sz ts tags/.up_at tags/Circle tags/.tn tags/Artist tags/Title`
|
1208
|
+
* to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Circle,tags/.tn,tags/Artist,tags/Title,href`
|
1201
1209
|
* to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
|
1202
1210
|
|
1203
1211
|
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag, or to add your own translation
|
@@ -1220,7 +1228,11 @@ if you want to entirely replace the copyparty response with your own jinja2 temp
|
|
1220
1228
|
|
1221
1229
|
enable symlink-based upload deduplication globally with `--dedup` or per-volume with volflag `dedup`
|
1222
1230
|
|
1223
|
-
when someone tries to upload a file that already exists on the server, the upload will be politely declined and
|
1231
|
+
by default, when someone tries to upload a file that already exists on the server, the upload will be politely declined, and the server will copy the existing file over to where the upload would have gone
|
1232
|
+
|
1233
|
+
if you enable deduplication with `--dedup` then it'll create a symlink instead of a full copy, thus reducing disk space usage
|
1234
|
+
|
1235
|
+
* on the contrary, if your server is hooked up to s3-glacier or similar storage where reading is expensive, and you cannot use `--safe-dedup=1` because you have other software tampering with your files, so you want to entirely disable detection of duplicate data instead, then you can specify `--no-clone` globally or `noclone` as a volflag
|
1224
1236
|
|
1225
1237
|
**warning:** when enabling dedup, you should also:
|
1226
1238
|
* enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
|
@@ -1261,7 +1273,7 @@ through arguments:
|
|
1261
1273
|
* `-e2t` enables metadata indexing on upload
|
1262
1274
|
* `-e2ts` also scans for tags in all files that don't have tags yet
|
1263
1275
|
* `-e2tsr` also deletes all existing tags, doing a full reindex
|
1264
|
-
* `-e2v`
|
1276
|
+
* `-e2v` verifies file integrity at startup, comparing hashes from the db
|
1265
1277
|
* `-e2vu` patches the database with the new hashes from the filesystem
|
1266
1278
|
* `-e2vp` panics and kills copyparty instead
|
1267
1279
|
|
@@ -1478,7 +1490,7 @@ replace 404 and 403 errors with something completely different (that's it for no
|
|
1478
1490
|
|
1479
1491
|
replace copyparty passwords with oauth and such
|
1480
1492
|
|
1481
|
-
you can disable the built-in password-based login
|
1493
|
+
you can disable the built-in password-based login system, and instead replace it with a separate piece of software (an identity provider) which will then handle authenticating / authorizing of users; this makes it possible to login with passkeys / fido2 / webauthn / yubikey / ldap / active directory / oauth / many other single-sign-on contraptions
|
1482
1494
|
|
1483
1495
|
a popular choice is [Authelia](https://www.authelia.com/) (config-file based), another one is [authentik](https://goauthentik.io/) (GUI-based, more complex)
|
1484
1496
|
|
@@ -1505,7 +1517,7 @@ if permitted, users can change their own passwords in the control-panel
|
|
1505
1517
|
|
1506
1518
|
* if you run multiple copyparty instances with different users you *almost definitely* want to specify separate DBs for each instance
|
1507
1519
|
|
1508
|
-
* if [password hashing](#password-hashing) is
|
1520
|
+
* if [password hashing](#password-hashing) is enabled, the passwords in the db are also hashed
|
1509
1521
|
|
1510
1522
|
* ...which means that all user-defined passwords will be forgotten if you change password-hashing settings
|
1511
1523
|
|
@@ -1525,7 +1537,7 @@ you may improve performance by specifying larger values for `--iobuf` / `--s-rd-
|
|
1525
1537
|
|
1526
1538
|
## hiding from google
|
1527
1539
|
|
1528
|
-
tell search engines you
|
1540
|
+
tell search engines you don't wanna be indexed, either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:
|
1529
1541
|
|
1530
1542
|
* `--no-robots` adds HTTP (`X-Robots-Tag`) and HTML (`<meta>`) headers with `noindex, nofollow` globally
|
1531
1543
|
* volflag `[...]:c,norobots` does the same thing for that single volume
|
@@ -1600,6 +1612,33 @@ if you want to change the fonts, see [./docs/rice/](./docs/rice/)
|
|
1600
1612
|
`-lo log/cpp-%Y-%m%d-%H%M%S.txt.xz`
|
1601
1613
|
|
1602
1614
|
|
1615
|
+
## listen on port 80 and 443
|
1616
|
+
|
1617
|
+
become a *real* webserver which people can access by just going to your IP or domain without specifying a port
|
1618
|
+
|
1619
|
+
**if you're on windows,** then you just need to add the commandline argument `-p 80,443` and you're done! nice
|
1620
|
+
|
1621
|
+
**if you're on macos,** sorry, I don't know
|
1622
|
+
|
1623
|
+
**if you're on Linux,** you have the following 4 options:
|
1624
|
+
|
1625
|
+
* **option 1:** set up a [reverse-proxy](#reverse-proxy) -- this one makes a lot of sense if you're running on a proper headless server, because that way you get real HTTPS too
|
1626
|
+
|
1627
|
+
* **option 2:** NAT to port 3923 -- this is cumbersome since you'll need to do it every time you reboot, and the exact command may depend on your linux distribution:
|
1628
|
+
```bash
|
1629
|
+
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3923
|
1630
|
+
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 3923
|
1631
|
+
```
|
1632
|
+
|
1633
|
+
* **option 3:** disable the [security policy](https://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html) which prevents the use of 80 and 443; this is *probably* fine:
|
1634
|
+
```
|
1635
|
+
setcap CAP_NET_BIND_SERVICE=+eip $(realpath $(which python))
|
1636
|
+
python copyparty-sfx.py -p 80,443
|
1637
|
+
```
|
1638
|
+
|
1639
|
+
* **option 4:** run copyparty as root (please don't)
|
1640
|
+
|
1641
|
+
|
1603
1642
|
## reverse-proxy
|
1604
1643
|
|
1605
1644
|
running copyparty next to other websites hosted on an existing webserver such as nginx, caddy, or apache
|
@@ -1948,7 +1987,7 @@ interact with copyparty using non-browser clients
|
|
1948
1987
|
|
1949
1988
|
* [igloo irc](https://iglooirc.com/): Method: `post` Host: `https://you.com/up/?want=url&pw=hunter2` Multipart: `yes` File parameter: `f`
|
1950
1989
|
|
1951
|
-
copyparty returns a truncated sha512sum of your PUT/POST as base64; you can generate the same checksum locally to verify
|
1990
|
+
copyparty returns a truncated sha512sum of your PUT/POST as base64; you can generate the same checksum locally to verify uploads:
|
1952
1991
|
|
1953
1992
|
b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|tr '+/' '-_'|head -c44;}
|
1954
1993
|
b512 <movie.mkv
|
@@ -2048,7 +2087,7 @@ when uploading files,
|
|
2048
2087
|
* up to 30% faster uploads if you hide the upload status list by switching away from the `[🚀]` up2k ui-tab (or closing it)
|
2049
2088
|
* optionally you can switch to the lightweight potato ui by clicking the `[🥔]`
|
2050
2089
|
* switching to another browser-tab also works, the favicon will update every 10 seconds in that case
|
2051
|
-
* unlikely to be a problem, but can happen when
|
2090
|
+
* unlikely to be a problem, but can happen when uploading many small files, or your internet is too fast, or PC too slow
|
2052
2091
|
|
2053
2092
|
|
2054
2093
|
# security
|
@@ -2096,7 +2135,7 @@ other misc notes:
|
|
2096
2135
|
|
2097
2136
|
behavior that might be unexpected
|
2098
2137
|
|
2099
|
-
* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
|
2138
|
+
* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `PREADME.md` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
|
2100
2139
|
* users can submit `<script>`s which autorun (in a sandbox) for other visitors in a few ways;
|
2101
2140
|
* uploading a `README.md` -- avoid with `--no-readme`
|
2102
2141
|
* renaming `some.html` to `.epilogue.html` -- avoid with either `--no-logues` or `--no-dot-ren`
|
@@ -2174,13 +2213,13 @@ if [cfssl](https://github.com/cloudflare/cfssl/releases/latest) is installed, co
|
|
2174
2213
|
|
2175
2214
|
## client crashes
|
2176
2215
|
|
2177
|
-
###
|
2216
|
+
### firefox wsod
|
2178
2217
|
|
2179
2218
|
firefox 87 can crash during uploads -- the entire browser goes, including all other browser tabs, everything turns white
|
2180
2219
|
|
2181
2220
|
however you can hit `F12` in the up2k tab and use the devtools to see how far you got in the uploads:
|
2182
2221
|
|
2183
|
-
* get a complete list of all uploads, organized by
|
2222
|
+
* get a complete list of all uploads, organized by status (ok / no-good / busy / queued):
|
2184
2223
|
`var tabs = { ok:[], ng:[], bz:[], q:[] }; for (var a of up2k.ui.tab) tabs[a.in].push(a); tabs`
|
2185
2224
|
|
2186
2225
|
* list of filenames which failed:
|
@@ -2297,7 +2336,7 @@ then again, if you are already into downloading shady binaries from the internet
|
|
2297
2336
|
|
2298
2337
|
## zipapp
|
2299
2338
|
|
2300
|
-
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it does not unpack any
|
2339
|
+
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2301
2340
|
|
2302
2341
|
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
|
2303
2342
|
|
@@ -1,38 +1,38 @@
|
|
1
|
-
copyparty/__init__.py,sha256=
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
1
|
+
copyparty/__init__.py,sha256=Chqw7uXX4r_-a2p6-xthrrqVHFI4aZdW45sWU7UvqeE,2597
|
2
|
+
copyparty/__main__.py,sha256=6yMejKWZGgZBsIJ3A_lkpieMdpd0q7-nUgbsFTrpcR4,109765
|
3
|
+
copyparty/__version__.py,sha256=cN7xKCyFmKnXASldOC8xD6LcXlzSFiM6G6TpRgB0zh4,258
|
4
|
+
copyparty/authsrv.py,sha256=clsE8whf32_eIRES1r7VvsVkc4OiJCjiBEJM6pYkwwI,98670
|
5
5
|
copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
|
6
6
|
copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
|
7
7
|
copyparty/broker_thr.py,sha256=MXrwjusP0z1LPURUhi5jx_TL3jrXhYcDrJPDSKu6EEU,1705
|
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=33nLatBUmzRFKQ4KpoQei3ZY6EqRrlaHpQnvCNFXcHI,10112
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
|
13
13
|
copyparty/ftpd.py,sha256=VTx0gZUdK_0xRORMZT1XymSYvsXsCZs6-wsM2J4raYA,17459
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=QWOSdYhOxU3i-uCeoUUCEYt2O6pXbi3KjXoTr0YZm4Y,191346
|
15
15
|
copyparty/httpconn.py,sha256=DN09_r3cPFN4UGCVtAhnIH6cBIDKF-fe4IgMeoHUnSc,6809
|
16
16
|
copyparty/httpsrv.py,sha256=TnfqA4edgO187y8fV0Q-lchnnD2bMSBTPZgncEjGMhY,17009
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
|
19
19
|
copyparty/metrics.py,sha256=aV09nntEmKMIyde8xoPtj1ehDOQVQOHchRF4uMMNzqM,8855
|
20
|
-
copyparty/mtag.py,sha256=
|
20
|
+
copyparty/mtag.py,sha256=8WGjEn0T0Ri9ww1yBpLUnFHZiTQMye1BMXL6SkK3MRo,18893
|
21
21
|
copyparty/multicast.py,sha256=Ha27l2oATEa-Qo2WOzkeRgjAm6G_YDCfbVJWR-ao2UE,12319
|
22
22
|
copyparty/pwhash.py,sha256=AdLMLyIi2IDhGtbKIQOswKUxWvO7ARYYRF_ThsryOoc,4124
|
23
23
|
copyparty/smbd.py,sha256=Or7RF13cl1r3ncnpVh8BqyAGqH2Oa04O9iPZWCoB0Bo,14609
|
24
|
-
copyparty/ssdp.py,sha256=
|
24
|
+
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=JTMrQwcWH85hXB_cKG206eDZ967WZDGaP00AWvl_gB0,3214
|
27
27
|
copyparty/svchub.py,sha256=qLG2CE8VHro7I6FnYMwiij0LcBC5lJw-hiucBag3NHY,39939
|
28
28
|
copyparty/szip.py,sha256=sDypi1_yR6-62fIZ_3D0L9PfIzCUiK_3JqcaJCvTBCs,8601
|
29
|
-
copyparty/tcpsrv.py,sha256=
|
29
|
+
copyparty/tcpsrv.py,sha256=l_vb9FoF0AJur0IoqHNUSBDqMgBO_MRUZeDszi1UNfY,19881
|
30
30
|
copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
|
31
31
|
copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
|
32
32
|
copyparty/th_srv.py,sha256=hI9wY1E_9N9Cgqvtr8zADeVqqiLGTiTdAnYAA7WFvJw,29346
|
33
33
|
copyparty/u2idx.py,sha256=JjgqwgJBNj6sTn4PJfuqM3VEHqlmoyGC5bk4_92K2h0,13414
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=UlycCjhC0C1tMwZvHXlkoXRTaFIEbivg-sXGEBQCBCs,164564
|
35
|
+
copyparty/util.py,sha256=4VKiIzGPY6kb7PkTL3lfD1z-ewQKMQM5_J3meGirlS0,91338
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
38
38
|
copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
|
@@ -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=4dS8-r4si84ca71l98672ahnRI86Aq95MU-bc5knykk,7962
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
58
|
+
copyparty/web/browser.css.gz,sha256=4bAS9Xkl2fflhaxRSRSVoYQcpXsg1mCWxsYjId7phbU,11610
|
59
59
|
copyparty/web/browser.html,sha256=ISpfvWEawufJCYZIqvuXiyUgiXgjmOTtScz4zrEaypI,4870
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=0WhsCM1Kr0PFryxz7hF31apLgvsJac4jWy8_ePfp_40,84989
|
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
|
@@ -106,9 +106,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
106
106
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
107
107
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
108
108
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
109
|
-
copyparty-1.15.
|
110
|
-
copyparty-1.15.
|
111
|
-
copyparty-1.15.
|
112
|
-
copyparty-1.15.
|
113
|
-
copyparty-1.15.
|
114
|
-
copyparty-1.15.
|
109
|
+
copyparty-1.15.6.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
110
|
+
copyparty-1.15.6.dist-info/METADATA,sha256=rFYHpfJWHe5_4nuyJ9BCvq4YxRoaDRIgdY-VBjjqLqs,137956
|
111
|
+
copyparty-1.15.6.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
112
|
+
copyparty-1.15.6.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
113
|
+
copyparty-1.15.6.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
114
|
+
copyparty-1.15.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|