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 CHANGED
@@ -105,7 +105,6 @@ RES = set(zs.strip().split("\n"))
105
105
 
106
106
  class EnvParams(object):
107
107
  def __init__(self) :
108
- self.pkg = None
109
108
  self.t0 = time.time()
110
109
  self.mod = ""
111
110
  self.cfg = ""
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 intersesting ways for it to break unexpectedly
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
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 4)
3
+ VERSION = (1, 15, 6)
4
4
  CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 4)
5
+ BUILD_DT = (2024, 10, 12)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
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/gruop is mapped when setting perms;
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
- "nodupe": "rejects existing files (instead of symlinking them)",
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(locked), num_left), 6)
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, chekc that timestamp matches the client's
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 enumerate([".prologue.html", ".epilogue.html"]):
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
- readme = ""
3249
- if not self.args.no_readme and not logues[1]:
3250
- if lnames is None:
3251
- fns = ["README.md", "readme.md"]
3252
- elif "readme.md" in lnames:
3253
- fns = [lnames["readme.md"]]
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
- readme = f.read().decode("utf-8")
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
- return logues, readme
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
- "# {}: {}".format(x, ls[x])
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, readme = self._add_logues(vn, abspath, None)
5135
+ logues, readmes = self._add_logues(vn, abspath, None)
5120
5136
  ls_ret["logues"] = j2a["logues"] = logues
5121
- ls_ret["readme"] = cgv["readme"] = readme
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, readme = self._add_logues(vn, abspath, lnames)
5393
+ logues, readmes = self._add_logues(vn, abspath, lnames)
5378
5394
  ls_ret["logues"] = j2a["logues"] = logues
5379
- ls_ret["readme"] = cgv["readme"] = readme
5395
+ ls_ret["readmes"] = cgv["readmes"] = readmes
5380
5396
 
5381
- if not files and not dirs and not readme and not logues[0] and not logues[1]:
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
- if next((x for x in files if x["name"] == doc), None):
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 rkeobo
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 connectino
83
+ return False # close connection
84
84
 
85
85
 
86
86
  class SSDPd(MCast):
copyparty/tcpsrv.py CHANGED
@@ -92,7 +92,7 @@ class TcpSrv(object):
92
92
  continue
93
93
 
94
94
  # binding 0.0.0.0 after :: fails on dualstack
95
- # but is necessary on non-dualstakc
95
+ # but is necessary on non-dualstack
96
96
  if successful_binds:
97
97
  continue
98
98
 
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
- fp = djoin(job["ptop"], job["prel"], job["name"])
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 = [wark]
2818
+ argv = [dwark]
2806
2819
  else:
2807
2820
  q = r"select * from up where substr(w,1,16)=? and +w=?"
2808
- argv = [wark[:16], wark]
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
- wark, st.st_size, dsize, st.st_mtime, dtime, dp_abs
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
- dhashes, st = self._hashlist_from_file(orig_ap)
2882
- dwark = up2k_wark_from_hashlist(self.salt, st.st_size, dhashes)
2883
- if wark != dwark:
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 % (dwark, wark, orig_ap))
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
- dhashes, _ = self._hashlist_from_file(orig_ap)
2950
- dwark = up2k_wark_from_hashlist(self.salt, st.st_size, dhashes)
2951
- if wark != dwark:
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 % (dwark, wark, orig_ap))
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"], wark[:16], job["prel"], job["name"]))
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 , chashes
3443
+ self, ptop , wark , written , locked
3426
3444
  ) :
3427
3445
  with self.mutex, self.reg_mutex:
3428
- return self._confirm_chunks(ptop, wark, chashes)
3446
+ return self._confirm_chunks(ptop, wark, written, locked)
3429
3447
 
3430
3448
  def _confirm_chunks(
3431
- self, ptop , wark , chashes
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 chashes:
3461
+ for chash in locked:
3444
3462
  job["busy"].pop(chash, None)
3445
3463
 
3446
3464
  try:
3447
- for chash in chashes:
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, wark, rd, fn, sz, ip, at))
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 = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
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 = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
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, wark[:16], rd, fn))
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, wark[:16], rd, fn))
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(E , name ):
3535
+ def _find_impresource(pkg , name ):
3536
3536
  try:
3537
- files = impresources.files(E.pkg)
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(E , name ):
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(E, name)
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(E.pkg.__name__, name):
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(E, name) or os.path.exists(os.path.join(E.mod, name))
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(E, name)
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
- if _pkg_resource_exists(E.pkg.__name__, name):
3589
- stream = pkg_resources.resource_stream(E.pkg.__name__, name)
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
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.15.4
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 dont wanna be indexed
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
- * [frefox wsod](#frefox-wsod) - firefox 87 can crash during uploads
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 covnerted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
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 inutitive experiences:
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, afer which they expire / self-destruct
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 itsef), then:
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 http://partybox.local if the machine's hostname is `partybox` unless `--name` specifies soemthing else
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 enbled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain
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 greay way to [access copyparty straight from the file explorer in your OS](#mount-as-drive)
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/Cirle tags/.tn tags/Artist tags/Title`
1200
- * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Cirle,tags/.tn,tags/Artist,tags/Title,href`
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 a symlink is created instead, pointing to the nearest copy on disk, thus reducinc disk space usage
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` verfies file integrity at startup, comparing hashes from the db
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 sysem, 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
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 enbled, the passwords in the db are also hashed
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 dont wanna be indexed, either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:
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 uplaods:
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 uploding many small files, or your internet is too fast, or PC too slow
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
- ### frefox wsod
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 statuts (ok / no-good / busy / queued):
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 temporay 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
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=wkz_fW1qOsy4bUb6UOX45wyoM6eMXZrH_pbbJTHyhx4,2622
2
- copyparty/__main__.py,sha256=haJukP40kmpE0HidO3bY2mmohe-H4ih_cwFTullxrlE,109570
3
- copyparty/__version__.py,sha256=QdadkcRd9LbeLxEk1w-II6vFXxoK9pVhEgKcLiaEbOQ,257
4
- copyparty/authsrv.py,sha256=Uw_RaCJIsXUzjYih4R2VLQOkDfUePLfidiW5ERvCtMo,98670
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=M9OEsKmsrWm5ibh6mTCevdwoBKSN1YrlrulPoHbr7uY,10000
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=eukvfi0OzIrAW6ICg-DDA3NoFBPPRQHvF_vuOiJ9ufc,190587
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=0UIti7JyB6j8rPJ1SlC7JmwlqDfBMjQhXgncxscJGPg,18893
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=8iyF5sqIjATJLWcAtnJa8eadHosOn0CP4ywltzJ7bVY,7023
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=jM_Za64O8LEMfMrU4irJluIJZrU494e2b759r_KhaUQ,19881
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=TzCNQKiC-fxfV2EpXdy1oCA3yExpHyzrH6BmgFrTrJg,163817
35
- copyparty/util.py,sha256=B9BhffBGsRE7f48078646gDABNVerSAuM8EbQpAjlxY,91242
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=mQ0YsAtbRPzaRoVB8IN3aUTHPCpcIfdzzGBOekABSoM,11612
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=70pmmr2lL4pGRrszxyQPrtwDfk4DZ7Ig3kLXWK2E-9c,84921
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.4.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
110
- copyparty-1.15.4.dist-info/METADATA,sha256=BpkzqpVDqywI5NP2j8_M_ccH12Ym3p4wUnNE9sdzYVs,135552
111
- copyparty-1.15.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
112
- copyparty-1.15.4.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
113
- copyparty-1.15.4.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
114
- copyparty-1.15.4.dist-info/RECORD,,
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,,