copyparty 1.17.0__py3-none-any.whl → 1.17.1__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/__main__.py +5 -2
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +4 -0
- copyparty/cert.py +3 -5
- copyparty/cfg.py +6 -0
- copyparty/httpcli.py +35 -36
- copyparty/sutil.py +4 -3
- copyparty/tftpd.py +2 -1
- copyparty/th_srv.py +3 -3
- copyparty/up2k.py +15 -8
- copyparty/util.py +17 -9
- copyparty/web/a/u2c.py +3 -3
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/svcs.html +1 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/METADATA +11 -3
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/RECORD +22 -22
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/WHEEL +1 -1
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.17.0.dist-info → copyparty-1.17.1.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -995,6 +995,9 @@ def add_upload(ap):
|
|
995
995
|
ap2 = ap.add_argument_group('upload options')
|
996
996
|
ap2.add_argument("--dotpart", action="store_true", help="dotfile incomplete uploads, hiding them from clients unless \033[33m-ed\033[0m")
|
997
997
|
ap2.add_argument("--plain-ip", action="store_true", help="when avoiding filename collisions by appending the uploader's ip to the filename: append the plaintext ip instead of salting and hashing the ip")
|
998
|
+
ap2.add_argument("--put-name", metavar="TXT", type=u, default="put-{now.6f}-{cip}.bin", help="filename for nameless uploads (when uploader doesn't provide a name); default is [\033[32mput-UNIXTIME-IP.bin\033[0m] (the \033[32m.6f\033[0m means six decimal places) (volflag=put_name)")
|
999
|
+
ap2.add_argument("--put-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for PUT/WebDAV uploads: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=put_ck)")
|
1000
|
+
ap2.add_argument("--bup-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for bup/basic-uploader: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=bup_ck)")
|
998
1001
|
ap2.add_argument("--unpost", metavar="SEC", type=int, default=3600*12, help="grace period where uploads can be deleted by the uploader, even without delete permissions; 0=disabled, default=12h")
|
999
1002
|
ap2.add_argument("--u2abort", metavar="NUM", type=int, default=1, help="clients can abort incomplete uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=u2abort)")
|
1000
1003
|
ap2.add_argument("--blank-wt", metavar="SEC", type=int, default=300, help="file write grace period (any client can write to a blank file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
@@ -1371,8 +1374,8 @@ def add_thumbnail(ap):
|
|
1371
1374
|
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips")
|
1372
1375
|
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
1373
1376
|
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
1374
|
-
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
1375
|
-
ap2.add_argument("--th-spec-cnv", metavar="T
|
1377
|
+
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
1378
|
+
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
1376
1379
|
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz", help="audio/image formats to decompress before passing to ffmpeg")
|
1377
1380
|
|
1378
1381
|
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -2067,6 +2067,10 @@ class AuthSrv(object):
|
|
2067
2067
|
if len(zs) == 3: # fc5 => ffcc55
|
2068
2068
|
vol.flags["tcolor"] = "".join([x * 2 for x in zs])
|
2069
2069
|
|
2070
|
+
# volflag syntax currently doesn't allow for ':' in value
|
2071
|
+
zs = vol.flags["put_name"]
|
2072
|
+
vol.flags["put_name2"] = zs.replace("{now.", "{now:.")
|
2073
|
+
|
2070
2074
|
if vol.flags.get("neversymlink"):
|
2071
2075
|
vol.flags["hardlinkonly"] = True # was renamed
|
2072
2076
|
if vol.flags.get("hardlinkonly"):
|
copyparty/cert.py
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
import calendar
|
2
2
|
import errno
|
3
|
-
import filecmp
|
4
3
|
import json
|
5
4
|
import os
|
6
|
-
import shutil
|
7
5
|
import time
|
8
6
|
|
9
7
|
from .__init__ import ANYWIN
|
10
|
-
from .util import Netdev, load_resource, runcmd,
|
8
|
+
from .util import Netdev, atomic_move, load_resource, runcmd, wunlink
|
11
9
|
|
12
10
|
HAVE_CFSSL = not os.environ.get("PRTY_NO_CFSSL")
|
13
11
|
|
@@ -118,7 +116,7 @@ def _gen_ca(log , args):
|
|
118
116
|
wunlink(nlog, bname + ".key", VF)
|
119
117
|
except:
|
120
118
|
pass
|
121
|
-
|
119
|
+
atomic_move(nlog, bname + "-key.pem", bname + ".key", VF)
|
122
120
|
wunlink(nlog, bname + ".csr", VF)
|
123
121
|
|
124
122
|
log("cert", "new ca OK", 2)
|
@@ -211,7 +209,7 @@ def _gen_srv(log , args, netdevs ):
|
|
211
209
|
wunlink(nlog, bname + ".key", VF)
|
212
210
|
except:
|
213
211
|
pass
|
214
|
-
|
212
|
+
atomic_move(nlog, bname + "-key.pem", bname + ".key", VF)
|
215
213
|
wunlink(nlog, bname + ".csr", VF)
|
216
214
|
|
217
215
|
with open(os.path.join(args.crt_dir, "ca.pem"), "rb") as f:
|
copyparty/cfg.py
CHANGED
@@ -75,6 +75,7 @@ def vf_vmap() :
|
|
75
75
|
"th_x3": "th3x",
|
76
76
|
}
|
77
77
|
for k in (
|
78
|
+
"bup_ck",
|
78
79
|
"dbd",
|
79
80
|
"forget_ip",
|
80
81
|
"hsortn",
|
@@ -95,6 +96,8 @@ def vf_vmap() :
|
|
95
96
|
"og_title_i",
|
96
97
|
"og_tpl",
|
97
98
|
"og_ua",
|
99
|
+
"put_ck",
|
100
|
+
"put_name",
|
98
101
|
"mv_retry",
|
99
102
|
"rm_retry",
|
100
103
|
"sort",
|
@@ -165,6 +168,9 @@ flagcats = {
|
|
165
168
|
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
166
169
|
"nosub": "forces all uploads into the top folder of the vfs",
|
167
170
|
"magic": "enables filetype detection for nameless uploads",
|
171
|
+
"put_name": "fallback filename for nameless uploads",
|
172
|
+
"put_ck": "default checksum-hasher for PUT/WebDAV uploads",
|
173
|
+
"bup_ck": "default checksum-hasher for bup/basic uploads",
|
168
174
|
"gz": "allows server-side gzip compression of uploads with ?gz",
|
169
175
|
"xz": "allows server-side lzma compression of uploads with ?xz",
|
170
176
|
"pk": "forces server-side compression, optional arg: xz,9",
|
copyparty/httpcli.py
CHANGED
@@ -113,7 +113,6 @@ from .util import (
|
|
113
113
|
vol_san,
|
114
114
|
vroots,
|
115
115
|
vsplit,
|
116
|
-
wrename,
|
117
116
|
wunlink,
|
118
117
|
yieldfile,
|
119
118
|
)
|
@@ -1408,7 +1407,7 @@ class HttpCli(object):
|
|
1408
1407
|
desc = "%s - %s" % (tag_a, tag_t) if tag_t and tag_a else (tag_t or tag_a)
|
1409
1408
|
desc = html_escape(desc, True, True) if desc else title
|
1410
1409
|
mime = html_escape(guess_mime(title))
|
1411
|
-
lmod = formatdate(i["ts"])
|
1410
|
+
lmod = formatdate(max(0, i["ts"]))
|
1412
1411
|
zsa = (iurl, iurl, title, desc, lmod, iurl, mime, i["sz"])
|
1413
1412
|
zs = (
|
1414
1413
|
"""\
|
@@ -1565,7 +1564,7 @@ class HttpCli(object):
|
|
1565
1564
|
for x in fgen:
|
1566
1565
|
rp = vjoin(vtop, x["vp"])
|
1567
1566
|
st = x["st"]
|
1568
|
-
mtime = st.st_mtime
|
1567
|
+
mtime = max(0, st.st_mtime)
|
1569
1568
|
if stat.S_ISLNK(st.st_mode):
|
1570
1569
|
try:
|
1571
1570
|
st = bos.stat(os.path.join(tap, x["vp"]))
|
@@ -2091,8 +2090,7 @@ class HttpCli(object):
|
|
2091
2090
|
suffix = "-{:.6f}-{}".format(time.time(), self.dip())
|
2092
2091
|
nameless = not fn
|
2093
2092
|
if nameless:
|
2094
|
-
|
2095
|
-
fn = "put" + suffix
|
2093
|
+
fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip())
|
2096
2094
|
|
2097
2095
|
params = {"suffix": suffix, "fdir": fdir}
|
2098
2096
|
if self.args.nw:
|
@@ -2171,28 +2169,26 @@ class HttpCli(object):
|
|
2171
2169
|
# small toctou, but better than clobbering a hardlink
|
2172
2170
|
wunlink(self.log, path, vfs.flags)
|
2173
2171
|
|
2174
|
-
halg = "sha512"
|
2175
2172
|
hasher = None
|
2176
2173
|
copier = hashcopy
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2186
|
-
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
raise Pebkac(500, "unknown hash alg")
|
2174
|
+
halg = self.ouparam.get("ck") or self.headers.get("ck") or vfs.flags["put_ck"]
|
2175
|
+
if halg == "sha512":
|
2176
|
+
pass
|
2177
|
+
elif halg == "no":
|
2178
|
+
copier = justcopy
|
2179
|
+
halg = ""
|
2180
|
+
elif halg == "md5":
|
2181
|
+
hasher = hashlib.md5(**USED4SEC)
|
2182
|
+
elif halg == "sha1":
|
2183
|
+
hasher = hashlib.sha1(**USED4SEC)
|
2184
|
+
elif halg == "sha256":
|
2185
|
+
hasher = hashlib.sha256(**USED4SEC)
|
2186
|
+
elif halg in ("blake2", "b2"):
|
2187
|
+
hasher = hashlib.blake2b(**USED4SEC)
|
2188
|
+
elif halg in ("blake2s", "b2s"):
|
2189
|
+
hasher = hashlib.blake2s(**USED4SEC)
|
2190
|
+
else:
|
2191
|
+
raise Pebkac(500, "unknown hash alg")
|
2196
2192
|
|
2197
2193
|
f, fn = ren_open(fn, *open_a, **params)
|
2198
2194
|
try:
|
@@ -2917,7 +2913,8 @@ class HttpCli(object):
|
|
2917
2913
|
self.parser.drop()
|
2918
2914
|
|
2919
2915
|
self.log("logout " + self.uname)
|
2920
|
-
self.
|
2916
|
+
if not self.uname.startswith("s_"):
|
2917
|
+
self.asrv.forget_session(self.conn.hsrv.broker, self.uname)
|
2921
2918
|
self.get_pwd_cookie("x")
|
2922
2919
|
|
2923
2920
|
dst = self.args.SRS + "?h"
|
@@ -3068,15 +3065,18 @@ class HttpCli(object):
|
|
3068
3065
|
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
3069
3066
|
self._assert_safe_rem(rem)
|
3070
3067
|
|
3071
|
-
halg = "sha512"
|
3072
3068
|
hasher = None
|
3073
|
-
copier = hashcopy
|
3074
3069
|
if nohash:
|
3075
3070
|
halg = ""
|
3076
3071
|
copier = justcopy
|
3077
|
-
|
3078
|
-
|
3079
|
-
|
3072
|
+
else:
|
3073
|
+
copier = hashcopy
|
3074
|
+
halg = (
|
3075
|
+
self.ouparam.get("ck") or self.headers.get("ck") or vfs.flags["bup_ck"]
|
3076
|
+
)
|
3077
|
+
if halg == "sha512":
|
3078
|
+
pass
|
3079
|
+
elif halg == "no":
|
3080
3080
|
copier = justcopy
|
3081
3081
|
halg = ""
|
3082
3082
|
elif halg == "md5":
|
@@ -3089,8 +3089,6 @@ class HttpCli(object):
|
|
3089
3089
|
hasher = hashlib.blake2b(**USED4SEC)
|
3090
3090
|
elif halg in ("blake2s", "b2s"):
|
3091
3091
|
hasher = hashlib.blake2s(**USED4SEC)
|
3092
|
-
elif halg == "sha512":
|
3093
|
-
pass
|
3094
3092
|
else:
|
3095
3093
|
raise Pebkac(500, "unknown hash alg")
|
3096
3094
|
|
@@ -3552,7 +3550,7 @@ class HttpCli(object):
|
|
3552
3550
|
except:
|
3553
3551
|
pass
|
3554
3552
|
if dp:
|
3555
|
-
|
3553
|
+
atomic_move(self.log, fp, os.path.join(dp, mfile2), vfs.flags)
|
3556
3554
|
|
3557
3555
|
p_field, _, p_data = next(self.parser.gen)
|
3558
3556
|
if p_field != "body":
|
@@ -3961,7 +3959,7 @@ class HttpCli(object):
|
|
3961
3959
|
for ext in ("", ".gz"):
|
3962
3960
|
if ptop is not None:
|
3963
3961
|
sz = job["size"]
|
3964
|
-
file_ts = job["lmod"]
|
3962
|
+
file_ts = max(0, job["lmod"])
|
3965
3963
|
editions["plain"] = (ap_data, sz)
|
3966
3964
|
break
|
3967
3965
|
|
@@ -5482,6 +5480,7 @@ class HttpCli(object):
|
|
5482
5480
|
raise Pebkac(400, "selected file not found on disk: [%s]" % (fn,))
|
5483
5481
|
|
5484
5482
|
pw = req.get("pw") or ""
|
5483
|
+
pw = self.asrv.ah.hash(pw)
|
5485
5484
|
now = int(time.time())
|
5486
5485
|
sexp = req["exp"]
|
5487
5486
|
exp = int(sexp) if sexp else 0
|
@@ -6099,7 +6098,7 @@ class HttpCli(object):
|
|
6099
6098
|
margin = "-"
|
6100
6099
|
|
6101
6100
|
sz = inf.st_size
|
6102
|
-
zd = datetime.fromtimestamp(linf.st_mtime, UTC)
|
6101
|
+
zd = datetime.fromtimestamp(max(0, linf.st_mtime), UTC)
|
6103
6102
|
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
6104
6103
|
zd.year,
|
6105
6104
|
zd.month,
|
copyparty/sutil.py
CHANGED
@@ -11,6 +11,9 @@ from .bos import bos
|
|
11
11
|
from .th_cli import ThumbCli
|
12
12
|
from .util import UTC, vjoin, vol_san
|
13
13
|
|
14
|
+
TAR_NO_OPUS = set("aac|m4a|mp3|oga|ogg|opus|wma".split("|"))
|
15
|
+
|
16
|
+
|
14
17
|
class StreamArc(object):
|
15
18
|
def __init__(
|
16
19
|
self,
|
@@ -76,9 +79,7 @@ def enthumb(
|
|
76
79
|
) :
|
77
80
|
rem = f["vp"]
|
78
81
|
ext = rem.rsplit(".", 1)[-1].lower()
|
79
|
-
if (fmt == "mp3" and ext == "mp3") or (
|
80
|
-
fmt == "opus" and ext in "aac|m4a|mp3|ogg|opus|wma".split("|")
|
81
|
-
):
|
82
|
+
if (fmt == "mp3" and ext == "mp3") or (fmt == "opus" and ext in TAR_NO_OPUS):
|
82
83
|
raise Exception()
|
83
84
|
|
84
85
|
vp = vjoin(vtop, rem.split("/", 1)[1])
|
copyparty/tftpd.py
CHANGED
@@ -281,6 +281,7 @@ class Tftpd(object):
|
|
281
281
|
if not ptn or not ptn.match(fn.lower()):
|
282
282
|
return None
|
283
283
|
|
284
|
+
tsdt = datetime.fromtimestamp
|
284
285
|
vn, rem = self.asrv.vfs.get(vpath, "*", True, False)
|
285
286
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
286
287
|
rem,
|
@@ -293,7 +294,7 @@ class Tftpd(object):
|
|
293
294
|
dirs1 = [(v.st_mtime, v.st_size, k + "/") for k, v in vfs_ls if k in dnames]
|
294
295
|
fils1 = [(v.st_mtime, v.st_size, k) for k, v in vfs_ls if k not in dnames]
|
295
296
|
real1 = dirs1 + fils1
|
296
|
-
realt = [(
|
297
|
+
realt = [(tsdt(max(0, mt), UTC), sz, fn) for mt, sz, fn in real1]
|
297
298
|
reals = [
|
298
299
|
(
|
299
300
|
"%04d-%02d-%02d %02d:%02d:%02d"
|
copyparty/th_srv.py
CHANGED
@@ -24,13 +24,13 @@ from .util import (
|
|
24
24
|
Cooldown,
|
25
25
|
Daemon,
|
26
26
|
afsenc,
|
27
|
+
atomic_move,
|
27
28
|
fsenc,
|
28
29
|
min_ex,
|
29
30
|
runcmd,
|
30
31
|
statdir,
|
31
32
|
ub64enc,
|
32
33
|
vsplit,
|
33
|
-
wrename,
|
34
34
|
wunlink,
|
35
35
|
)
|
36
36
|
|
@@ -409,7 +409,7 @@ class ThumbSrv(object):
|
|
409
409
|
wunlink(self.log, ap_unpk, vn.flags)
|
410
410
|
|
411
411
|
try:
|
412
|
-
|
412
|
+
atomic_move(self.log, ttpath, tpath, vn.flags)
|
413
413
|
except Exception as ex:
|
414
414
|
if not os.path.exists(tpath):
|
415
415
|
t = "failed to move [%s] to [%s]: %r"
|
@@ -673,7 +673,7 @@ class ThumbSrv(object):
|
|
673
673
|
except:
|
674
674
|
pass
|
675
675
|
else:
|
676
|
-
|
676
|
+
atomic_move(self.log, wtpath, tpath, vn.flags)
|
677
677
|
|
678
678
|
def conv_spec(self, abspath , tpath , fmt , vn ) :
|
679
679
|
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
copyparty/up2k.py
CHANGED
@@ -1112,7 +1112,7 @@ class Up2k(object):
|
|
1112
1112
|
ft = "\033[0;32m{}{:.0}"
|
1113
1113
|
ff = "\033[0;35m{}{:.0}"
|
1114
1114
|
fv = "\033[0;36m{}:\033[90m{}"
|
1115
|
-
zs = "ext_th_d html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
1115
|
+
zs = "ext_th_d html_head put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
1116
1116
|
fx = set(zs.split())
|
1117
1117
|
fd = vf_bmap()
|
1118
1118
|
fd.update(vf_cmap())
|
@@ -2110,11 +2110,12 @@ class Up2k(object):
|
|
2110
2110
|
return -1
|
2111
2111
|
|
2112
2112
|
w = bw[:-1].decode("ascii")
|
2113
|
+
w16 = w[:16]
|
2113
2114
|
|
2114
2115
|
with self.mutex:
|
2115
2116
|
try:
|
2116
2117
|
q = "select rd, fn, ip, at from up where substr(w,1,16)=? and +w=?"
|
2117
|
-
rd, fn, ip, at = cur.execute(q, (
|
2118
|
+
rd, fn, ip, at = cur.execute(q, (w16, w)).fetchone()
|
2118
2119
|
except:
|
2119
2120
|
# file modified/deleted since spooling
|
2120
2121
|
continue
|
@@ -2123,8 +2124,12 @@ class Up2k(object):
|
|
2123
2124
|
rd, fn = s3dec(rd, fn)
|
2124
2125
|
|
2125
2126
|
if "mtp" in flags:
|
2127
|
+
q = "select 1 from mt where w=? and +k='t:mtp' limit 1"
|
2128
|
+
if cur.execute(q, (w16,)).fetchone():
|
2129
|
+
continue
|
2130
|
+
|
2126
2131
|
q = "insert into mt values (?,'t:mtp','a')"
|
2127
|
-
cur.execute(q, (
|
2132
|
+
cur.execute(q, (w16,))
|
2128
2133
|
|
2129
2134
|
abspath = djoin(ptop, rd, fn)
|
2130
2135
|
self.pp.msg = "c%d %s" % (nq, abspath)
|
@@ -2180,7 +2185,7 @@ class Up2k(object):
|
|
2180
2185
|
return tf, -1
|
2181
2186
|
|
2182
2187
|
if flt == 1:
|
2183
|
-
q = "select
|
2188
|
+
q = "select 1 from mt where w=? and +k != 't:mtp'"
|
2184
2189
|
if c2.execute(q, (row[0][:16],)).fetchone():
|
2185
2190
|
continue
|
2186
2191
|
|
@@ -3215,7 +3220,7 @@ class Up2k(object):
|
|
3215
3220
|
if hr.get("reloc"):
|
3216
3221
|
x = pathmod(self.vfs, dst, vp, hr["reloc"])
|
3217
3222
|
if x:
|
3218
|
-
|
3223
|
+
ud1 = (vfs.vpath, job["prel"], job["name"])
|
3219
3224
|
pdir, _, job["name"], (vfs, rem) = x
|
3220
3225
|
dst = os.path.join(pdir, job["name"])
|
3221
3226
|
job["vcfg"] = vfs.flags
|
@@ -3223,7 +3228,8 @@ class Up2k(object):
|
|
3223
3228
|
job["vtop"] = vfs.vpath
|
3224
3229
|
job["prel"] = rem
|
3225
3230
|
job["name"] = sanitize_fn(job["name"], "")
|
3226
|
-
|
3231
|
+
ud2 = (vfs.vpath, job["prel"], job["name"])
|
3232
|
+
if ud1 != ud2:
|
3227
3233
|
# print(json.dumps(job, sort_keys=True, indent=4))
|
3228
3234
|
job["hash"] = cj["hash"]
|
3229
3235
|
self.log("xbu reloc1:%d..." % (depth,), 6)
|
@@ -4970,14 +4976,15 @@ class Up2k(object):
|
|
4970
4976
|
if hr.get("reloc"):
|
4971
4977
|
x = pathmod(self.vfs, ap_chk, vp_chk, hr["reloc"])
|
4972
4978
|
if x:
|
4973
|
-
|
4979
|
+
ud1 = (vfs.vpath, job["prel"], job["name"])
|
4974
4980
|
pdir, _, job["name"], (vfs, rem) = x
|
4975
4981
|
job["vcfg"] = vf = vfs.flags
|
4976
4982
|
job["ptop"] = vfs.realpath
|
4977
4983
|
job["vtop"] = vfs.vpath
|
4978
4984
|
job["prel"] = rem
|
4979
4985
|
job["name"] = sanitize_fn(job["name"], "")
|
4980
|
-
|
4986
|
+
ud2 = (vfs.vpath, job["prel"], job["name"])
|
4987
|
+
if ud1 != ud2:
|
4981
4988
|
self.log("xbu reloc2:%d..." % (depth,), 6)
|
4982
4989
|
return self._handle_json(job, depth + 1)
|
4983
4990
|
|
copyparty/util.py
CHANGED
@@ -2497,6 +2497,11 @@ def _fs_mvrm(
|
|
2497
2497
|
now = time.time()
|
2498
2498
|
if ex.errno == errno.ENOENT:
|
2499
2499
|
return False
|
2500
|
+
if not attempt and ex.errno == errno.EXDEV:
|
2501
|
+
t = "using copy+delete (%s)\n %s\n %s"
|
2502
|
+
log(t % (ex.strerror, src, dst))
|
2503
|
+
osfun = shutil.move
|
2504
|
+
continue
|
2500
2505
|
if now - t0 > maxtime or attempt == 90209:
|
2501
2506
|
raise
|
2502
2507
|
if not attempt:
|
@@ -2521,15 +2526,18 @@ def atomic_move(log , src , dst , flags ) :
|
|
2521
2526
|
elif flags.get("mv_re_t"):
|
2522
2527
|
_fs_mvrm(log, src, dst, True, flags)
|
2523
2528
|
else:
|
2524
|
-
|
2525
|
-
|
2526
|
-
|
2527
|
-
|
2528
|
-
|
2529
|
-
|
2530
|
-
|
2531
|
-
|
2532
|
-
|
2529
|
+
try:
|
2530
|
+
os.replace(bsrc, bdst)
|
2531
|
+
except OSError as ex:
|
2532
|
+
if ex.errno != errno.EXDEV:
|
2533
|
+
raise
|
2534
|
+
t = "using copy+delete (%s);\n %s\n %s"
|
2535
|
+
log(t % (ex.strerror, src, dst))
|
2536
|
+
try:
|
2537
|
+
os.unlink(bdst)
|
2538
|
+
except:
|
2539
|
+
pass
|
2540
|
+
shutil.move(bsrc, bdst)
|
2533
2541
|
|
2534
2542
|
|
2535
2543
|
def wunlink(log , abspath , flags ) :
|
copyparty/web/a/u2c.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
-
S_VERSION = "2.
|
5
|
-
S_BUILD_DT = "2025-
|
4
|
+
S_VERSION = "2.11"
|
5
|
+
S_BUILD_DT = "2025-05-18"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -1287,7 +1287,7 @@ class Ctl(object):
|
|
1287
1287
|
if self.ar.jw:
|
1288
1288
|
print("%s %s" % (wark, vp))
|
1289
1289
|
else:
|
1290
|
-
zd = datetime.datetime.fromtimestamp(file.lmod, UTC)
|
1290
|
+
zd = datetime.datetime.fromtimestamp(max(0, file.lmod), UTC)
|
1291
1291
|
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
1292
1292
|
zd.year,
|
1293
1293
|
zd.month,
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.html
CHANGED
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.17.
|
3
|
+
Version: 1.17.1
|
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
|
@@ -162,7 +162,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
162
162
|
* [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
|
163
163
|
* [feature beefybits](#feature-beefybits) - force-enable features with known issues on your OS/env
|
164
164
|
* [packages](#packages) - the party might be closer than you think
|
165
|
-
* [arch package](#arch-package) -
|
165
|
+
* [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
|
166
166
|
* [fedora package](#fedora-package) - does not exist yet
|
167
167
|
* [nix package](#nix-package) - `nix profile install github:9001/copyparty`
|
168
168
|
* [nixos module](#nixos-module)
|
@@ -475,6 +475,9 @@ upgrade notes
|
|
475
475
|
|
476
476
|
"frequently" asked questions
|
477
477
|
|
478
|
+
* CopyParty?
|
479
|
+
* nope! the name is either copyparty (all-lowercase) or Copyparty -- it's [one word](https://en.wiktionary.org/wiki/copyparty) after all :>
|
480
|
+
|
478
481
|
* can I change the 🌲 spinning pine-tree loading animation?
|
479
482
|
* [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
|
480
483
|
|
@@ -973,6 +976,7 @@ semi-intentional limitations:
|
|
973
976
|
|
974
977
|
* cleanup of expired shares only works when global option `e2d` is set, and/or at least one volume on the server has volflag `e2d`
|
975
978
|
* 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
|
979
|
+
* if you change [password hashing](#password-hashing) settings after creating a password-protected share, then that share will stop working
|
976
980
|
* 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
|
977
981
|
* no option to "delete after first access" because tricky
|
978
982
|
* when linking something to discord (for example) it'll get accessed by their scraper and that would count as a hit
|
@@ -2261,10 +2265,14 @@ if your distro/OS is not mentioned below, there might be some hints in the [«on
|
|
2261
2265
|
|
2262
2266
|
## arch package
|
2263
2267
|
|
2264
|
-
|
2268
|
+
`pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
|
2265
2269
|
|
2266
2270
|
it comes with a [systemd service](./contrib/package/arch/copyparty.service) and expects to find one or more [config files](./docs/example.conf) in `/etc/copyparty.d/`
|
2267
2271
|
|
2272
|
+
after installing it, you may want to `cp /usr/lib/systemd/system/copyparty.service /etc/systemd/system/` and then `vim /etc/systemd/system/copyparty.service` to change what user/group it is running as (you only need to do this once)
|
2273
|
+
|
2274
|
+
NOTE: there used to be an aur package; this evaporated when copyparty was adopted by the official archlinux repos. If you're still using the aur package, please move
|
2275
|
+
|
2268
2276
|
|
2269
2277
|
## fedora package
|
2270
2278
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
copyparty/__init__.py,sha256=TnFSStmHlwlRIClWW8jSHxZpt3dl_kN6_pEnqBqh3mE,2638
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
2
|
+
copyparty/__main__.py,sha256=FZs3IMHTFyeR_-Ftx-M-61_gOwmQCM3bD5b9jo6nrbs,120810
|
3
|
+
copyparty/__version__.py,sha256=OCUlop4ig08TTAg6PnNAKigP9eAnhhG-82RUqzvbE-w,253
|
4
|
+
copyparty/authsrv.py,sha256=Z7VH7nO-Yd2X33d8qdMemb6bsAGZqgtaRPFgSAF7rl0,113463
|
5
5
|
copyparty/broker_mp.py,sha256=QdOXXvV2Xn6J0CysEqyY3GZbqxQMyWnTpnba-a5lMc0,4987
|
6
6
|
copyparty/broker_mpw.py,sha256=PpSS4SK3pItlpfD8OwVr3QmJEPKlUgaf2nuMOozixgU,3347
|
7
7
|
copyparty/broker_thr.py,sha256=fjoYtpSscUA7-nMl4r1n2R7UK3J9lrvLS3rUZ-iJzKQ,1721
|
8
8
|
copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
|
9
|
-
copyparty/cert.py,sha256=
|
10
|
-
copyparty/cfg.py,sha256=
|
9
|
+
copyparty/cert.py,sha256=pSSeVYticrDsnsrdRtfpUQN-8WRObsqrYtSRroXmgxo,7992
|
10
|
+
copyparty/cfg.py,sha256=H2p2bHRWk2e4rOx5NZWUVHUcoETy3dtzDkeC8TdosRY,14522
|
11
11
|
copyparty/dxml.py,sha256=vu5uZQtwvwoqnFHbULs2Zh_y2DETu0T-ENpMZ1i2CV4,2505
|
12
12
|
copyparty/fsutil.py,sha256=NC_CJC4TDag399vVDH9_uQfdfpTMwRFLNxERSWhlVvs,4594
|
13
13
|
copyparty/ftpd.py,sha256=G7PApVIFeSzRo4-D-9uRb8NxYgz6nFwTEbrOk1ErYCU,17969
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=9yCKMqUD3J2m1tmvx9VIF_RkGi2LvbBHkUx4xxBzVL4,221960
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
16
|
copyparty/httpsrv.py,sha256=pxH_Eh8ElBLvOEDejgpP9Bvk65HNEou-03aYIcgXhrs,18090
|
17
17
|
copyparty/ico.py,sha256=-7QjF_jIxnPo4Vr0oUPksQ_U_Ef0HRsSPm3s71idOz8,3879
|
@@ -23,16 +23,16 @@ copyparty/pwhash.py,sha256=zHoz9FHGkFBxoRvSfG1XyjN3ibww_h5GE6_m5yS-fws,4246
|
|
23
23
|
copyparty/smbd.py,sha256=dixFl2wlWymq_Cycc8a4cVB4gY8RSg2e3tE7Xr-aDa0,14614
|
24
24
|
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
|
-
copyparty/sutil.py,sha256=
|
26
|
+
copyparty/sutil.py,sha256=E65jAaOzHlJYnqsOKDMPVT8kALPUVexpkSStWBdItkY,3231
|
27
27
|
copyparty/svchub.py,sha256=leERN449te_yBbyChcOenjY_nuNPoFJpncLHfDMvDYE,46618
|
28
28
|
copyparty/szip.py,sha256=9srQzjsTBrBadf6QMh4YRAL70rkZLevAOHqXWK5jvr8,8846
|
29
29
|
copyparty/tcpsrv.py,sha256=F5K4Qr4eBLfhdLT_39yDf6ftrhWuGTrd6DSqqp_6e-Q,20480
|
30
|
-
copyparty/tftpd.py,sha256=
|
30
|
+
copyparty/tftpd.py,sha256=tbnxUsilwyusrAUCVVjJUZnR9TIHDkE-99WLsUxAIGA,14029
|
31
31
|
copyparty/th_cli.py,sha256=IEX5tCb0gw9Z2aRIDL9bwdvJ6g5jhWZ8OEAAz16_xN4,5426
|
32
|
-
copyparty/th_srv.py,sha256=
|
32
|
+
copyparty/th_srv.py,sha256=2omGprnKGWim6U7fjJAXI41UCZBSxRwZfS0rCDsUDps,32556
|
33
33
|
copyparty/u2idx.py,sha256=4Y5OOPyVkc-pS0z6e3p4StXAMnjHobSOMmMsvNUTD34,13674
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=nnR_ZKaopSNBuAjSN3Q5G_UVe6GmYD1NkxJt5QQf02o,178079
|
35
|
+
copyparty/util.py,sha256=Keb-mlTq4rtWjv3utaGsKqwujHYPZLkaBZOeivnGxKc,103485
|
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=r2c_hOZV_RTyl4CqWWX14FDWP8nnDVwGkDl4Sfk0rU4,8239
|
58
|
-
copyparty/web/browser.css.gz,sha256
|
58
|
+
copyparty/web/browser.css.gz,sha256=-w-OUbKy0UdL9vYKAC95ayl4MesDAFjqrq60uAxPj4U,11728
|
59
59
|
copyparty/web/browser.html,sha256=auvhLVE_t0aIN0q-nk0zOWFqITgDhroMAAviBNLoFfc,4788
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=wdxJBHlMCo1Oh9v1YZsj6rucul7qhQts1-iVfmFPjr8,94639
|
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
|
@@ -80,15 +80,15 @@ copyparty/web/shares.js.gz,sha256=emeY2-wjkh8x1JgaW6ny5fcC7XpZzZzfE1f-sEyntQ4,94
|
|
80
80
|
copyparty/web/splash.css.gz,sha256=S8_A7JJl71xACRBYGzafeaD82OacW6Fa7oKPiNyrhAs,1087
|
81
81
|
copyparty/web/splash.html,sha256=ouFB1P9g0K-S3LZQtOLfNz3GLXIRjyETkxo9aJZhiYk,6249
|
82
82
|
copyparty/web/splash.js.gz,sha256=4VqNznN10-bT33IJm3VWzBEJ1s08XZyxFB1TYPUkuAo,2739
|
83
|
-
copyparty/web/svcs.html,sha256=
|
83
|
+
copyparty/web/svcs.html,sha256=cxgrhX9wD0Z_kvidry3aS9ubuGXYDj2f4ehq1X8T1EA,14227
|
84
84
|
copyparty/web/svcs.js.gz,sha256=lMXEP9W-VlXyANlva4q0ASSxvvHYlE2CrmxGgZXZop0,713
|
85
85
|
copyparty/web/ui.css.gz,sha256=iDjrmq32aDN6l2S5AjCQdKjD6bxmzP6ji2WjM1FjKiU,2819
|
86
86
|
copyparty/web/up2k.js.gz,sha256=7AKmoJOtFh9tx3Ha7w2F-z69-XZo_LzyR3ilWnBO_D8,24524
|
87
|
-
copyparty/web/util.js.gz,sha256=
|
87
|
+
copyparty/web/util.js.gz,sha256=Ha2u-RG4HAYOL_QZnt426man0BdhucVNYOQjG5v69AA,15254
|
88
88
|
copyparty/web/w.hash.js.gz,sha256=JhJagnqIkcKng_hs6otEgzcuQE7keToG_r5dd2o3EfU,1108
|
89
89
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
90
|
copyparty/web/a/partyfuse.py,sha256=9p5Hpg_IBiSimv7j9kmPhCGpy-FLXSRUOYnLjJ5JifU,28049
|
91
|
-
copyparty/web/a/u2c.py,sha256=
|
91
|
+
copyparty/web/a/u2c.py,sha256=auXzLj04dt_lw4H70PhNUK0GjrQEThrybo2-77SLsUg,53165
|
92
92
|
copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
|
93
93
|
copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
|
94
94
|
copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
|
@@ -109,9 +109,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
109
109
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
110
110
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
111
111
|
copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
|
112
|
-
copyparty-1.17.
|
113
|
-
copyparty-1.17.
|
114
|
-
copyparty-1.17.
|
115
|
-
copyparty-1.17.
|
116
|
-
copyparty-1.17.
|
117
|
-
copyparty-1.17.
|
112
|
+
copyparty-1.17.1.dist-info/licenses/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
113
|
+
copyparty-1.17.1.dist-info/METADATA,sha256=ApeqUFduyJZInDL2s46CSphR3JT7XsAmrTvoOlLCa20,163313
|
114
|
+
copyparty-1.17.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
115
|
+
copyparty-1.17.1.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
116
|
+
copyparty-1.17.1.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
117
|
+
copyparty-1.17.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|