copyparty 1.15.9__py3-none-any.whl → 1.16.0__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
@@ -77,6 +77,7 @@ web/deps/prismd.css
77
77
  web/deps/scp.woff2
78
78
  web/deps/sha512.ac.js
79
79
  web/deps/sha512.hw.js
80
+ web/iiam.gif
80
81
  web/md.css
81
82
  web/md.html
82
83
  web/md.js
copyparty/__main__.py CHANGED
@@ -50,6 +50,8 @@ from .util import (
50
50
  PARTFTPY_VER,
51
51
  PY_DESC,
52
52
  PYFTPD_VER,
53
+ RAM_AVAIL,
54
+ RAM_TOTAL,
53
55
  SQLITE_VER,
54
56
  UNPLICATIONS,
55
57
  Daemon,
@@ -676,6 +678,8 @@ def get_sects():
676
678
  \033[36mxbu\033[35m executes CMD before a file upload starts
677
679
  \033[36mxau\033[35m executes CMD after a file upload finishes
678
680
  \033[36mxiu\033[35m executes CMD after all uploads finish and volume is idle
681
+ \033[36mxbc\033[35m executes CMD before a file copy
682
+ \033[36mxac\033[35m executes CMD after a file copy
679
683
  \033[36mxbr\033[35m executes CMD before a file rename/move
680
684
  \033[36mxar\033[35m executes CMD after a file rename/move
681
685
  \033[36mxbd\033[35m executes CMD before a file delete
@@ -1193,6 +1197,8 @@ def add_hooks(ap):
1193
1197
  ap2.add_argument("--xbu", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file upload starts")
1194
1198
  ap2.add_argument("--xau", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file upload finishes")
1195
1199
  ap2.add_argument("--xiu", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after all uploads finish and volume is idle")
1200
+ ap2.add_argument("--xbc", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file copy")
1201
+ ap2.add_argument("--xac", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file copy")
1196
1202
  ap2.add_argument("--xbr", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file move/rename")
1197
1203
  ap2.add_argument("--xar", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file move/rename")
1198
1204
  ap2.add_argument("--xbd", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m before a file delete")
@@ -1225,6 +1231,7 @@ def add_optouts(ap):
1225
1231
  ap2.add_argument("--no-dav", action="store_true", help="disable webdav support")
1226
1232
  ap2.add_argument("--no-del", action="store_true", help="disable delete operations")
1227
1233
  ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations")
1234
+ ap2.add_argument("--no-cp", action="store_true", help="disable copy operations")
1228
1235
  ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in <title>")
1229
1236
  ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
1230
1237
  ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
@@ -1299,6 +1306,7 @@ def add_logging(ap):
1299
1306
  ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
1300
1307
  ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
1301
1308
  ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
1309
+ ap2.add_argument("--ohead", metavar="HEADER", type=u, action='append', help="print response \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
1302
1310
  ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|\?th=[wj]$|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
1303
1311
 
1304
1312
 
@@ -1307,9 +1315,12 @@ def add_admin(ap):
1307
1315
  ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
1308
1316
  ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
1309
1317
  ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
1318
+ ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
1310
1319
 
1311
1320
 
1312
1321
  def add_thumbnail(ap):
1322
+ th_ram = (RAM_AVAIL or RAM_TOTAL or 9) * 0.6
1323
+ th_ram = int(max(min(th_ram, 6), 1) * 10) / 10
1313
1324
  ap2 = ap.add_argument_group('thumbnail options')
1314
1325
  ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails (volflag=dthumb)")
1315
1326
  ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails (volflag=dvthumb)")
@@ -1317,7 +1328,7 @@ def add_thumbnail(ap):
1317
1328
  ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)")
1318
1329
  ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails")
1319
1330
  ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="conversion timeout in seconds (volflag=convt)")
1320
- ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=6.0, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
1331
+ ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=th_ram, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
1321
1332
  ap2.add_argument("--th-crop", metavar="TXT", type=u, default="y", help="crop thumbnails to 4:3 or keep dynamic height; client can override in UI unless force. [\033[32my\033[0m]=crop, [\033[32mn\033[0m]=nocrop, [\033[32mfy\033[0m]=force-y, [\033[32mfn\033[0m]=force-n (volflag=crop)")
1322
1333
  ap2.add_argument("--th-x3", metavar="TXT", type=u, default="n", help="show thumbs at 3x resolution; client can override in UI unless force. [\033[32my\033[0m]=yes, [\033[32mn\033[0m]=no, [\033[32mfy\033[0m]=force-yes, [\033[32mfn\033[0m]=force-no (volflag=th3x)")
1323
1334
  ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference")
@@ -1452,6 +1463,7 @@ def add_ui(ap, retry):
1452
1463
  ap2.add_argument("--pb-url", metavar="URL", type=u, default="https://github.com/9001/copyparty", help="powered-by link; disable with \033[33m-np\033[0m")
1453
1464
  ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
1454
1465
  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")
1466
+ ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
1455
1467
  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")
1456
1468
  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)")
1457
1469
  ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
@@ -1749,6 +1761,9 @@ def main(argv = None) :
1749
1761
  if al.ihead:
1750
1762
  al.ihead = [x.lower() for x in al.ihead]
1751
1763
 
1764
+ if al.ohead:
1765
+ al.ohead = [x.lower() for x in al.ohead]
1766
+
1752
1767
  if HAVE_SSL:
1753
1768
  if al.ssl_ver:
1754
1769
  configure_ssl_ver(al)
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 9)
4
- CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 18)
3
+ VERSION = (1, 16, 0)
4
+ CODENAME = "COPYparty"
5
+ BUILD_DT = (2024, 11, 10)
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
@@ -545,15 +545,14 @@ class VFS(object):
545
545
  return self._get_dbv(vrem)
546
546
 
547
547
  shv, srem = src
548
- return shv, vjoin(srem, vrem)
548
+ return shv._get_dbv(vjoin(srem, vrem))
549
549
 
550
550
  def _get_dbv(self, vrem ) :
551
551
  dbv = self.dbv
552
552
  if not dbv:
553
553
  return self, vrem
554
554
 
555
- tv = [self.vpath[len(dbv.vpath) :].lstrip("/"), vrem]
556
- vrem = "/".join([x for x in tv if x])
555
+ vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
557
556
  return dbv, vrem
558
557
 
559
558
  def canonical(self, rem , resolve = True) :
@@ -585,10 +584,11 @@ class VFS(object):
585
584
  scandir ,
586
585
  permsets ,
587
586
  lstat = False,
587
+ throw = False,
588
588
  ) :
589
589
  """replaces _ls for certain shares (single-file, or file selection)"""
590
590
  vn, rem = self.shr_src # type: ignore
591
- abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat)
591
+ abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat, throw)
592
592
  real = [x for x in real if os.path.basename(x[0]) in self.shr_files]
593
593
  return abspath, real, {}
594
594
 
@@ -599,11 +599,12 @@ class VFS(object):
599
599
  scandir ,
600
600
  permsets ,
601
601
  lstat = False,
602
+ throw = False,
602
603
  ) :
603
604
  """return user-readable [fsdir,real,virt] items at vpath"""
604
605
  virt_vis = {} # nodes readable by user
605
606
  abspath = self.canonical(rem)
606
- real = list(statdir(self.log, scandir, lstat, abspath))
607
+ real = list(statdir(self.log, scandir, lstat, abspath, throw))
607
608
  real.sort()
608
609
  if not rem:
609
610
  # no vfs nodes in the list of real inodes
@@ -665,6 +666,10 @@ class VFS(object):
665
666
  """
666
667
  recursively yields from ./rem;
667
668
  rel is a unix-style user-defined vpath (not vfs-related)
669
+
670
+ NOTE: don't invoke this function from a dbv; subvols are only
671
+ descended into if rem is blank due to the _ls `if not rem:`
672
+ which intention is to prevent unintended access to subvols
668
673
  """
669
674
 
670
675
  fsroot, vfs_ls, vfs_virt = self.ls(rem, uname, scandir, permsets, lstat=lstat)
@@ -905,7 +910,7 @@ class AuthSrv(object):
905
910
  self._reload()
906
911
  return True
907
912
 
908
- broker.ask("_reload_blocking", False).get()
913
+ broker.ask("reload", False, True).get()
909
914
  return True
910
915
 
911
916
  def _map_volume_idp(
@@ -1375,7 +1380,7 @@ class AuthSrv(object):
1375
1380
  flags[name] = True
1376
1381
  return
1377
1382
 
1378
- zs = "mtp on403 on404 xbu xau xiu xbr xar xbd xad xm xban"
1383
+ zs = "mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
1379
1384
  if name not in zs.split():
1380
1385
  if value is True:
1381
1386
  t = "└─add volflag [{}] = {} ({})"
@@ -1930,7 +1935,7 @@ class AuthSrv(object):
1930
1935
  vol.flags[k] = odfusion(getattr(self.args, k), vol.flags[k])
1931
1936
 
1932
1937
  # append additive args from argv to volflags
1933
- hooks = "xbu xau xiu xbr xar xbd xad xm xban".split()
1938
+ hooks = "xbu xau xiu xbc xac xbr xar xbd xad xm xban".split()
1934
1939
  for name in "mtp on404 on403".split() + hooks:
1935
1940
  self._read_volflag(vol.flags, name, getattr(self.args, name), True)
1936
1941
 
@@ -2381,7 +2386,7 @@ class AuthSrv(object):
2381
2386
  self._reload()
2382
2387
  return True, "new password OK"
2383
2388
 
2384
- broker.ask("_reload_blocking", False, False).get()
2389
+ broker.ask("reload", False, False).get()
2385
2390
  return True, "new password OK"
2386
2391
 
2387
2392
  def setup_chpw(self, acct ) :
@@ -2633,7 +2638,7 @@ class AuthSrv(object):
2633
2638
  ]
2634
2639
 
2635
2640
  csv = set("i p th_covers zm_on zm_off zs_on zs_off".split())
2636
- zs = "c ihead mtm mtp on403 on404 xad xar xau xiu xban xbd xbr xbu xm"
2641
+ zs = "c ihead ohead mtm mtp on403 on404 xac xad xar xau xiu xban xbc xbd xbr xbu xm"
2637
2642
  lst = set(zs.split())
2638
2643
  askip = set("a v c vc cgen exp_lg exp_md theme".split())
2639
2644
  fskip = set("exp_lg exp_md mv_re_r mv_re_t rm_re_r rm_re_t".split())
copyparty/broker_mp.py CHANGED
@@ -39,6 +39,9 @@ class BrokerMp(object):
39
39
  self.procs = []
40
40
  self.mutex = threading.Lock()
41
41
 
42
+ self.retpend = {}
43
+ self.retpend_mutex = threading.Lock()
44
+
42
45
  self.num_workers = self.args.j or CORES
43
46
  self.log("broker", "booting {} subprocesses".format(self.num_workers))
44
47
  for n in range(1, self.num_workers + 1):
@@ -50,6 +53,8 @@ class BrokerMp(object):
50
53
  self.procs.append(proc)
51
54
  proc.start()
52
55
 
56
+ Daemon(self.periodic, "mp-periodic")
57
+
53
58
  def shutdown(self) :
54
59
  self.log("broker", "shutting down")
55
60
  for n, proc in enumerate(self.procs):
@@ -86,8 +91,10 @@ class BrokerMp(object):
86
91
  self.log(*args)
87
92
 
88
93
  elif dest == "retq":
89
- # response from previous ipc call
90
- raise Exception("invalid broker_mp usage")
94
+ with self.retpend_mutex:
95
+ retq = self.retpend.pop(retq_id)
96
+
97
+ retq.put(args[0])
91
98
 
92
99
  else:
93
100
  # new ipc invoking managed service in hub
@@ -105,7 +112,6 @@ class BrokerMp(object):
105
112
  proc.q_pend.put((retq_id, "retq", rv))
106
113
 
107
114
  def ask(self, dest , *args ) :
108
-
109
115
  # new non-ipc invoking managed service in hub
110
116
  obj = self.hub
111
117
  for node in dest.split("."):
@@ -117,17 +123,30 @@ class BrokerMp(object):
117
123
  retq.put(rv)
118
124
  return retq
119
125
 
126
+ def wask(self, dest , *args ) :
127
+ # call from hub to workers
128
+ ret = []
129
+ for p in self.procs:
130
+ retq = ExceptionalQueue(1)
131
+ retq_id = id(retq)
132
+ with self.retpend_mutex:
133
+ self.retpend[retq_id] = retq
134
+
135
+ p.q_pend.put((retq_id, dest, list(args)))
136
+ ret.append(retq)
137
+ return ret
138
+
120
139
  def say(self, dest , *args ) :
121
140
  """
122
141
  send message to non-hub component in other process,
123
142
  returns a Queue object which eventually contains the response if want_retval
124
143
  (not-impl here since nothing uses it yet)
125
144
  """
126
- if dest == "listen":
145
+ if dest == "httpsrv.listen":
127
146
  for p in self.procs:
128
147
  p.q_pend.put((0, dest, [args[0], len(self.procs)]))
129
148
 
130
- elif dest == "set_netdevs":
149
+ elif dest == "httpsrv.set_netdevs":
131
150
  for p in self.procs:
132
151
  p.q_pend.put((0, dest, list(args)))
133
152
 
@@ -136,3 +155,19 @@ class BrokerMp(object):
136
155
 
137
156
  else:
138
157
  raise Exception("what is " + str(dest))
158
+
159
+ def periodic(self) :
160
+ while True:
161
+ time.sleep(1)
162
+
163
+ tdli = {}
164
+ tdls = {}
165
+ qs = self.wask("httpsrv.read_dls")
166
+ for q in qs:
167
+ qr = q.get()
168
+ dli, dls = qr
169
+ tdli.update(dli)
170
+ tdls.update(dls)
171
+ tdl = (tdli, tdls)
172
+ for p in self.procs:
173
+ p.q_pend.put((0, "httpsrv.write_dls", tdl))
copyparty/broker_mpw.py CHANGED
@@ -76,37 +76,38 @@ class MpWorker(BrokerCli):
76
76
  while True:
77
77
  retq_id, dest, args = self.q_pend.get()
78
78
 
79
- # self.logw("work: [{}]".format(d[0]))
79
+ if dest == "retq":
80
+ # response from previous ipc call
81
+ with self.retpend_mutex:
82
+ retq = self.retpend.pop(retq_id)
83
+
84
+ retq.put(args)
85
+ continue
86
+
80
87
  if dest == "shutdown":
81
88
  self.httpsrv.shutdown()
82
89
  self.logw("ok bye")
83
90
  sys.exit(0)
84
91
  return
85
92
 
86
- elif dest == "reload":
93
+ if dest == "reload":
87
94
  self.logw("mpw.asrv reloading")
88
95
  self.asrv.reload()
89
96
  self.logw("mpw.asrv reloaded")
97
+ continue
90
98
 
91
- elif dest == "reload_sessions":
99
+ if dest == "reload_sessions":
92
100
  with self.asrv.mutex:
93
101
  self.asrv.load_sessions()
102
+ continue
94
103
 
95
- elif dest == "listen":
96
- self.httpsrv.listen(args[0], args[1])
97
-
98
- elif dest == "set_netdevs":
99
- self.httpsrv.set_netdevs(args[0])
104
+ obj = self
105
+ for node in dest.split("."):
106
+ obj = getattr(obj, node)
100
107
 
101
- elif dest == "retq":
102
- # response from previous ipc call
103
- with self.retpend_mutex:
104
- retq = self.retpend.pop(retq_id)
105
-
106
- retq.put(args)
107
-
108
- else:
109
- raise Exception("what is " + str(dest))
108
+ rv = obj(*args) # type: ignore
109
+ if retq_id:
110
+ self.say("retq", rv, retq_id=retq_id)
110
111
 
111
112
  def ask(self, dest , *args ) :
112
113
  retq = ExceptionalQueue(1)
@@ -117,5 +118,5 @@ class MpWorker(BrokerCli):
117
118
  self.q_yield.put((retq_id, dest, list(args)))
118
119
  return retq
119
120
 
120
- def say(self, dest , *args ) :
121
- self.q_yield.put((0, dest, list(args)))
121
+ def say(self, dest , *args , retq_id=0) :
122
+ self.q_yield.put((retq_id, dest, list(args)))
copyparty/broker_thr.py CHANGED
@@ -49,11 +49,11 @@ class BrokerThr(BrokerCli):
49
49
  return NotExQueue(obj(*args)) # type: ignore
50
50
 
51
51
  def say(self, dest , *args ) :
52
- if dest == "listen":
52
+ if dest == "httpsrv.listen":
53
53
  self.httpsrv.listen(args[0], 1)
54
54
  return
55
55
 
56
- if dest == "set_netdevs":
56
+ if dest == "httpsrv.set_netdevs":
57
57
  self.httpsrv.set_netdevs(args[0])
58
58
  return
59
59
 
copyparty/cfg.py CHANGED
@@ -103,10 +103,12 @@ def vf_cmap() :
103
103
  "mte",
104
104
  "mth",
105
105
  "mtp",
106
+ "xac",
106
107
  "xad",
107
108
  "xar",
108
109
  "xau",
109
110
  "xban",
111
+ "xbc",
110
112
  "xbd",
111
113
  "xbr",
112
114
  "xbu",
@@ -212,6 +214,8 @@ flagcats = {
212
214
  "xbu=CMD": "execute CMD before a file upload starts",
213
215
  "xau=CMD": "execute CMD after a file upload finishes",
214
216
  "xiu=CMD": "execute CMD after all uploads finish and volume is idle",
217
+ "xbc=CMD": "execute CMD before a file copy",
218
+ "xac=CMD": "execute CMD after a file copy",
215
219
  "xbr=CMD": "execute CMD before a file rename/move",
216
220
  "xar=CMD": "execute CMD after a file rename/move",
217
221
  "xbd=CMD": "execute CMD before a file delete",
copyparty/ftpd.py CHANGED
@@ -292,6 +292,7 @@ class FtpFs(AbstractedFS):
292
292
  self.uname,
293
293
  not self.args.no_scandir,
294
294
  [[True, False], [False, True]],
295
+ throw=True,
295
296
  )
296
297
  vfs_ls = [x[0] for x in vfs_ls1]
297
298
  vfs_ls.extend(vfs_virt.keys())