copyparty 1.19.1__py3-none-any.whl → 1.19.3__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/up2k.py CHANGED
@@ -77,7 +77,7 @@ except:
77
77
  if HAVE_SQLITE3:
78
78
  import sqlite3
79
79
 
80
- DB_VER = 5
80
+ DB_VER = 6
81
81
 
82
82
  if TYPE_CHECKING:
83
83
  from .svchub import SvcHub
@@ -141,6 +141,7 @@ class Up2k(object):
141
141
 
142
142
  self.salt = self.args.warksalt
143
143
  self.r_hash = re.compile("^[0-9a-zA-Z_-]{44}$")
144
+ self.abrt_key = ""
144
145
 
145
146
  self.gid = 0
146
147
  self.gt0 = 0
@@ -897,7 +898,7 @@ class Up2k(object):
897
898
  self.iacct = self.asrv.iacct
898
899
  self.grps = self.asrv.grps
899
900
 
900
- have_e2d = self.args.idp_h_usr or self.args.chpw or self.args.shr
901
+ have_e2d = self.args.have_idp_hdrs or self.args.chpw or self.args.shr
901
902
  vols = list(all_vols.values())
902
903
  t0 = time.time()
903
904
 
@@ -1645,7 +1646,7 @@ class Up2k(object):
1645
1646
  abspath = cdirs + fn
1646
1647
  nohash = reh.search(abspath) if reh else False
1647
1648
 
1648
- sql = "select w, mt, sz, ip, at from up where rd = ? and fn = ?"
1649
+ sql = "select w, mt, sz, ip, at, un from up where rd = ? and fn = ?"
1649
1650
  try:
1650
1651
  c = db.c.execute(sql, (rd, fn))
1651
1652
  except:
@@ -1654,7 +1655,7 @@ class Up2k(object):
1654
1655
  in_db = list(c.fetchall())
1655
1656
  if in_db:
1656
1657
  self.pp.n -= 1
1657
- dw, dts, dsz, ip, at = in_db[0]
1658
+ dw, dts, dsz, ip, at, un = in_db[0]
1658
1659
  if len(in_db) > 1:
1659
1660
  t = "WARN: multiple entries: %r => %r |%d|\n%r"
1660
1661
  rep_db = "\n".join([repr(x) for x in in_db])
@@ -1667,6 +1668,9 @@ class Up2k(object):
1667
1668
  if dts == lmod and dsz == sz and (nohash or dw[0] != "#" or not sz):
1668
1669
  continue
1669
1670
 
1671
+ if un is None:
1672
+ un = ""
1673
+
1670
1674
  t = "reindex %r => %r mtime(%s/%s) size(%s/%s)"
1671
1675
  self.log(t % (top, rp, dts, lmod, dsz, sz))
1672
1676
  self.db_rm(db.c, rd, fn, 0)
@@ -1677,6 +1681,7 @@ class Up2k(object):
1677
1681
  dw = ""
1678
1682
  ip = ""
1679
1683
  at = 0
1684
+ un = ""
1680
1685
 
1681
1686
  self.pp.msg = "a%d %s" % (self.pp.n, abspath)
1682
1687
 
@@ -1702,9 +1707,10 @@ class Up2k(object):
1702
1707
  if dw and dw != wark:
1703
1708
  ip = ""
1704
1709
  at = 0
1710
+ un = ""
1705
1711
 
1706
1712
  # skip upload hooks by not providing vflags
1707
- self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", "", ip, at)
1713
+ self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", un, ip, at)
1708
1714
  db.n += 1
1709
1715
  db.nf += 1
1710
1716
  tfa += 1
@@ -2140,8 +2146,8 @@ class Up2k(object):
2140
2146
 
2141
2147
  with self.mutex:
2142
2148
  try:
2143
- q = "select rd, fn, ip, at from up where substr(w,1,16)=? and +w=?"
2144
- rd, fn, ip, at = cur.execute(q, (w16, w)).fetchone()
2149
+ q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? and +w=?"
2150
+ rd, fn, ip, at, un = cur.execute(q, (w16, w)).fetchone()
2145
2151
  except:
2146
2152
  # file modified/deleted since spooling
2147
2153
  continue
@@ -2160,12 +2166,15 @@ class Up2k(object):
2160
2166
  abspath = djoin(ptop, rd, fn)
2161
2167
  self.pp.msg = "c%d %s" % (nq, abspath)
2162
2168
  if not mpool:
2163
- n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at)
2169
+ n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at, un)
2164
2170
  else:
2171
+ oth_tags = {}
2165
2172
  if ip:
2166
- oth_tags = {"up_ip": ip, "up_at": at}
2167
- else:
2168
- oth_tags = {}
2173
+ oth_tags["up_ip"] = ip
2174
+ if at:
2175
+ oth_tags["up_at"] = at
2176
+ if un:
2177
+ oth_tags["up_by"] = un
2169
2178
 
2170
2179
  mpool.put(Mpqe({}, entags, w, abspath, oth_tags))
2171
2180
  with self.mutex:
@@ -2321,8 +2330,8 @@ class Up2k(object):
2321
2330
  if w in in_progress:
2322
2331
  continue
2323
2332
 
2324
- q = "select rd, fn, ip, at from up where substr(w,1,16)=? limit 1"
2325
- rd, fn, ip, at = cur.execute(q, (w,)).fetchone()
2333
+ q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? limit 1"
2334
+ rd, fn, ip, at, un = cur.execute(q, (w,)).fetchone()
2326
2335
  rd, fn = s3dec(rd, fn)
2327
2336
  abspath = djoin(ptop, rd, fn)
2328
2337
 
@@ -2346,7 +2355,10 @@ class Up2k(object):
2346
2355
 
2347
2356
  if ip:
2348
2357
  oth_tags["up_ip"] = ip
2358
+ if at:
2349
2359
  oth_tags["up_at"] = at
2360
+ if un:
2361
+ oth_tags["up_by"] = un
2350
2362
 
2351
2363
  jobs.append(Mpqe(parsers, set(), w, abspath, oth_tags))
2352
2364
  in_progress[w] = True
@@ -2534,6 +2546,7 @@ class Up2k(object):
2534
2546
  abspath ,
2535
2547
  ip ,
2536
2548
  at ,
2549
+ un ,
2537
2550
  ) :
2538
2551
  """will mutex(main)"""
2539
2552
 
@@ -2553,7 +2566,10 @@ class Up2k(object):
2553
2566
 
2554
2567
  if ip:
2555
2568
  tags["up_ip"] = ip
2569
+ if at:
2556
2570
  tags["up_at"] = at
2571
+ if un:
2572
+ tags["up_by"] = un
2557
2573
 
2558
2574
  with self.mutex:
2559
2575
  return self._tag_file(write_cur, entags, wark, abspath, tags)
@@ -2655,16 +2671,19 @@ class Up2k(object):
2655
2671
  if not existed and ver is None:
2656
2672
  return self._try_create_db(db_path, cur)
2657
2673
 
2658
- if ver == 4:
2674
+ for upver in (4, 5):
2675
+ if ver != upver:
2676
+ continue
2659
2677
  try:
2660
2678
  t = "creating backup before upgrade: "
2661
2679
  cur = self._backup_db(db_path, cur, ver, t)
2662
- self._upgrade_v4(cur)
2663
- ver = 5
2680
+ getattr(self, "_upgrade_v%d" % (upver,))(cur)
2681
+ ver += 1 # type: ignore
2664
2682
  except:
2665
- self.log("WARN: failed to upgrade from v4", 3)
2683
+ self.log("WARN: failed to upgrade from v%d" % (ver,), 3)
2666
2684
 
2667
2685
  if ver == DB_VER:
2686
+ # these no longer serve their intended purpose but they're great as additional sanchks
2668
2687
  self._add_dhash_tab(cur)
2669
2688
  self._add_xiu_tab(cur)
2670
2689
  self._add_cv_tab(cur)
@@ -2765,7 +2784,7 @@ class Up2k(object):
2765
2784
  idx = r"create index up_w on up(w)"
2766
2785
 
2767
2786
  for cmd in [
2768
- r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int)",
2787
+ r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int, un text)",
2769
2788
  r"create index up_vp on up(rd, fn)",
2770
2789
  r"create index up_fn on up(fn)",
2771
2790
  r"create index up_ip on up(ip)",
@@ -2798,6 +2817,15 @@ class Up2k(object):
2798
2817
 
2799
2818
  cur.connection.commit()
2800
2819
 
2820
+ def _upgrade_v5(self, cur ) :
2821
+ for cmd in [
2822
+ r"alter table up add column un text",
2823
+ r"update kv set v=6 where k='sver'",
2824
+ ]:
2825
+ cur.execute(cmd)
2826
+
2827
+ cur.connection.commit()
2828
+
2801
2829
  def _add_dhash_tab(self, cur ) :
2802
2830
  # v5 -> v5a
2803
2831
  try:
@@ -2995,7 +3023,7 @@ class Up2k(object):
2995
3023
  argv = [dwark[:16], dwark]
2996
3024
 
2997
3025
  c2 = cur.execute(q, tuple(argv))
2998
- for _, dtime, dsize, dp_dir, dp_fn, ip, at in c2:
3026
+ for _, dtime, dsize, dp_dir, dp_fn, ip, at, _ in c2:
2999
3027
  if dp_dir.startswith("//") or dp_fn.startswith("//"):
3000
3028
  dp_dir, dp_fn = s3dec(dp_dir, dp_fn)
3001
3029
 
@@ -3416,7 +3444,7 @@ class Up2k(object):
3416
3444
  try:
3417
3445
  vrel = vjoin(job["prel"], fname)
3418
3446
  xlink = bool(vf.get("xlink"))
3419
- cur, wark, _, _, _, _ = self._find_from_vpath(ptop, vrel)
3447
+ cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, vrel)
3420
3448
  self._forget_file(ptop, vrel, cur, wark, True, st.st_size, xlink)
3421
3449
  except Exception as ex:
3422
3450
  self.log("skipping replace-relink: %r" % (ex,))
@@ -3870,13 +3898,13 @@ class Up2k(object):
3870
3898
  # plugins may expect this to look like an actual IP
3871
3899
  db_ip = "1.1.1.1" if "no_db_ip" in vflags else ip
3872
3900
 
3873
- sql = "insert into up values (?,?,?,?,?,?,?)"
3874
- v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3901
+ sql = "insert into up values (?,?,?,?,?,?,?,?)"
3902
+ v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr)
3875
3903
  try:
3876
3904
  db.execute(sql, v)
3877
3905
  except:
3878
3906
  rd, fn = s3enc(self.mem_cur, rd, fn)
3879
- v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3907
+ v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr)
3880
3908
  db.execute(sql, v)
3881
3909
 
3882
3910
  self.volsize[db] += sz
@@ -3967,6 +3995,9 @@ class Up2k(object):
3967
3995
  except:
3968
3996
  pass
3969
3997
 
3998
+ def handle_fs_abrt(self, akey ) :
3999
+ self.abrt_key = akey
4000
+
3970
4001
  def handle_rm(
3971
4002
  self,
3972
4003
  uname ,
@@ -4013,7 +4044,7 @@ class Up2k(object):
4013
4044
  vn, rem = vn0.get_dbv(rem0)
4014
4045
  ptop = vn.realpath
4015
4046
  with self.mutex, self.reg_mutex:
4016
- abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
4047
+ abrt_cfg = vn.flags.get("u2abort", 1)
4017
4048
  addr = (ip or "\n") if abrt_cfg in (1, 2) else ""
4018
4049
  user = ((uname or "\n"), "*") if abrt_cfg in (1, 3) else None
4019
4050
  reg = self.registry.get(ptop, {}) if abrt_cfg else {}
@@ -4034,17 +4065,22 @@ class Up2k(object):
4034
4065
  if partial:
4035
4066
  dip = ip
4036
4067
  dat = time.time()
4068
+ dun = uname
4069
+ un_cfg = 1
4037
4070
  else:
4038
- if not self.args.unpost:
4071
+ un_cfg = vn.flags["unp_who"]
4072
+ if not self.args.unpost or not un_cfg:
4039
4073
  t = "the unpost feature is disabled in server config"
4040
4074
  raise Pebkac(400, t)
4041
4075
 
4042
- _, _, _, _, dip, dat = self._find_from_vpath(ptop, rem)
4076
+ _, _, _, _, dip, dat, dun = self._find_from_vpath(ptop, rem)
4043
4077
 
4044
4078
  t = "you cannot delete this: "
4045
4079
  if not dip:
4046
4080
  t += "file not found"
4047
- elif dip != ip:
4081
+ elif dip != ip and un_cfg in (1, 2):
4082
+ t += "not uploaded by (You)"
4083
+ elif dun != uname and un_cfg in (1, 3):
4048
4084
  t += "not uploaded by (You)"
4049
4085
  elif dat < time.time() - self.args.unpost:
4050
4086
  t += "uploaded too long ago"
@@ -4133,7 +4169,7 @@ class Up2k(object):
4133
4169
  try:
4134
4170
  ptop = dbv.realpath
4135
4171
  xlink = bool(dbv.flags.get("xlink"))
4136
- cur, wark, _, _, _, _ = self._find_from_vpath(ptop, volpath)
4172
+ cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, volpath)
4137
4173
  self._forget_file(
4138
4174
  ptop, volpath, cur, wark, True, st.st_size, xlink
4139
4175
  )
@@ -4176,7 +4212,7 @@ class Up2k(object):
4176
4212
 
4177
4213
  return n_files, ok + ok2, ng + ng2
4178
4214
 
4179
- def handle_cp(self, uname , ip , svp , dvp ) :
4215
+ def handle_cp(self, abrt , uname , ip , svp , dvp ) :
4180
4216
  if svp == dvp or dvp.startswith(svp + "/"):
4181
4217
  raise Pebkac(400, "cp: cannot copy parent into subfolder")
4182
4218
 
@@ -4223,6 +4259,8 @@ class Up2k(object):
4223
4259
 
4224
4260
  dvpf = dvp + svpf[len(svp) :]
4225
4261
  self._cp_file(uname, ip, svpf, dvpf, curs)
4262
+ if abrt and abrt == self.abrt_key:
4263
+ raise Pebkac(400, "filecopy aborted by http-api")
4226
4264
 
4227
4265
  for v in curs:
4228
4266
  v.connection.commit()
@@ -4292,7 +4330,7 @@ class Up2k(object):
4292
4330
 
4293
4331
  bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
4294
4332
 
4295
- c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
4333
+ c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath(
4296
4334
  svn_dbv.realpath, srem_dbv
4297
4335
  )
4298
4336
  c2 = self.cur.get(dvn.realpath)
@@ -4316,7 +4354,7 @@ class Up2k(object):
4316
4354
  w,
4317
4355
  w,
4318
4356
  "",
4319
- "",
4357
+ un or "",
4320
4358
  ip or "",
4321
4359
  at or 0,
4322
4360
  )
@@ -4389,7 +4427,7 @@ class Up2k(object):
4389
4427
 
4390
4428
  return "k"
4391
4429
 
4392
- def handle_mv(self, uname , ip , svp , dvp ) :
4430
+ def handle_mv(self, abrt , uname , ip , svp , dvp ) :
4393
4431
  if svp == dvp or dvp.startswith(svp + "/"):
4394
4432
  raise Pebkac(400, "mv: cannot move parent into subfolder")
4395
4433
 
@@ -4444,6 +4482,8 @@ class Up2k(object):
4444
4482
 
4445
4483
  dvpf = dvp + svpf[len(svp) :]
4446
4484
  self._mv_file(uname, ip, svpf, dvpf, curs)
4485
+ if abrt and abrt == self.abrt_key:
4486
+ raise Pebkac(400, "filemove aborted by http-api")
4447
4487
 
4448
4488
  for v in curs:
4449
4489
  v.connection.commit()
@@ -4575,7 +4615,7 @@ class Up2k(object):
4575
4615
 
4576
4616
  return "k"
4577
4617
 
4578
- c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(svn.realpath, srem)
4618
+ c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath(svn.realpath, srem)
4579
4619
  c2 = self.cur.get(dvn.realpath)
4580
4620
 
4581
4621
  has_dupes = False
@@ -4608,7 +4648,7 @@ class Up2k(object):
4608
4648
  w,
4609
4649
  w,
4610
4650
  "",
4611
- "",
4651
+ un or "",
4612
4652
  ip or "",
4613
4653
  at or 0,
4614
4654
  )
@@ -4708,13 +4748,14 @@ class Up2k(object):
4708
4748
 
4709
4749
 
4710
4750
 
4751
+
4711
4752
 
4712
4753
  cur = self.cur.get(ptop)
4713
4754
  if not cur:
4714
- return None, None, None, None, "", None
4755
+ return None, None, None, None, "", None, ""
4715
4756
 
4716
4757
  rd, fn = vsplit(vrem)
4717
- q = "select w, mt, sz, ip, at from up where rd=? and fn=? limit 1"
4758
+ q = "select w, mt, sz, ip, at, un from up where rd=? and fn=? limit 1"
4718
4759
  try:
4719
4760
  c = cur.execute(q, (rd, fn))
4720
4761
  except:
@@ -4722,9 +4763,9 @@ class Up2k(object):
4722
4763
 
4723
4764
  hit = c.fetchone()
4724
4765
  if hit:
4725
- wark, ftime, fsize, ip, at = hit
4726
- return cur, wark, ftime, fsize, ip, at
4727
- return cur, None, None, None, "", None
4766
+ wark, ftime, fsize, ip, at, un = hit
4767
+ return cur, wark, ftime, fsize, ip, at, un
4768
+ return cur, None, None, None, "", None, ""
4728
4769
 
4729
4770
  def _forget_file(
4730
4771
  self,
copyparty/util.py CHANGED
@@ -378,6 +378,9 @@ application swf=x-shockwave-flash m3u=vnd.apple.mpegurl db3=vnd.sqlite3 sqlite=v
378
378
  text ass=plain ssa=plain
379
379
  image jpg=jpeg xpm=x-xpixmap psd=vnd.adobe.photoshop jpf=jpx tif=tiff ico=x-icon djvu=vnd.djvu
380
380
  image heic=heic-sequence heif=heif-sequence hdr=vnd.radiance svg=svg+xml
381
+ image arw=x-sony-arw cr2=x-canon-cr2 crw=x-canon-crw dcr=x-kodak-dcr dng=x-adobe-dng erf=x-epson-erf
382
+ image k25=x-kodak-k25 kdc=x-kodak-kdc mrw=x-minolta-mrw nef=x-nikon-nef orf=x-olympus-orf
383
+ image pef=x-pentax-pef raf=x-fuji-raf raw=x-panasonic-raw sr2=x-sony-sr2 srf=x-sony-srf x3f=x-sigma-x3f
381
384
  audio caf=x-caf mp3=mpeg m4a=mp4 mid=midi mpc=musepack aif=aiff au=basic qcp=qcelp
382
385
  video mkv=x-matroska mov=quicktime avi=x-msvideo m4v=x-m4v ts=mp2t
383
386
  video asf=x-ms-asf flv=x-flv 3gp=3gpp 3g2=3gpp2 rmvb=vnd.rn-realmedia-vbr
@@ -2864,6 +2867,27 @@ def load_ipu(
2864
2867
  return ip_u, nm
2865
2868
 
2866
2869
 
2870
+ def load_ipr(
2871
+ log , iprs , defer_mutex = False
2872
+ ) :
2873
+ ret = {}
2874
+ for ipr in iprs:
2875
+ try:
2876
+ zs, uname = ipr.split("=")
2877
+ cidrs = zs.split(",")
2878
+ except:
2879
+ t = "\n invalid value %r for argument --ipr; must be CIDR[,CIDR[,...]]=UNAME (192.168.0.0/16=amelia)"
2880
+ raise Exception(t % (ipr,))
2881
+ try:
2882
+ nm = NetMap(["::"], cidrs, True, True, defer_mutex)
2883
+ except Exception as ex:
2884
+ t = "failed to translate --ipr into netmap, probably due to invalid config: %r"
2885
+ log("root", t % (ex,), 1)
2886
+ raise
2887
+ ret[uname] = nm
2888
+ return ret
2889
+
2890
+
2867
2891
  def yieldfile(fn , bufsz ) :
2868
2892
  readsz = min(bufsz, 128 * 1024)
2869
2893
  with open(fsenc(fn), "rb", bufsz) as f:
@@ -3489,7 +3513,7 @@ def runihook(
3489
3513
  verbose ,
3490
3514
  cmd ,
3491
3515
  vol ,
3492
- ups ,
3516
+ ups ,
3493
3517
  ) :
3494
3518
  _, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
3495
3519
  bcmd = [sfsenc(x) for x in acmd]
Binary file
Binary file
Binary file
copyparty/web/rups.js.gz CHANGED
Binary file
Binary file
copyparty/web/splash.html CHANGED
@@ -22,7 +22,7 @@
22
22
  <p id="b">howdy stranger &nbsp; <small>(you're not logged in)</small></p>
23
23
  {%- else %}
24
24
  <a id="c" href="{{ r }}/?pw=x" class="logout">logout</a>
25
- <p><span id="m">welcome back,</span> <strong>{{ this.uname|e }}</strong></p>
25
+ <p><span id="m">welcome back,</span> <strong id="un">{{ this.uname|e }}</strong></p>
26
26
  {%- endif %}
27
27
  {%- endif %}
28
28
 
@@ -168,6 +168,13 @@
168
168
 
169
169
  <li><a id="af" href="{{ r }}/?ru">show recent uploads</a></li>
170
170
  <li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
171
+
172
+ {%- if this.uname != '*' %}
173
+ <li><form method="post" enctype="multipart/form-data">
174
+ <input type="hidden" name="act" value="logout" />
175
+ <input type="submit" id="lo" value="logout “{{ this.uname|e }}” everywhere" />
176
+ </form></li>
177
+ {% endif %}
171
178
  </ul>
172
179
 
173
180
  </div>
Binary file
copyparty/web/svcs.html CHANGED
@@ -42,7 +42,7 @@
42
42
 
43
43
 
44
44
 
45
- {% if args.idp_h_usr %}
45
+ {% if args.have_idp_hdrs %}
46
46
  <p style="line-height:2em"><b>WARNING:</b> this server is using IdP-based authentication, so this stuff may not work as advertised. Depending on server config, these commands can probably only be used to access areas which don't require authentication, unless you auth using any non-IdP accounts defined in the copyparty config. Please see <a href="https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients">the IdP docs</a></p>
47
47
  {% endif %}
48
48
 
copyparty/web/up2k.js.gz CHANGED
Binary file
copyparty/web/util.js.gz CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.19.1
3
+ Version: 1.19.3
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
@@ -155,7 +155,9 @@ made in Norway 🇳🇴
155
155
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
156
156
  * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
157
157
  * [ip auth](#ip-auth) - autologin based on IP range (CIDR)
158
+ * [restrict to ip](#restrict-to-ip) - limit a user to certain IP ranges (CIDR)
158
159
  * [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
160
+ * [generic header auth](#generic-header-auth) - other ways to auth by header
159
161
  * [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
160
162
  * [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
161
163
  * [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed
@@ -331,6 +333,7 @@ also see [comparison to similar software](./docs/versus.md)
331
333
  * ☑ realtime streaming of growing files (logfiles and such)
332
334
  * ☑ [thumbnails](#thumbnails)
333
335
  * ☑ ...of images using Pillow, pyvips, or FFmpeg
336
+ * ☑ ...of RAW images using rawpy
334
337
  * ☑ ...of videos using FFmpeg
335
338
  * ☑ ...of audio (spectrograms) using FFmpeg
336
339
  * ☑ cache eviction (max-age; maybe max-size eventually)
@@ -577,6 +580,8 @@ examples:
577
580
  * replacing the `g` permission with `wg` would let anonymous users upload files, but not see the required filekey to access it
578
581
  * replacing the `g` permission with `wG` would let anonymous users upload files, receiving a working direct link in return
579
582
 
583
+ if you want to grant access to all users who are logged in, the group `acct` will always contain all known users, so for example `-v /mnt/music:music:r,@acct`
584
+
580
585
  anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
581
586
 
582
587
  and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
@@ -602,6 +607,7 @@ and if you want to use config files instead of commandline args (good!) then her
602
607
  accs:
603
608
  r: u1, u2 # only these accounts can read,
604
609
  r: @g1 # (exactly the same, just with a group instead)
610
+ r: @acct # (alternatively, ALL users who are logged in)
605
611
  rw: u3 # and only u3 can read-write
606
612
 
607
613
  [/inc]
@@ -1957,6 +1963,20 @@ repeat the option to map additional subnets
1957
1963
  **be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`
1958
1964
 
1959
1965
 
1966
+ ### restrict to ip
1967
+
1968
+ limit a user to certain IP ranges (CIDR) , using the global-option `--ipr`
1969
+
1970
+ for example, if the user `spartacus` should get rejected if they're not connecting from an IP that starts with `192.168.123` or `172.16`, then you can either specify `--ipr=192.168.123.0/24,172.16.0.0/16=spartacus` as a commandline option, or put this in a config file:
1971
+
1972
+ ```yaml
1973
+ [global]
1974
+ ipr: 192.168.123.0/24,172.16.0.0/16=spartacus
1975
+ ```
1976
+
1977
+ repeat the option to map additional users
1978
+
1979
+
1960
1980
  ## identity providers
1961
1981
 
1962
1982
  replace copyparty passwords with oauth and such
@@ -1976,6 +1996,20 @@ a more complete example of the copyparty configuration options [look like this](
1976
1996
  but if you just want to let users change their own passwords, then you probably want [user-changeable passwords](#user-changeable-passwords) instead
1977
1997
 
1978
1998
 
1999
+ ### generic header auth
2000
+
2001
+ other ways to auth by header
2002
+
2003
+ if you have a middleware which adds a header with a user identifier, for example tailscale's `Tailscale-User-Login: alice.m@forest.net` then you can automatically auth as `alice` by defining that mapping with `--idp-hm-usr '^Tailscale-User-Login^alice.m@forest.net^alice'` or the following config file:
2004
+
2005
+ ```yaml
2006
+ [global]
2007
+ idp-hm-usr: ^Tailscale-User-Login^alice.m@forest.net^alice
2008
+ ```
2009
+
2010
+ repeat the whole `idp-hm-usr` option to add more mappings
2011
+
2012
+
1979
2013
  ## user-changeable passwords
1980
2014
 
1981
2015
  if permitted, users can change their own passwords in the control-panel
@@ -2857,9 +2891,10 @@ enable [music tags](#metadata-from-audio-files):
2857
2891
  enable [thumbnails](#thumbnails) of...
2858
2892
  * **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
2859
2893
  * **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
2860
- * **HEIF pictures:** `pyvips` or `ffmpeg` or `pyheif-pillow-opener` (requires Linux or a C compiler)
2894
+ * **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
2861
2895
  * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
2862
2896
  * **JPEG XL pictures:** `pyvips` or `ffmpeg`
2897
+ * **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats)
2863
2898
 
2864
2899
  enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
2865
2900
 
@@ -2890,9 +2925,10 @@ set any of the following environment variables to disable its associated optiona
2890
2925
  | `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
2891
2926
  | `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
2892
2927
  | `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
2893
- | `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pyheif-pillow-opener/) |
2928
+ | `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pillow-heif/) |
2894
2929
  | `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
2895
2930
  | `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
2931
+ | `PRTY_NO_RAW` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
2896
2932
  | `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
2897
2933
 
2898
2934
  example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`