copyparty 1.15.10__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")
@@ -1308,9 +1315,12 @@ def add_admin(ap):
1308
1315
  ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
1309
1316
  ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
1310
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")
1311
1319
 
1312
1320
 
1313
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
1314
1324
  ap2 = ap.add_argument_group('thumbnail options')
1315
1325
  ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails (volflag=dthumb)")
1316
1326
  ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails (volflag=dvthumb)")
@@ -1318,7 +1328,7 @@ def add_thumbnail(ap):
1318
1328
  ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)")
1319
1329
  ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails")
1320
1330
  ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="conversion timeout in seconds (volflag=convt)")
1321
- 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")
1322
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)")
1323
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)")
1324
1334
  ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference")
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 10)
4
- CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 26)
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
@@ -584,10 +584,11 @@ class VFS(object):
584
584
  scandir ,
585
585
  permsets ,
586
586
  lstat = False,
587
+ throw = False,
587
588
  ) :
588
589
  """replaces _ls for certain shares (single-file, or file selection)"""
589
590
  vn, rem = self.shr_src # type: ignore
590
- abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat)
591
+ abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat, throw)
591
592
  real = [x for x in real if os.path.basename(x[0]) in self.shr_files]
592
593
  return abspath, real, {}
593
594
 
@@ -598,11 +599,12 @@ class VFS(object):
598
599
  scandir ,
599
600
  permsets ,
600
601
  lstat = False,
602
+ throw = False,
601
603
  ) :
602
604
  """return user-readable [fsdir,real,virt] items at vpath"""
603
605
  virt_vis = {} # nodes readable by user
604
606
  abspath = self.canonical(rem)
605
- real = list(statdir(self.log, scandir, lstat, abspath))
607
+ real = list(statdir(self.log, scandir, lstat, abspath, throw))
606
608
  real.sort()
607
609
  if not rem:
608
610
  # no vfs nodes in the list of real inodes
@@ -664,6 +666,10 @@ class VFS(object):
664
666
  """
665
667
  recursively yields from ./rem;
666
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
667
673
  """
668
674
 
669
675
  fsroot, vfs_ls, vfs_virt = self.ls(rem, uname, scandir, permsets, lstat=lstat)
@@ -904,7 +910,7 @@ class AuthSrv(object):
904
910
  self._reload()
905
911
  return True
906
912
 
907
- broker.ask("_reload_blocking", False).get()
913
+ broker.ask("reload", False, True).get()
908
914
  return True
909
915
 
910
916
  def _map_volume_idp(
@@ -1374,7 +1380,7 @@ class AuthSrv(object):
1374
1380
  flags[name] = True
1375
1381
  return
1376
1382
 
1377
- 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"
1378
1384
  if name not in zs.split():
1379
1385
  if value is True:
1380
1386
  t = "└─add volflag [{}] = {} ({})"
@@ -1929,7 +1935,7 @@ class AuthSrv(object):
1929
1935
  vol.flags[k] = odfusion(getattr(self.args, k), vol.flags[k])
1930
1936
 
1931
1937
  # append additive args from argv to volflags
1932
- 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()
1933
1939
  for name in "mtp on404 on403".split() + hooks:
1934
1940
  self._read_volflag(vol.flags, name, getattr(self.args, name), True)
1935
1941
 
@@ -2380,7 +2386,7 @@ class AuthSrv(object):
2380
2386
  self._reload()
2381
2387
  return True, "new password OK"
2382
2388
 
2383
- broker.ask("_reload_blocking", False, False).get()
2389
+ broker.ask("reload", False, False).get()
2384
2390
  return True, "new password OK"
2385
2391
 
2386
2392
  def setup_chpw(self, acct ) :
@@ -2632,7 +2638,7 @@ class AuthSrv(object):
2632
2638
  ]
2633
2639
 
2634
2640
  csv = set("i p th_covers zm_on zm_off zs_on zs_off".split())
2635
- zs = "c ihead ohead 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"
2636
2642
  lst = set(zs.split())
2637
2643
  askip = set("a v c vc cgen exp_lg exp_md theme".split())
2638
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())