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 +1 -0
- copyparty/__main__.py +16 -1
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +15 -10
- copyparty/broker_mp.py +40 -5
- copyparty/broker_mpw.py +20 -19
- copyparty/broker_thr.py +2 -2
- copyparty/cfg.py +4 -0
- copyparty/ftpd.py +1 -0
- copyparty/httpcli.py +285 -79
- copyparty/httpsrv.py +39 -0
- copyparty/metrics.py +3 -0
- copyparty/svchub.py +47 -33
- copyparty/szip.py +1 -1
- copyparty/tcpsrv.py +2 -2
- copyparty/tftpd.py +1 -0
- copyparty/th_srv.py +10 -3
- copyparty/up2k.py +339 -69
- copyparty/util.py +79 -11
- copyparty/web/a/partyfuse.py +3 -2
- copyparty/web/a/u2c.py +20 -5
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +24 -3
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/METADATA +12 -4
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/RECORD +35 -35
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/WHEEL +1 -1
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/LICENSE +0 -0
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.9.dist-info → copyparty-1.16.0.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
4
|
import argparse # typechk
|
5
|
-
import calendar
|
6
5
|
import copy
|
7
6
|
import errno
|
8
7
|
import gzip
|
@@ -19,7 +18,6 @@ import threading # typechk
|
|
19
18
|
import time
|
20
19
|
import uuid
|
21
20
|
from datetime import datetime
|
22
|
-
from email.utils import parsedate
|
23
21
|
from operator import itemgetter
|
24
22
|
|
25
23
|
import jinja2 # typechk
|
@@ -107,6 +105,7 @@ from .util import (
|
|
107
105
|
unquotep,
|
108
106
|
vjoin,
|
109
107
|
vol_san,
|
108
|
+
vroots,
|
110
109
|
vsplit,
|
111
110
|
wrename,
|
112
111
|
wunlink,
|
@@ -123,6 +122,11 @@ _ = (argparse, threading)
|
|
123
122
|
|
124
123
|
NO_CACHE = {"Cache-Control": "no-cache"}
|
125
124
|
|
125
|
+
ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
|
126
|
+
|
127
|
+
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
|
128
|
+
H_CONN_CLOSE = "Connection: Close"
|
129
|
+
|
126
130
|
LOGUES = [[0, ".prologue.html"], [1, ".epilogue.html"]]
|
127
131
|
|
128
132
|
READMES = [[0, ["preadme.md", "PREADME.md"]], [1, ["readme.md", "README.md"]]]
|
@@ -177,6 +181,7 @@ class HttpCli(object):
|
|
177
181
|
self.rem = " "
|
178
182
|
self.vpath = " "
|
179
183
|
self.vpaths = " "
|
184
|
+
self.dl_id = ""
|
180
185
|
self.gctx = " " # additional context for garda
|
181
186
|
self.trailing_slash = True
|
182
187
|
self.uname = " "
|
@@ -628,7 +633,7 @@ class HttpCli(object):
|
|
628
633
|
avn.can_access("", self.uname) if avn else [False] * 8
|
629
634
|
)
|
630
635
|
self.avn = avn
|
631
|
-
self.vn = vn
|
636
|
+
self.vn = vn # note: do not dbv due to walk/zipgen
|
632
637
|
self.rem = rem
|
633
638
|
|
634
639
|
self.s.settimeout(self.args.s_tbody or None)
|
@@ -717,6 +722,11 @@ class HttpCli(object):
|
|
717
722
|
except Pebkac:
|
718
723
|
return False
|
719
724
|
|
725
|
+
finally:
|
726
|
+
if self.dl_id:
|
727
|
+
self.conn.hsrv.dli.pop(self.dl_id, None)
|
728
|
+
self.conn.hsrv.dls.pop(self.dl_id, None)
|
729
|
+
|
720
730
|
def dip(self) :
|
721
731
|
if self.args.plain_ip:
|
722
732
|
return self.ip.replace(":", ".")
|
@@ -787,11 +797,11 @@ class HttpCli(object):
|
|
787
797
|
|
788
798
|
def k304(self) :
|
789
799
|
k304 = self.cookies.get("k304")
|
790
|
-
return (
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
)
|
800
|
+
return k304 == "y" or (self.args.k304 == 2 and k304 != "n")
|
801
|
+
|
802
|
+
def no304(self) :
|
803
|
+
no304 = self.cookies.get("no304")
|
804
|
+
return no304 == "y" or (self.args.no304 == 2 and no304 != "n")
|
795
805
|
|
796
806
|
def _build_html_head(self, maybe_html , kv ) :
|
797
807
|
html = str(maybe_html)
|
@@ -826,25 +836,28 @@ class HttpCli(object):
|
|
826
836
|
) :
|
827
837
|
response = ["%s %s %s" % (self.http_ver, status, HTTPCODE[status])]
|
828
838
|
|
829
|
-
if length is not None:
|
830
|
-
response.append("Content-Length: " + unicode(length))
|
831
|
-
|
832
|
-
if status == 304 and self.k304():
|
833
|
-
self.keepalive = False
|
834
|
-
|
835
|
-
# close if unknown length, otherwise take client's preference
|
836
|
-
response.append("Connection: " + ("Keep-Alive" if self.keepalive else "Close"))
|
837
|
-
response.append("Date: " + formatdate())
|
838
|
-
|
839
839
|
# headers{} overrides anything set previously
|
840
840
|
if headers:
|
841
841
|
self.out_headers.update(headers)
|
842
842
|
|
843
|
-
|
844
|
-
|
845
|
-
|
843
|
+
if status == 304:
|
844
|
+
self.out_headers.pop("Content-Length", None)
|
845
|
+
self.out_headers.pop("Content-Type", None)
|
846
|
+
self.out_headerlist.clear()
|
847
|
+
if self.k304():
|
848
|
+
self.keepalive = False
|
849
|
+
else:
|
850
|
+
if length is not None:
|
851
|
+
response.append("Content-Length: " + unicode(length))
|
852
|
+
|
853
|
+
if mime:
|
854
|
+
self.out_headers["Content-Type"] = mime
|
855
|
+
elif "Content-Type" not in self.out_headers:
|
856
|
+
self.out_headers["Content-Type"] = "text/html; charset=utf-8"
|
846
857
|
|
847
|
-
|
858
|
+
# close if unknown length, otherwise take client's preference
|
859
|
+
response.append(H_CONN_KEEPALIVE if self.keepalive else H_CONN_CLOSE)
|
860
|
+
response.append("Date: " + formatdate())
|
848
861
|
|
849
862
|
for k, zs in list(self.out_headers.items()) + self.out_headerlist:
|
850
863
|
response.append("%s: %s" % (k, zs))
|
@@ -858,6 +871,19 @@ class HttpCli(object):
|
|
858
871
|
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
|
859
872
|
raise Pebkac(999)
|
860
873
|
|
874
|
+
if self.args.ohead and self.do_log:
|
875
|
+
keys = self.args.ohead
|
876
|
+
if "*" in keys:
|
877
|
+
lines = response[1:]
|
878
|
+
else:
|
879
|
+
lines = []
|
880
|
+
for zs in response[1:]:
|
881
|
+
if zs.split(":")[0].lower() in keys:
|
882
|
+
lines.append(zs)
|
883
|
+
for zs in lines:
|
884
|
+
hk, hv = zs.split(": ")
|
885
|
+
self.log("[O] {}: \033[33m[{}]".format(hk, hv), 5)
|
886
|
+
|
861
887
|
response.append("\r\n")
|
862
888
|
try:
|
863
889
|
self.s.sendall("\r\n".join(response).encode("utf-8"))
|
@@ -937,13 +963,14 @@ class HttpCli(object):
|
|
937
963
|
|
938
964
|
lines = [
|
939
965
|
"%s %s %s" % (self.http_ver or "HTTP/1.1", status, HTTPCODE[status]),
|
940
|
-
|
966
|
+
H_CONN_CLOSE,
|
941
967
|
]
|
942
968
|
|
943
969
|
if body:
|
944
970
|
lines.append("Content-Length: " + unicode(len(body)))
|
945
971
|
|
946
|
-
|
972
|
+
lines.append("\r\n")
|
973
|
+
self.s.sendall("\r\n".join(lines).encode("utf-8") + body)
|
947
974
|
|
948
975
|
def urlq(self, add , rm ) :
|
949
976
|
"""
|
@@ -1170,6 +1197,9 @@ class HttpCli(object):
|
|
1170
1197
|
if "move" in self.uparam:
|
1171
1198
|
return self.handle_mv()
|
1172
1199
|
|
1200
|
+
if "copy" in self.uparam:
|
1201
|
+
return self.handle_cp()
|
1202
|
+
|
1173
1203
|
if not self.vpath and self.ouparam:
|
1174
1204
|
if "reload" in self.uparam:
|
1175
1205
|
return self.handle_reload()
|
@@ -1177,12 +1207,6 @@ class HttpCli(object):
|
|
1177
1207
|
if "stack" in self.uparam:
|
1178
1208
|
return self.tx_stack()
|
1179
1209
|
|
1180
|
-
if "ups" in self.uparam:
|
1181
|
-
return self.tx_ups()
|
1182
|
-
|
1183
|
-
if "k304" in self.uparam:
|
1184
|
-
return self.set_k304()
|
1185
|
-
|
1186
1210
|
if "setck" in self.uparam:
|
1187
1211
|
return self.setck()
|
1188
1212
|
|
@@ -1195,9 +1219,16 @@ class HttpCli(object):
|
|
1195
1219
|
if "shares" in self.uparam:
|
1196
1220
|
return self.tx_shares()
|
1197
1221
|
|
1222
|
+
if "dls" in self.uparam:
|
1223
|
+
return self.tx_dls()
|
1224
|
+
|
1198
1225
|
if "h" in self.uparam:
|
1199
1226
|
return self.tx_mounts()
|
1200
1227
|
|
1228
|
+
if "ups" in self.uparam:
|
1229
|
+
# vpath is used for share translation
|
1230
|
+
return self.tx_ups()
|
1231
|
+
|
1201
1232
|
if "rss" in self.uparam:
|
1202
1233
|
return self.tx_rss()
|
1203
1234
|
|
@@ -1426,6 +1457,7 @@ class HttpCli(object):
|
|
1426
1457
|
not self.args.no_scandir,
|
1427
1458
|
[[True, False]],
|
1428
1459
|
lstat="davrt" not in vn.flags,
|
1460
|
+
throw=True,
|
1429
1461
|
)
|
1430
1462
|
if not self.can_read:
|
1431
1463
|
vfs_ls = []
|
@@ -1763,6 +1795,9 @@ class HttpCli(object):
|
|
1763
1795
|
if "move" in self.uparam:
|
1764
1796
|
return self.handle_mv()
|
1765
1797
|
|
1798
|
+
if "copy" in self.uparam:
|
1799
|
+
return self.handle_cp()
|
1800
|
+
|
1766
1801
|
if "delete" in self.uparam:
|
1767
1802
|
return self.handle_rm([])
|
1768
1803
|
|
@@ -2380,6 +2415,15 @@ class HttpCli(object):
|
|
2380
2415
|
if "purl" in ret:
|
2381
2416
|
ret["purl"] = self.args.SR + ret["purl"]
|
2382
2417
|
|
2418
|
+
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
2419
|
+
# strip common suffix (uploader's folder structure)
|
2420
|
+
vp_req, vp_vfs = vroots(self.vpath, vjoin(dbv.vpath, vrem))
|
2421
|
+
if not ret["purl"].startswith(vp_vfs):
|
2422
|
+
t = "share-mapping failed; req=[%s] dbv=[%s] vrem=[%s] n1=[%s] n2=[%s] purl=[%s]"
|
2423
|
+
zt = (self.vpath, dbv.vpath, vrem, vp_req, vp_vfs, ret["purl"])
|
2424
|
+
raise Pebkac(500, t % zt)
|
2425
|
+
ret["purl"] = vp_req + ret["purl"][len(vp_vfs) :]
|
2426
|
+
|
2383
2427
|
ret = json.dumps(ret)
|
2384
2428
|
self.log(ret)
|
2385
2429
|
self.reply(ret.encode("utf-8"), mime="application/json")
|
@@ -2472,7 +2516,11 @@ class HttpCli(object):
|
|
2472
2516
|
chashes.append(siblings[n : n + clen])
|
2473
2517
|
|
2474
2518
|
vfs, _ = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
2475
|
-
ptop =
|
2519
|
+
ptop = vfs.get_dbv("")[0].realpath
|
2520
|
+
# if this is a share, then get_dbv has been overridden to return
|
2521
|
+
# the dbv (which does not exist as a property). And its realpath
|
2522
|
+
# could point into the middle of its origin vfs node, meaning it
|
2523
|
+
# is not necessarily registered with up2k, so get_dbv is crucial
|
2476
2524
|
|
2477
2525
|
broker = self.conn.hsrv.broker
|
2478
2526
|
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
@@ -3378,25 +3426,29 @@ class HttpCli(object):
|
|
3378
3426
|
self.reply(response.encode("utf-8"))
|
3379
3427
|
return True
|
3380
3428
|
|
3381
|
-
def _chk_lastmod(self, file_ts )
|
3429
|
+
def _chk_lastmod(self, file_ts ) :
|
3430
|
+
# ret: lastmod, do_send, can_range
|
3382
3431
|
file_lastmod = formatdate(file_ts)
|
3383
|
-
|
3384
|
-
if
|
3385
|
-
|
3386
|
-
|
3387
|
-
|
3388
|
-
|
3389
|
-
|
3390
|
-
|
3391
|
-
|
3392
|
-
|
3393
|
-
|
3394
|
-
|
3395
|
-
|
3396
|
-
|
3397
|
-
|
3432
|
+
c_ifrange = self.headers.get("if-range")
|
3433
|
+
c_lastmod = self.headers.get("if-modified-since")
|
3434
|
+
|
3435
|
+
if not c_ifrange and not c_lastmod:
|
3436
|
+
return file_lastmod, True, True
|
3437
|
+
|
3438
|
+
if c_ifrange and c_ifrange != file_lastmod:
|
3439
|
+
t = "sending entire file due to If-Range; cli(%s) file(%s)"
|
3440
|
+
self.log(t % (c_ifrange, file_lastmod), 6)
|
3441
|
+
return file_lastmod, True, False
|
3442
|
+
|
3443
|
+
do_send = c_lastmod != file_lastmod
|
3444
|
+
if do_send and c_lastmod:
|
3445
|
+
t = "sending body due to If-Modified-Since cli(%s) file(%s)"
|
3446
|
+
self.log(t % (c_lastmod, file_lastmod), 6)
|
3447
|
+
elif not do_send and self.no304():
|
3448
|
+
do_send = True
|
3449
|
+
self.log("sending body due to no304")
|
3398
3450
|
|
3399
|
-
return file_lastmod, True
|
3451
|
+
return file_lastmod, do_send, True
|
3400
3452
|
|
3401
3453
|
def _use_dirkey(self, vn , ap ) :
|
3402
3454
|
if self.can_read or not self.can_get:
|
@@ -3546,7 +3598,7 @@ class HttpCli(object):
|
|
3546
3598
|
# if-modified
|
3547
3599
|
|
3548
3600
|
if file_ts > 0:
|
3549
|
-
file_lastmod, do_send = self._chk_lastmod(int(file_ts))
|
3601
|
+
file_lastmod, do_send, _ = self._chk_lastmod(int(file_ts))
|
3550
3602
|
self.out_headers["Last-Modified"] = file_lastmod
|
3551
3603
|
if not do_send:
|
3552
3604
|
status = 304
|
@@ -3629,6 +3681,8 @@ class HttpCli(object):
|
|
3629
3681
|
self.args.s_wr_sz,
|
3630
3682
|
self.args.s_wr_slp,
|
3631
3683
|
not self.args.no_poll,
|
3684
|
+
{},
|
3685
|
+
"",
|
3632
3686
|
)
|
3633
3687
|
res.close()
|
3634
3688
|
|
@@ -3708,7 +3762,7 @@ class HttpCli(object):
|
|
3708
3762
|
#
|
3709
3763
|
# if-modified
|
3710
3764
|
|
3711
|
-
file_lastmod, do_send = self._chk_lastmod(int(file_ts))
|
3765
|
+
file_lastmod, do_send, can_range = self._chk_lastmod(int(file_ts))
|
3712
3766
|
self.out_headers["Last-Modified"] = file_lastmod
|
3713
3767
|
if not do_send:
|
3714
3768
|
status = 304
|
@@ -3752,7 +3806,14 @@ class HttpCli(object):
|
|
3752
3806
|
|
3753
3807
|
# let's not support 206 with compression
|
3754
3808
|
# and multirange / multipart is also not-impl (mostly because calculating contentlength is a pain)
|
3755
|
-
if
|
3809
|
+
if (
|
3810
|
+
do_send
|
3811
|
+
and not is_compressed
|
3812
|
+
and hrange
|
3813
|
+
and can_range
|
3814
|
+
and file_sz
|
3815
|
+
and "," not in hrange
|
3816
|
+
):
|
3756
3817
|
try:
|
3757
3818
|
if not hrange.lower().startswith("bytes"):
|
3758
3819
|
raise Exception()
|
@@ -3836,6 +3897,19 @@ class HttpCli(object):
|
|
3836
3897
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
3837
3898
|
return True
|
3838
3899
|
|
3900
|
+
dls = self.conn.hsrv.dls
|
3901
|
+
if upper - lower > 0x400000: # 4m
|
3902
|
+
now = time.time()
|
3903
|
+
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
3904
|
+
dls[self.dl_id] = (now, 0)
|
3905
|
+
self.conn.hsrv.dli[self.dl_id] = (
|
3906
|
+
now,
|
3907
|
+
upper - lower,
|
3908
|
+
self.vn,
|
3909
|
+
self.vpath,
|
3910
|
+
self.uname,
|
3911
|
+
)
|
3912
|
+
|
3839
3913
|
if ptop is not None:
|
3840
3914
|
return self.tx_pipe(
|
3841
3915
|
ptop, req_path, ap_data, job, lower, upper, status, mime, logmsg
|
@@ -3855,6 +3929,8 @@ class HttpCli(object):
|
|
3855
3929
|
self.args.s_wr_sz,
|
3856
3930
|
self.args.s_wr_slp,
|
3857
3931
|
not self.args.no_poll,
|
3932
|
+
dls,
|
3933
|
+
self.dl_id,
|
3858
3934
|
)
|
3859
3935
|
|
3860
3936
|
if remains > 0:
|
@@ -4005,6 +4081,8 @@ class HttpCli(object):
|
|
4005
4081
|
wr_sz,
|
4006
4082
|
wr_slp,
|
4007
4083
|
not self.args.no_poll,
|
4084
|
+
self.conn.hsrv.dls,
|
4085
|
+
self.dl_id,
|
4008
4086
|
)
|
4009
4087
|
|
4010
4088
|
spd = self._spd((upper - lower) - remains)
|
@@ -4090,6 +4168,18 @@ class HttpCli(object):
|
|
4090
4168
|
self.log("transcoding to [{}]".format(cfmt))
|
4091
4169
|
fgen = gfilter(fgen, self.thumbcli, self.uname, vpath, cfmt)
|
4092
4170
|
|
4171
|
+
now = time.time()
|
4172
|
+
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
4173
|
+
self.conn.hsrv.dli[self.dl_id] = (
|
4174
|
+
now,
|
4175
|
+
0,
|
4176
|
+
self.vn,
|
4177
|
+
"%s :%s" % (self.vpath, ext),
|
4178
|
+
self.uname,
|
4179
|
+
)
|
4180
|
+
dls = self.conn.hsrv.dls
|
4181
|
+
dls[self.dl_id] = (time.time(), 0)
|
4182
|
+
|
4093
4183
|
bgen = packer(
|
4094
4184
|
self.log,
|
4095
4185
|
self.asrv,
|
@@ -4098,6 +4188,7 @@ class HttpCli(object):
|
|
4098
4188
|
pre_crc="crc" in uarg,
|
4099
4189
|
cmp=uarg if cancmp or uarg == "pax" else "",
|
4100
4190
|
)
|
4191
|
+
n = 0
|
4101
4192
|
bsent = 0
|
4102
4193
|
for buf in bgen.gen():
|
4103
4194
|
if not buf:
|
@@ -4111,6 +4202,11 @@ class HttpCli(object):
|
|
4111
4202
|
bgen.stop()
|
4112
4203
|
break
|
4113
4204
|
|
4205
|
+
n += 1
|
4206
|
+
if n >= 4:
|
4207
|
+
n = 0
|
4208
|
+
dls[self.dl_id] = (time.time(), bsent)
|
4209
|
+
|
4114
4210
|
spd = self._spd(bsent)
|
4115
4211
|
self.log("{}, {}".format(logmsg, spd))
|
4116
4212
|
return True
|
@@ -4223,7 +4319,7 @@ class HttpCli(object):
|
|
4223
4319
|
sz_md = len(lead) + len(fullfile)
|
4224
4320
|
|
4225
4321
|
file_ts = int(max(ts_md, self.E.t0))
|
4226
|
-
file_lastmod, do_send = self._chk_lastmod(file_ts)
|
4322
|
+
file_lastmod, do_send, _ = self._chk_lastmod(file_ts)
|
4227
4323
|
self.out_headers["Last-Modified"] = file_lastmod
|
4228
4324
|
self.out_headers.update(NO_CACHE)
|
4229
4325
|
status = 200 if do_send else 304
|
@@ -4367,6 +4463,32 @@ class HttpCli(object):
|
|
4367
4463
|
}
|
4368
4464
|
|
4369
4465
|
|
4466
|
+
dls = dl_list = []
|
4467
|
+
if self.conn.hsrv.tdls:
|
4468
|
+
zi = self.args.dl_list
|
4469
|
+
if zi == 2 or (zi == 1 and self.avol):
|
4470
|
+
dl_list = self.get_dls()
|
4471
|
+
for t0, t1, sent, sz, vp, dl_id, uname in dl_list:
|
4472
|
+
rem = sz - sent
|
4473
|
+
td = max(0.1, now - t0)
|
4474
|
+
rd, fn = vsplit(vp)
|
4475
|
+
if not rd:
|
4476
|
+
rd = "/"
|
4477
|
+
erd = quotep(rd)
|
4478
|
+
rds = rd.replace("/", " / ")
|
4479
|
+
spd = humansize(sent / td, True) + "/s"
|
4480
|
+
hsent = humansize(sent, True)
|
4481
|
+
idle = s2hms(now - t1, True)
|
4482
|
+
usr = "%s @%s" % (dl_id, uname) if dl_id else uname
|
4483
|
+
if sz and sent and td:
|
4484
|
+
eta = s2hms((sz - sent) / (sent / td), True)
|
4485
|
+
perc = int(100 * sent / sz)
|
4486
|
+
else:
|
4487
|
+
eta = perc = "--"
|
4488
|
+
|
4489
|
+
fn = html_escape(fn) if fn else self.conn.hsrv.iiam
|
4490
|
+
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
|
4491
|
+
|
4370
4492
|
fmt = self.uparam.get("ls", "")
|
4371
4493
|
if not fmt and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
4372
4494
|
fmt = "v"
|
@@ -4388,6 +4510,12 @@ class HttpCli(object):
|
|
4388
4510
|
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
4389
4511
|
txt += "\n"
|
4390
4512
|
|
4513
|
+
if dls:
|
4514
|
+
txt += "\n\nactive downloads:"
|
4515
|
+
for zt in dls:
|
4516
|
+
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
4517
|
+
txt += "\n"
|
4518
|
+
|
4391
4519
|
if rvol:
|
4392
4520
|
txt += "\nyou can browse:"
|
4393
4521
|
for v in rvol:
|
@@ -4409,8 +4537,9 @@ class HttpCli(object):
|
|
4409
4537
|
rvol=rvol,
|
4410
4538
|
wvol=wvol,
|
4411
4539
|
avol=avol,
|
4412
|
-
in_shr=self.args.shr and self.vpath.startswith(self.args.
|
4540
|
+
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
4413
4541
|
vstate=vstate,
|
4542
|
+
dls=dls,
|
4414
4543
|
ups=ups,
|
4415
4544
|
scanning=vs["scanning"],
|
4416
4545
|
hashq=vs["hashq"],
|
@@ -4419,7 +4548,9 @@ class HttpCli(object):
|
|
4419
4548
|
dbwt=vs["dbwt"],
|
4420
4549
|
url_suf=suf,
|
4421
4550
|
k304=self.k304(),
|
4551
|
+
no304=self.no304(),
|
4422
4552
|
k304vis=self.args.k304 > 0,
|
4553
|
+
no304vis=self.args.no304 > 0,
|
4423
4554
|
ver=S_VERSION if self.args.ver else "",
|
4424
4555
|
chpw=self.args.chpw and self.uname != "*",
|
4425
4556
|
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
@@ -4427,29 +4558,21 @@ class HttpCli(object):
|
|
4427
4558
|
self.reply(html.encode("utf-8"))
|
4428
4559
|
return True
|
4429
4560
|
|
4430
|
-
def set_k304(self) :
|
4431
|
-
v = self.uparam["k304"].lower()
|
4432
|
-
if v in "yn":
|
4433
|
-
dur = 86400 * 299
|
4434
|
-
else:
|
4435
|
-
dur = 0
|
4436
|
-
v = "x"
|
4437
|
-
|
4438
|
-
ck = gencookie("k304", v, self.args.R, False, dur)
|
4439
|
-
self.out_headerlist.append(("Set-Cookie", ck))
|
4440
|
-
self.redirect("", "?h#cc")
|
4441
|
-
return True
|
4442
|
-
|
4443
4561
|
def setck(self) :
|
4444
4562
|
k, v = self.uparam["setck"].split("=", 1)
|
4445
|
-
t = 0 if v
|
4563
|
+
t = 0 if v in ("", "x") else 86400 * 299
|
4446
4564
|
ck = gencookie(k, v, self.args.R, False, t)
|
4447
4565
|
self.out_headerlist.append(("Set-Cookie", ck))
|
4448
|
-
self.
|
4566
|
+
if "cc" in self.ouparam:
|
4567
|
+
self.redirect("", "?h#cc")
|
4568
|
+
else:
|
4569
|
+
self.reply(b"o7\n")
|
4449
4570
|
return True
|
4450
4571
|
|
4451
4572
|
def set_cfg_reset(self) :
|
4452
|
-
for k in
|
4573
|
+
for k in ALL_COOKIES:
|
4574
|
+
if k not in self.cookies:
|
4575
|
+
continue
|
4453
4576
|
cookie = gencookie(k, "x", self.args.R, False)
|
4454
4577
|
self.out_headerlist.append(("Set-Cookie", cookie))
|
4455
4578
|
|
@@ -4479,8 +4602,14 @@ class HttpCli(object):
|
|
4479
4602
|
|
4480
4603
|
t = t.format(self.args.SR)
|
4481
4604
|
qv = quotep(self.vpaths) + self.ourlq()
|
4482
|
-
|
4483
|
-
|
4605
|
+
html = self.j2s(
|
4606
|
+
"splash",
|
4607
|
+
this=self,
|
4608
|
+
qvpath=qv,
|
4609
|
+
msg=t,
|
4610
|
+
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
4611
|
+
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
4612
|
+
)
|
4484
4613
|
self.reply(html.encode("utf-8"), status=rc)
|
4485
4614
|
return True
|
4486
4615
|
|
@@ -4528,7 +4657,7 @@ class HttpCli(object):
|
|
4528
4657
|
if self.args.no_reload:
|
4529
4658
|
raise Pebkac(403, "the reload feature is disabled in server config")
|
4530
4659
|
|
4531
|
-
x = self.conn.hsrv.broker.ask("reload")
|
4660
|
+
x = self.conn.hsrv.broker.ask("reload", True, True)
|
4532
4661
|
return self.redirect("", "?h", x.get(), "return to", False)
|
4533
4662
|
|
4534
4663
|
def tx_stack(self) :
|
@@ -4631,6 +4760,40 @@ class HttpCli(object):
|
|
4631
4760
|
ret["a"] = dirs
|
4632
4761
|
return ret
|
4633
4762
|
|
4763
|
+
def get_dls(self) :
|
4764
|
+
ret = []
|
4765
|
+
dls = self.conn.hsrv.tdls
|
4766
|
+
for dl_id, (t0, sz, vn, vp, uname) in self.conn.hsrv.tdli.items():
|
4767
|
+
t1, sent = dls[dl_id]
|
4768
|
+
if sent > 0x100000: # 1m; buffers 2~4
|
4769
|
+
sent -= 0x100000
|
4770
|
+
if self.uname not in vn.axs.uread:
|
4771
|
+
vp = ""
|
4772
|
+
elif self.uname not in vn.axs.udot and (vp.startswith(".") or "/." in vp):
|
4773
|
+
vp = ""
|
4774
|
+
if self.uname not in vn.axs.uadmin:
|
4775
|
+
dl_id = uname = ""
|
4776
|
+
|
4777
|
+
ret.append([t0, t1, sent, sz, vp, dl_id, uname])
|
4778
|
+
return ret
|
4779
|
+
|
4780
|
+
def tx_dls(self) :
|
4781
|
+
ret = [
|
4782
|
+
{
|
4783
|
+
"t0": x[0],
|
4784
|
+
"t1": x[1],
|
4785
|
+
"sent": x[2],
|
4786
|
+
"size": x[3],
|
4787
|
+
"path": x[4],
|
4788
|
+
"conn": x[5],
|
4789
|
+
"uname": x[6],
|
4790
|
+
}
|
4791
|
+
for x in self.get_dls()
|
4792
|
+
]
|
4793
|
+
zs = json.dumps(ret, separators=(",\n", ": "))
|
4794
|
+
self.reply(zs.encode("utf-8", "replace"), mime="application/json")
|
4795
|
+
return True
|
4796
|
+
|
4634
4797
|
def tx_ups(self) :
|
4635
4798
|
idx = self.conn.get_u2idx()
|
4636
4799
|
if not idx or not hasattr(idx, "p_end"):
|
@@ -4642,6 +4805,11 @@ class HttpCli(object):
|
|
4642
4805
|
lm = "ups [{}]".format(filt)
|
4643
4806
|
self.log(lm)
|
4644
4807
|
|
4808
|
+
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
4809
|
+
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
4810
|
+
else:
|
4811
|
+
shr_dbv = None
|
4812
|
+
|
4645
4813
|
ret = []
|
4646
4814
|
t0 = time.time()
|
4647
4815
|
lim = time.time() - self.args.unpost
|
@@ -4662,7 +4830,12 @@ class HttpCli(object):
|
|
4662
4830
|
else:
|
4663
4831
|
allvols = list(self.asrv.vfs.all_vols.values())
|
4664
4832
|
|
4665
|
-
allvols = [
|
4833
|
+
allvols = [
|
4834
|
+
x
|
4835
|
+
for x in allvols
|
4836
|
+
if "e2d" in x.flags
|
4837
|
+
and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv)
|
4838
|
+
]
|
4666
4839
|
|
4667
4840
|
for vol in allvols:
|
4668
4841
|
cur = idx.get_cur(vol)
|
@@ -4712,6 +4885,15 @@ class HttpCli(object):
|
|
4712
4885
|
|
4713
4886
|
ret = ret[:2000]
|
4714
4887
|
|
4888
|
+
if shr_dbv:
|
4889
|
+
# translate vpaths from share-target to share-url
|
4890
|
+
# to satisfy access checks
|
4891
|
+
vp_shr, vp_vfs = vroots(self.vpath, vjoin(shr_dbv.vpath, shr_vrem))
|
4892
|
+
for v in ret:
|
4893
|
+
vp = v["vp"]
|
4894
|
+
if vp.startswith(vp_vfs):
|
4895
|
+
v["vp"] = vp_shr + vp[len(vp_vfs) :]
|
4896
|
+
|
4715
4897
|
if self.is_vproxied:
|
4716
4898
|
for v in ret:
|
4717
4899
|
v["vp"] = self.args.SR + v["vp"]
|
@@ -4796,7 +4978,7 @@ class HttpCli(object):
|
|
4796
4978
|
|
4797
4979
|
cur.connection.commit()
|
4798
4980
|
if reload:
|
4799
|
-
self.conn.hsrv.broker.ask("
|
4981
|
+
self.conn.hsrv.broker.ask("reload", False, False).get()
|
4800
4982
|
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4801
4983
|
|
4802
4984
|
self.redirect(self.args.SRS + "?shares")
|
@@ -4841,7 +5023,7 @@ class HttpCli(object):
|
|
4841
5023
|
if m:
|
4842
5024
|
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
4843
5025
|
|
4844
|
-
if vp.startswith(self.args.
|
5026
|
+
if vp.startswith(self.args.shr1):
|
4845
5027
|
raise Pebkac(400, "yo dawg...")
|
4846
5028
|
|
4847
5029
|
cur = idx.get_shr()
|
@@ -4887,7 +5069,7 @@ class HttpCli(object):
|
|
4887
5069
|
cur.execute(q, (skey, fn))
|
4888
5070
|
|
4889
5071
|
cur.connection.commit()
|
4890
|
-
self.conn.hsrv.broker.ask("
|
5072
|
+
self.conn.hsrv.broker.ask("reload", False, False).get()
|
4891
5073
|
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4892
5074
|
|
4893
5075
|
fn = quotep(fns[0]) if len(fns) == 1 else ""
|
@@ -4938,16 +5120,39 @@ class HttpCli(object):
|
|
4938
5120
|
return self._mv(self.vpath, dst.lstrip("/"))
|
4939
5121
|
|
4940
5122
|
def _mv(self, vsrc , vdst ) :
|
4941
|
-
if not self.can_move:
|
4942
|
-
raise Pebkac(403, "not allowed for user " + self.uname)
|
4943
|
-
|
4944
5123
|
if self.args.no_mv:
|
4945
5124
|
raise Pebkac(403, "the rename/move feature is disabled in server config")
|
4946
5125
|
|
5126
|
+
self.asrv.vfs.get(vsrc, self.uname, True, False, True)
|
5127
|
+
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5128
|
+
|
4947
5129
|
x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst)
|
4948
5130
|
self.loud_reply(x.get(), status=201)
|
4949
5131
|
return True
|
4950
5132
|
|
5133
|
+
def handle_cp(self) :
|
5134
|
+
# full path of new loc (incl filename)
|
5135
|
+
dst = self.uparam.get("copy")
|
5136
|
+
|
5137
|
+
if self.is_vproxied and dst and dst.startswith(self.args.SR):
|
5138
|
+
dst = dst[len(self.args.RS) :]
|
5139
|
+
|
5140
|
+
if not dst:
|
5141
|
+
raise Pebkac(400, "need dst vpath")
|
5142
|
+
|
5143
|
+
return self._cp(self.vpath, dst.lstrip("/"))
|
5144
|
+
|
5145
|
+
def _cp(self, vsrc , vdst ) :
|
5146
|
+
if self.args.no_cp:
|
5147
|
+
raise Pebkac(403, "the copy feature is disabled in server config")
|
5148
|
+
|
5149
|
+
self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5150
|
+
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5151
|
+
|
5152
|
+
x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst)
|
5153
|
+
self.loud_reply(x.get(), status=201)
|
5154
|
+
return True
|
5155
|
+
|
4951
5156
|
def tx_ls(self, ls ) :
|
4952
5157
|
dirs = ls["dirs"]
|
4953
5158
|
files = ls["files"]
|
@@ -5377,6 +5582,7 @@ class HttpCli(object):
|
|
5377
5582
|
not self.args.no_scandir,
|
5378
5583
|
[[True, False], [False, True]],
|
5379
5584
|
lstat="lt" in self.uparam,
|
5585
|
+
throw=True,
|
5380
5586
|
)
|
5381
5587
|
stats = {k: v for k, v in vfs_ls}
|
5382
5588
|
ls_names = [x[0] for x in vfs_ls]
|