copyparty 1.18.6__py3-none-any.whl → 1.18.7__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 -3
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +37 -2
- copyparty/bos/bos.py +14 -3
- copyparty/cfg.py +4 -0
- copyparty/ftpd.py +4 -4
- copyparty/httpcli.py +71 -53
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +3 -3
- copyparty/tftpd.py +6 -3
- copyparty/th_srv.py +2 -2
- copyparty/up2k.py +10 -7
- copyparty/util.py +62 -22
- copyparty/web/browser.html +2 -2
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +15 -3
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/METADATA +23 -1
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/RECORD +24 -24
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/WHEEL +0 -0
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/entry_points.txt +0 -0
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.18.6.dist-info → copyparty-1.18.7.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -53,13 +53,13 @@ from .util import (
|
|
53
53
|
PYFTPD_VER,
|
54
54
|
RAM_AVAIL,
|
55
55
|
RAM_TOTAL,
|
56
|
+
RE_ANSI,
|
56
57
|
SQLITE_VER,
|
57
58
|
UNPLICATIONS,
|
58
59
|
URL_BUG,
|
59
60
|
URL_PRJ,
|
60
61
|
Daemon,
|
61
62
|
align_tab,
|
62
|
-
ansi_re,
|
63
63
|
b64enc,
|
64
64
|
dedent,
|
65
65
|
has_resource,
|
@@ -161,7 +161,7 @@ def lprint(*a , **ka ) :
|
|
161
161
|
txt = " ".join(unicode(x) for x in a) + eol
|
162
162
|
printed.append(txt)
|
163
163
|
if not VT100:
|
164
|
-
txt =
|
164
|
+
txt = RE_ANSI.sub("", txt)
|
165
165
|
|
166
166
|
print(txt, end="", **ka)
|
167
167
|
|
@@ -1045,6 +1045,8 @@ def add_upload(ap):
|
|
1045
1045
|
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
|
1046
1046
|
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
|
1047
1047
|
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
1048
|
+
ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
|
1049
|
+
ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
|
1048
1050
|
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
1049
1051
|
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
|
1050
1052
|
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
@@ -1547,7 +1549,7 @@ def add_ui(ap, retry):
|
|
1547
1549
|
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
|
1548
1550
|
ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
|
1549
1551
|
ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
|
1550
|
-
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1552
|
+
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1551
1553
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
1552
1554
|
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
1553
1555
|
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -33,6 +33,7 @@ from .util import (
|
|
33
33
|
afsenc,
|
34
34
|
get_df,
|
35
35
|
humansize,
|
36
|
+
json_hesc,
|
36
37
|
min_ex,
|
37
38
|
odfusion,
|
38
39
|
read_utf8,
|
@@ -63,6 +64,25 @@ if PY2:
|
|
63
64
|
|
64
65
|
|
65
66
|
LEELOO_DALLAS = "leeloo_dallas"
|
67
|
+
##
|
68
|
+
## you might be curious what Leeloo Dallas is doing here, so let me explain:
|
69
|
+
##
|
70
|
+
## certain daemonic tasks, namely:
|
71
|
+
## * deletion of expired files, running on a timer
|
72
|
+
## * deletion of sidecar files, initiated by plugins
|
73
|
+
## need to skip the usual permission-checks to do their thing,
|
74
|
+
## so we let Leeloo handle these
|
75
|
+
##
|
76
|
+
## and also, the smb-server has really shitty support for user-accounts
|
77
|
+
## so one popular way to avoid issues is by running copyparty without users;
|
78
|
+
## this makes all smb-clients identify as LD to gain unrestricted access
|
79
|
+
##
|
80
|
+
## Leeloo, being a fictional character from The Fifth Element,
|
81
|
+
## obviously does not exist and will never be able to access any copyparty
|
82
|
+
## instances from the outside (the username is rejected at every entrypoint)
|
83
|
+
##
|
84
|
+
## thanks for coming to my ted talk
|
85
|
+
|
66
86
|
|
67
87
|
SEE_LOG = "see log for details"
|
68
88
|
SEESLOG = " (see serverlog for details)"
|
@@ -114,6 +134,8 @@ class Lim(object):
|
|
114
134
|
self.reg = None # up2k registry
|
115
135
|
|
116
136
|
self.chmod_d = 0o755
|
137
|
+
self.uid = self.gid = -1
|
138
|
+
self.chown = False
|
117
139
|
|
118
140
|
self.nups = {} # num tracker
|
119
141
|
self.bups = {} # byte tracker list
|
@@ -276,6 +298,8 @@ class Lim(object):
|
|
276
298
|
# no branches yet; make one
|
277
299
|
sub = os.path.join(path, "0")
|
278
300
|
bos.mkdir(sub, self.chmod_d)
|
301
|
+
if self.chown:
|
302
|
+
os.chown(sub, self.uid, self.gid)
|
279
303
|
else:
|
280
304
|
# try newest branch only
|
281
305
|
sub = os.path.join(path, str(dirs[-1]))
|
@@ -291,6 +315,8 @@ class Lim(object):
|
|
291
315
|
# make a branch
|
292
316
|
sub = os.path.join(path, str(dirs[-1] + 1))
|
293
317
|
bos.mkdir(sub, self.chmod_d)
|
318
|
+
if self.chown:
|
319
|
+
os.chown(sub, self.uid, self.gid)
|
294
320
|
ret = self.dive(sub, lvs - 1)
|
295
321
|
if ret is None:
|
296
322
|
raise Pebkac(500, "rotation bug")
|
@@ -2153,7 +2179,7 @@ class AuthSrv(object):
|
|
2153
2179
|
if vf not in vol.flags:
|
2154
2180
|
vol.flags[vf] = getattr(self.args, ga)
|
2155
2181
|
|
2156
|
-
zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who"
|
2182
|
+
zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
|
2157
2183
|
for k in zs.split():
|
2158
2184
|
if k in vol.flags:
|
2159
2185
|
vol.flags[k] = int(vol.flags[k])
|
@@ -2190,8 +2216,17 @@ class AuthSrv(object):
|
|
2190
2216
|
if (is_d and zi != 0o755) or not is_d:
|
2191
2217
|
free_umask = True
|
2192
2218
|
|
2219
|
+
vol.flags.pop("chown", None)
|
2220
|
+
if vol.flags["uid"] != -1 or vol.flags["gid"] != -1:
|
2221
|
+
vol.flags["chown"] = True
|
2222
|
+
vol.flags.pop("fperms", None)
|
2223
|
+
if "chown" in vol.flags or vol.flags.get("chmod_f"):
|
2224
|
+
vol.flags["fperms"] = True
|
2193
2225
|
if vol.lim:
|
2194
2226
|
vol.lim.chmod_d = vol.flags["chmod_d"]
|
2227
|
+
vol.lim.chown = "chown" in vol.flags
|
2228
|
+
vol.lim.uid = vol.flags["uid"]
|
2229
|
+
vol.lim.gid = vol.flags["gid"]
|
2195
2230
|
|
2196
2231
|
if vol.flags.get("og"):
|
2197
2232
|
self.args.uqe = True
|
@@ -2742,7 +2777,7 @@ class AuthSrv(object):
|
|
2742
2777
|
"lifetime": vn.js_ls["lifetime"],
|
2743
2778
|
"u2sort": self.args.u2sort,
|
2744
2779
|
}
|
2745
|
-
vn.js_htm = json.dumps(js_htm)
|
2780
|
+
vn.js_htm = json_hesc(json.dumps(js_htm))
|
2746
2781
|
|
2747
2782
|
vols = list(vfs.all_nodes.values())
|
2748
2783
|
if enshare:
|
copyparty/bos/bos.py
CHANGED
@@ -6,8 +6,11 @@ import os
|
|
6
6
|
from ..util import SYMTIME, fsdec, fsenc
|
7
7
|
from . import path as path
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
MKD_755 = {"chmod_d": 0o755}
|
10
|
+
MKD_700 = {"chmod_d": 0o700}
|
11
|
+
|
12
|
+
_ = (path, MKD_755, MKD_700)
|
13
|
+
__all__ = ["path", "MKD_755", "MKD_700"]
|
11
14
|
|
12
15
|
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
|
13
16
|
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
|
@@ -17,11 +20,15 @@ def chmod(p , mode ) :
|
|
17
20
|
return os.chmod(fsenc(p), mode)
|
18
21
|
|
19
22
|
|
23
|
+
def chown(p , uid , gid ) :
|
24
|
+
return os.chown(fsenc(p), uid, gid)
|
25
|
+
|
26
|
+
|
20
27
|
def listdir(p = ".") :
|
21
28
|
return [fsdec(x) for x in os.listdir(fsenc(p))]
|
22
29
|
|
23
30
|
|
24
|
-
def makedirs(name ,
|
31
|
+
def makedirs(name , vf = MKD_755, exist_ok = True) :
|
25
32
|
# os.makedirs does 777 for all but leaf; this does mode on all
|
26
33
|
todo = []
|
27
34
|
bname = fsenc(name)
|
@@ -34,9 +41,13 @@ def makedirs(name , mode = 0o755, exist_ok = True) :
|
|
34
41
|
if not exist_ok:
|
35
42
|
os.mkdir(bname) # to throw
|
36
43
|
return False
|
44
|
+
mode = vf["chmod_d"]
|
45
|
+
chown = "chown" in vf
|
37
46
|
for zb in todo[::-1]:
|
38
47
|
try:
|
39
48
|
os.mkdir(zb, mode)
|
49
|
+
if chown:
|
50
|
+
os.chown(zb, vf["uid"], vf["gid"])
|
40
51
|
except:
|
41
52
|
if os.path.isdir(zb):
|
42
53
|
continue
|
copyparty/cfg.py
CHANGED
@@ -114,6 +114,8 @@ def vf_vmap() :
|
|
114
114
|
"unlist",
|
115
115
|
"u2abort",
|
116
116
|
"u2ts",
|
117
|
+
"uid",
|
118
|
+
"gid",
|
117
119
|
"ups_who",
|
118
120
|
"zip_who",
|
119
121
|
"zipmaxn",
|
@@ -175,6 +177,8 @@ flagcats = {
|
|
175
177
|
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
176
178
|
"chmod_d=755": "unix-permission for new dirs/folders",
|
177
179
|
"chmod_f=644": "unix-permission for new files",
|
180
|
+
"uid=573": "change owner of new files/folders to unix-user 573",
|
181
|
+
"gid=999": "change owner of new files/folders to unix-group 999",
|
178
182
|
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
179
183
|
"nosparse": "deny use of sparse files, mainly for slow storage",
|
180
184
|
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
copyparty/ftpd.py
CHANGED
@@ -31,6 +31,7 @@ from .util import (
|
|
31
31
|
relchk,
|
32
32
|
runhook,
|
33
33
|
sanitize_fn,
|
34
|
+
set_fperms,
|
34
35
|
vjoin,
|
35
36
|
wunlink,
|
36
37
|
)
|
@@ -258,8 +259,8 @@ class FtpFs(AbstractedFS):
|
|
258
259
|
wunlink(self.log, ap, VF_CAREFUL)
|
259
260
|
|
260
261
|
ret = open(fsenc(ap), mode, self.args.iobuf)
|
261
|
-
if w and "
|
262
|
-
|
262
|
+
if w and "fperms" in vfs.flags:
|
263
|
+
set_fperms(ret, vfs.flags)
|
263
264
|
|
264
265
|
return ret
|
265
266
|
|
@@ -293,8 +294,7 @@ class FtpFs(AbstractedFS):
|
|
293
294
|
|
294
295
|
def mkdir(self, path ) :
|
295
296
|
ap, vfs, _ = self.rv2a(path, w=True)
|
296
|
-
|
297
|
-
bos.makedirs(ap, chmod) # filezilla expects this
|
297
|
+
bos.makedirs(ap, vf=vfs.flags) # filezilla expects this
|
298
298
|
|
299
299
|
def listdir(self, path ) :
|
300
300
|
vpath = join(self.cwd, path)
|
copyparty/httpcli.py
CHANGED
@@ -33,7 +33,7 @@ except:
|
|
33
33
|
|
34
34
|
from .__init__ import ANYWIN, PY2, RES, TYPE_CHECKING, EnvParams, unicode
|
35
35
|
from .__version__ import S_VERSION
|
36
|
-
from .authsrv import VFS # typechk
|
36
|
+
from .authsrv import LEELOO_DALLAS, VFS # typechk
|
37
37
|
from .bos import bos
|
38
38
|
from .star import StreamTar
|
39
39
|
from .stolen.qrcodegen import QrCode, qr2svg
|
@@ -79,8 +79,10 @@ from .util import (
|
|
79
79
|
hidedir,
|
80
80
|
html_bescape,
|
81
81
|
html_escape,
|
82
|
+
html_sh_esc,
|
82
83
|
humansize,
|
83
84
|
ipnorm,
|
85
|
+
json_hesc,
|
84
86
|
justcopy,
|
85
87
|
load_resource,
|
86
88
|
loadpy,
|
@@ -103,6 +105,7 @@ from .util import (
|
|
103
105
|
sanitize_vpath,
|
104
106
|
sendfile_kern,
|
105
107
|
sendfile_py,
|
108
|
+
set_fperms,
|
106
109
|
stat_resource,
|
107
110
|
ub64dec,
|
108
111
|
ub64enc,
|
@@ -617,6 +620,9 @@ class HttpCli(object):
|
|
617
620
|
) or self.args.idp_h_key in self.headers
|
618
621
|
|
619
622
|
if trusted_key and trusted_xff:
|
623
|
+
if idp_usr.lower() == LEELOO_DALLAS:
|
624
|
+
self.loud_reply("send her back", status=403)
|
625
|
+
return False
|
620
626
|
self.asrv.idp_checkin(self.conn.hsrv.broker, idp_usr, idp_grp)
|
621
627
|
else:
|
622
628
|
if not trusted_key:
|
@@ -1105,15 +1111,18 @@ class HttpCli(object):
|
|
1105
1111
|
else:
|
1106
1112
|
return True
|
1107
1113
|
|
1114
|
+
host = self.host.lower()
|
1115
|
+
if host.startswith("["):
|
1116
|
+
if "]:" in host:
|
1117
|
+
host = host.split("]:")[0] + "]"
|
1118
|
+
else:
|
1119
|
+
host = host.split(":")[0]
|
1120
|
+
|
1108
1121
|
oh = self.out_headers
|
1109
1122
|
origin = origin.lower()
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
"https" if self.is_https else "http",
|
1114
|
-
self.host.lower().split(":")[0],
|
1115
|
-
)
|
1116
|
-
]
|
1123
|
+
proto = "https" if self.is_https else "http"
|
1124
|
+
good_origins = self.args.acao + ["%s://%s" % (proto, host)]
|
1125
|
+
|
1117
1126
|
if "pw" in ih or re.sub(r"(:[0-9]{1,5})?/?$", "", origin) in good_origins:
|
1118
1127
|
good_origin = True
|
1119
1128
|
bad_hdrs = ("",)
|
@@ -1570,6 +1579,18 @@ class HttpCli(object):
|
|
1570
1579
|
self.log("inaccessible: %r" % ("/" + self.vpath,))
|
1571
1580
|
raise Pebkac(401, "authenticate")
|
1572
1581
|
|
1582
|
+
if "quota-available-bytes" in props and not self.args.nid:
|
1583
|
+
bfree, btot, _ = get_df(vn.realpath, False)
|
1584
|
+
if btot:
|
1585
|
+
df = {
|
1586
|
+
"quota-available-bytes": str(bfree),
|
1587
|
+
"quota-used-bytes": str(btot - bfree),
|
1588
|
+
}
|
1589
|
+
else:
|
1590
|
+
df = {}
|
1591
|
+
else:
|
1592
|
+
df = {}
|
1593
|
+
|
1573
1594
|
fgen = itertools.chain([topdir], fgen)
|
1574
1595
|
vtop = vjoin(self.args.R, vjoin(vn.vpath, rem))
|
1575
1596
|
|
@@ -1612,6 +1633,9 @@ class HttpCli(object):
|
|
1612
1633
|
ap = os.path.join(tap, x["vp"])
|
1613
1634
|
pvs["getcontenttype"] = html_escape(guess_mime(rp, ap))
|
1614
1635
|
pvs["getcontentlength"] = str(st.st_size)
|
1636
|
+
elif df:
|
1637
|
+
pvs.update(df)
|
1638
|
+
df = {}
|
1615
1639
|
|
1616
1640
|
for k, v in pvs.items():
|
1617
1641
|
if k not in props:
|
@@ -2060,7 +2084,7 @@ class HttpCli(object):
|
|
2060
2084
|
fdir, fn = os.path.split(fdir)
|
2061
2085
|
rem, _ = vsplit(rem)
|
2062
2086
|
|
2063
|
-
bos.makedirs(fdir, vfs.flags
|
2087
|
+
bos.makedirs(fdir, vf=vfs.flags)
|
2064
2088
|
|
2065
2089
|
open_ka = {"fun": open}
|
2066
2090
|
open_a = ["wb", self.args.iobuf]
|
@@ -2117,9 +2141,7 @@ class HttpCli(object):
|
|
2117
2141
|
if nameless:
|
2118
2142
|
fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip())
|
2119
2143
|
|
2120
|
-
params = {"suffix": suffix, "fdir": fdir}
|
2121
|
-
if "chmod_f" in vfs.flags:
|
2122
|
-
params["chmod"] = vfs.flags["chmod_f"]
|
2144
|
+
params = {"suffix": suffix, "fdir": fdir, "vf": vfs.flags}
|
2123
2145
|
if self.args.nw:
|
2124
2146
|
params = {}
|
2125
2147
|
fn = os.devnull
|
@@ -2167,7 +2189,7 @@ class HttpCli(object):
|
|
2167
2189
|
if self.args.nw:
|
2168
2190
|
fn = os.devnull
|
2169
2191
|
else:
|
2170
|
-
bos.makedirs(fdir, vfs.flags
|
2192
|
+
bos.makedirs(fdir, vf=vfs.flags)
|
2171
2193
|
path = os.path.join(fdir, fn)
|
2172
2194
|
if not nameless:
|
2173
2195
|
self.vpath = vjoin(self.vpath, fn)
|
@@ -2299,7 +2321,7 @@ class HttpCli(object):
|
|
2299
2321
|
if self.args.hook_v:
|
2300
2322
|
log_reloc(self.log, hr["reloc"], x, path, vp, fn, vfs, rem)
|
2301
2323
|
fdir, self.vpath, fn, (vfs, rem) = x
|
2302
|
-
bos.makedirs(fdir, vfs.flags
|
2324
|
+
bos.makedirs(fdir, vf=vfs.flags)
|
2303
2325
|
path2 = os.path.join(fdir, fn)
|
2304
2326
|
atomic_move(self.log, path, path2, vfs.flags)
|
2305
2327
|
path = path2
|
@@ -2584,7 +2606,7 @@ class HttpCli(object):
|
|
2584
2606
|
dst = vfs.canonical(rem)
|
2585
2607
|
try:
|
2586
2608
|
if not bos.path.isdir(dst):
|
2587
|
-
bos.makedirs(dst, vfs.flags
|
2609
|
+
bos.makedirs(dst, vf=vfs.flags)
|
2588
2610
|
except OSError as ex:
|
2589
2611
|
self.log("makedirs failed %r" % (dst,))
|
2590
2612
|
if not bos.path.isdir(dst):
|
@@ -3027,7 +3049,7 @@ class HttpCli(object):
|
|
3027
3049
|
raise Pebkac(405, 'folder "/%s" already exists' % (vpath,))
|
3028
3050
|
|
3029
3051
|
try:
|
3030
|
-
bos.makedirs(fn, vfs.flags
|
3052
|
+
bos.makedirs(fn, vf=vfs.flags)
|
3031
3053
|
except OSError as ex:
|
3032
3054
|
if ex.errno == errno.EACCES:
|
3033
3055
|
raise Pebkac(500, "the server OS denied write-access")
|
@@ -3068,8 +3090,8 @@ class HttpCli(object):
|
|
3068
3090
|
|
3069
3091
|
with open(fsenc(fn), "wb") as f:
|
3070
3092
|
f.write(b"`GRUNNUR`\n")
|
3071
|
-
if "
|
3072
|
-
|
3093
|
+
if "fperms" in vfs.flags:
|
3094
|
+
set_fperms(f, vfs.flags)
|
3073
3095
|
|
3074
3096
|
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
3075
3097
|
self.redirect(vpath, "?edit")
|
@@ -3143,7 +3165,7 @@ class HttpCli(object):
|
|
3143
3165
|
)
|
3144
3166
|
upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/")
|
3145
3167
|
if not nullwrite:
|
3146
|
-
bos.makedirs(fdir_base, vfs.flags
|
3168
|
+
bos.makedirs(fdir_base, vf=vfs.flags)
|
3147
3169
|
|
3148
3170
|
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
3149
3171
|
zs = self.uparam.get("want") or self.headers.get("accept") or ""
|
@@ -3176,7 +3198,7 @@ class HttpCli(object):
|
|
3176
3198
|
if rnd:
|
3177
3199
|
fname = rand_name(fdir, fname, rnd)
|
3178
3200
|
|
3179
|
-
open_args = {"fdir": fdir, "suffix": suffix}
|
3201
|
+
open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
|
3180
3202
|
|
3181
3203
|
if "replace" in self.uparam:
|
3182
3204
|
if not self.can_delete:
|
@@ -3238,11 +3260,8 @@ class HttpCli(object):
|
|
3238
3260
|
else:
|
3239
3261
|
open_args["fdir"] = fdir
|
3240
3262
|
|
3241
|
-
if "chmod_f" in vfs.flags:
|
3242
|
-
open_args["chmod"] = vfs.flags["chmod_f"]
|
3243
|
-
|
3244
3263
|
if p_file and not nullwrite:
|
3245
|
-
bos.makedirs(fdir, vfs.flags
|
3264
|
+
bos.makedirs(fdir, vf=vfs.flags)
|
3246
3265
|
|
3247
3266
|
# reserve destination filename
|
3248
3267
|
f, fname = ren_open(fname, "wb", fdir=fdir, suffix=suffix)
|
@@ -3346,7 +3365,7 @@ class HttpCli(object):
|
|
3346
3365
|
if nullwrite:
|
3347
3366
|
fdir = ap2 = ""
|
3348
3367
|
else:
|
3349
|
-
bos.makedirs(fdir, vfs.flags
|
3368
|
+
bos.makedirs(fdir, vf=vfs.flags)
|
3350
3369
|
atomic_move(self.log, abspath, ap2, vfs.flags)
|
3351
3370
|
abspath = ap2
|
3352
3371
|
sz = bos.path.getsize(abspath)
|
@@ -3467,8 +3486,8 @@ class HttpCli(object):
|
|
3467
3486
|
ft = "{}:{}".format(self.ip, self.addr[1])
|
3468
3487
|
ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
|
3469
3488
|
f.write(ft.encode("utf-8"))
|
3470
|
-
if "
|
3471
|
-
|
3489
|
+
if "fperms" in vfs.flags:
|
3490
|
+
set_fperms(f, vfs.flags)
|
3472
3491
|
except Exception as ex:
|
3473
3492
|
suf = "\nfailed to write the upload report: {}".format(ex)
|
3474
3493
|
|
@@ -3518,7 +3537,7 @@ class HttpCli(object):
|
|
3518
3537
|
lim = vfs.get_dbv(rem)[0].lim
|
3519
3538
|
if lim:
|
3520
3539
|
fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker)
|
3521
|
-
bos.makedirs(fp, vfs.flags
|
3540
|
+
bos.makedirs(fp, vf=vfs.flags)
|
3522
3541
|
|
3523
3542
|
fp = os.path.join(fp, fn)
|
3524
3543
|
rem = "{}/{}".format(rp, fn).strip("/")
|
@@ -3586,15 +3605,17 @@ class HttpCli(object):
|
|
3586
3605
|
zs = ub64enc(zb).decode("ascii")[:24].lower()
|
3587
3606
|
dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs)
|
3588
3607
|
self.log("moving old version to %s/%s" % (dp, mfile2))
|
3589
|
-
if bos.makedirs(dp, vfs.flags
|
3608
|
+
if bos.makedirs(dp, vf=vfs.flags):
|
3590
3609
|
with open(os.path.join(dp, "dir.txt"), "wb") as f:
|
3591
3610
|
f.write(afsenc(vrd))
|
3592
|
-
if "
|
3593
|
-
|
3611
|
+
if "fperms" in vfs.flags:
|
3612
|
+
set_fperms(f, vfs.flags)
|
3594
3613
|
elif hist_cfg == "s":
|
3595
3614
|
dp = os.path.join(mdir, ".hist")
|
3596
3615
|
try:
|
3597
3616
|
bos.mkdir(dp, vfs.flags["chmod_d"])
|
3617
|
+
if "chown" in vfs.flags:
|
3618
|
+
bos.chown(dp, vfs.flags["uid"], vfs.flags["gid"])
|
3598
3619
|
hidedir(dp)
|
3599
3620
|
except:
|
3600
3621
|
pass
|
@@ -3632,8 +3653,8 @@ class HttpCli(object):
|
|
3632
3653
|
wunlink(self.log, fp, vfs.flags)
|
3633
3654
|
|
3634
3655
|
with open(fsenc(fp), "wb", self.args.iobuf) as f:
|
3635
|
-
if "
|
3636
|
-
|
3656
|
+
if "fperms" in vfs.flags:
|
3657
|
+
set_fperms(f, vfs.flags)
|
3637
3658
|
sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
|
3638
3659
|
|
3639
3660
|
if lim:
|
@@ -4870,11 +4891,8 @@ class HttpCli(object):
|
|
4870
4891
|
else:
|
4871
4892
|
rip = host
|
4872
4893
|
|
4873
|
-
|
4874
|
-
pw =
|
4875
|
-
vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
|
4876
|
-
pw = pw.replace(" ", "%20")
|
4877
|
-
vp = vp.replace(" ", "%20")
|
4894
|
+
vp = (self.uparam["hc"] or "").lstrip("/")
|
4895
|
+
pw = self.pw or "hunter2"
|
4878
4896
|
if pw in self.asrv.sesa:
|
4879
4897
|
pw = "hunter2"
|
4880
4898
|
|
@@ -4883,14 +4901,14 @@ class HttpCli(object):
|
|
4883
4901
|
args=self.args,
|
4884
4902
|
accs=bool(self.asrv.acct),
|
4885
4903
|
s="s" if self.is_https else "",
|
4886
|
-
rip=rip,
|
4887
|
-
ep=ep,
|
4888
|
-
vp=vp,
|
4889
|
-
rvp=vjoin(self.args.R, vp),
|
4890
|
-
host=host,
|
4891
|
-
hport=hport,
|
4904
|
+
rip=html_sh_esc(rip),
|
4905
|
+
ep=html_sh_esc(ep),
|
4906
|
+
vp=html_sh_esc(vp),
|
4907
|
+
rvp=html_sh_esc(vjoin(self.args.R, vp)),
|
4908
|
+
host=html_sh_esc(host),
|
4909
|
+
hport=html_sh_esc(hport),
|
4892
4910
|
aname=aname,
|
4893
|
-
pw=pw,
|
4911
|
+
pw=html_sh_esc(pw),
|
4894
4912
|
)
|
4895
4913
|
self.reply(html.encode("utf-8"))
|
4896
4914
|
return True
|
@@ -5552,7 +5570,7 @@ class HttpCli(object):
|
|
5552
5570
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
5553
5571
|
return True
|
5554
5572
|
|
5555
|
-
html = self.j2s("rups", this=self, v=jtxt)
|
5573
|
+
html = self.j2s("rups", this=self, v=json_hesc(jtxt))
|
5556
5574
|
self.reply(html.encode("utf-8"), status=200)
|
5557
5575
|
return True
|
5558
5576
|
|
@@ -5616,15 +5634,15 @@ class HttpCli(object):
|
|
5616
5634
|
raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
|
5617
5635
|
raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
|
5618
5636
|
|
5637
|
+
skey = self.uparam.get("skey") or self.vpath.split("/")[-1]
|
5638
|
+
|
5619
5639
|
if self.args.shr_v:
|
5620
|
-
self.log("handle_eshare: " +
|
5640
|
+
self.log("handle_eshare: " + skey)
|
5621
5641
|
|
5622
5642
|
cur = idx.get_shr()
|
5623
5643
|
if not cur:
|
5624
5644
|
raise Pebkac(400, "huh, sharing must be disabled in the server config...")
|
5625
5645
|
|
5626
|
-
skey = self.vpath.split("/")[-1]
|
5627
|
-
|
5628
5646
|
rows = cur.execute("select un, t1 from sh where k = ?", (skey,)).fetchall()
|
5629
5647
|
un = rows[0][0] if rows and rows[0] else ""
|
5630
5648
|
|
@@ -6133,13 +6151,13 @@ class HttpCli(object):
|
|
6133
6151
|
self.log("#wow #whoa")
|
6134
6152
|
|
6135
6153
|
if not self.args.nid:
|
6136
|
-
free, total,
|
6137
|
-
if total
|
6154
|
+
free, total, zs = get_df(abspath, False)
|
6155
|
+
if total:
|
6138
6156
|
h1 = humansize(free or 0)
|
6139
6157
|
h2 = humansize(total)
|
6140
6158
|
srv_info.append("{} free of {}".format(h1, h2))
|
6141
|
-
elif
|
6142
|
-
|
6159
|
+
elif zs:
|
6160
|
+
self.log("diskfree(%r): %s" % (abspath, zs), 3)
|
6143
6161
|
|
6144
6162
|
srv_infot = "</span> // <span>".join(srv_info)
|
6145
6163
|
|
copyparty/smbd.py
CHANGED
copyparty/svchub.py
CHANGED
@@ -45,6 +45,7 @@ from .util import (
|
|
45
45
|
HAVE_PSUTIL,
|
46
46
|
HAVE_SQLITE3,
|
47
47
|
HAVE_ZMQ,
|
48
|
+
RE_ANSI,
|
48
49
|
URL_BUG,
|
49
50
|
UTC,
|
50
51
|
VERSIONS,
|
@@ -54,7 +55,6 @@ from .util import (
|
|
54
55
|
HMaccas,
|
55
56
|
ODict,
|
56
57
|
alltrace,
|
57
|
-
ansi_re,
|
58
58
|
build_netmap,
|
59
59
|
expat_ver,
|
60
60
|
gzip,
|
@@ -1399,9 +1399,9 @@ class SvcHub(object):
|
|
1399
1399
|
if self.no_ansi:
|
1400
1400
|
fmt = "%s %-21s %s\n"
|
1401
1401
|
if "\033" in msg:
|
1402
|
-
msg =
|
1402
|
+
msg = RE_ANSI.sub("", msg)
|
1403
1403
|
if "\033" in src:
|
1404
|
-
src =
|
1404
|
+
src = RE_ANSI.sub("", src)
|
1405
1405
|
elif c:
|
1406
1406
|
if isinstance(c, int):
|
1407
1407
|
msg = "\033[3%sm%s\033[0m" % (c, msg)
|
copyparty/tftpd.py
CHANGED
@@ -45,6 +45,7 @@ from .util import (
|
|
45
45
|
exclude_dotfiles,
|
46
46
|
min_ex,
|
47
47
|
runhook,
|
48
|
+
set_fperms,
|
48
49
|
undot,
|
49
50
|
vjoin,
|
50
51
|
vsplit,
|
@@ -385,8 +386,8 @@ class Tftpd(object):
|
|
385
386
|
a = (self.args.iobuf,)
|
386
387
|
|
387
388
|
ret = open(ap, mode, *a, **ka)
|
388
|
-
if wr and "
|
389
|
-
|
389
|
+
if wr and "fperms" in vfs.flags:
|
390
|
+
set_fperms(ret, vfs.flags)
|
390
391
|
|
391
392
|
return ret
|
392
393
|
|
@@ -395,7 +396,9 @@ class Tftpd(object):
|
|
395
396
|
if "*" not in vfs.axs.uwrite:
|
396
397
|
yeet("blocked mkdir; folder not world-writable: /%s" % (vpath,))
|
397
398
|
|
398
|
-
|
399
|
+
bos.mkdir(ap, vfs.flags["chmod_d"])
|
400
|
+
if "chown" in vfs.flags:
|
401
|
+
bos.chown(ap, vfs.flags["uid"], vfs.flags["gid"])
|
399
402
|
|
400
403
|
def _unlink(self, vpath ) :
|
401
404
|
# return bos.unlink(self._v2a("stat", vpath, *a)[1])
|
copyparty/th_srv.py
CHANGED
@@ -266,8 +266,8 @@ class ThumbSrv(object):
|
|
266
266
|
self.log("joined waiting room for %r" % (tpath,))
|
267
267
|
except:
|
268
268
|
thdir = os.path.dirname(tpath)
|
269
|
-
chmod =
|
270
|
-
bos.makedirs(os.path.join(thdir, "w"), chmod)
|
269
|
+
chmod = bos.MKD_700 if self.args.free_umask else bos.MKD_755
|
270
|
+
bos.makedirs(os.path.join(thdir, "w"), vf=chmod)
|
271
271
|
|
272
272
|
inf_path = os.path.join(thdir, "dir.txt")
|
273
273
|
if not bos.path.exists(inf_path):
|
copyparty/up2k.py
CHANGED
@@ -911,7 +911,7 @@ class Up2k(object):
|
|
911
911
|
for vol in vols:
|
912
912
|
try:
|
913
913
|
# mkdir gonna happen at snap anyways;
|
914
|
-
bos.makedirs(vol.realpath, vol.flags
|
914
|
+
bos.makedirs(vol.realpath, vf=vol.flags)
|
915
915
|
dir_is_empty(self.log_func, not self.args.no_scandir, vol.realpath)
|
916
916
|
except Exception as ex:
|
917
917
|
self.volstate[vol.vpath] = "OFFLINE (cannot access folder)"
|
@@ -3293,7 +3293,7 @@ class Up2k(object):
|
|
3293
3293
|
reg,
|
3294
3294
|
"up2k._get_volsize",
|
3295
3295
|
)
|
3296
|
-
bos.makedirs(ap2, vfs.flags
|
3296
|
+
bos.makedirs(ap2, vf=vfs.flags)
|
3297
3297
|
vfs.lim.nup(cj["addr"])
|
3298
3298
|
vfs.lim.bup(cj["addr"], cj["size"])
|
3299
3299
|
|
@@ -3429,7 +3429,7 @@ class Up2k(object):
|
|
3429
3429
|
"wb",
|
3430
3430
|
fdir=fdir,
|
3431
3431
|
suffix="-%.6f-%s" % (ts, dip),
|
3432
|
-
|
3432
|
+
vf=vf,
|
3433
3433
|
)
|
3434
3434
|
f.close()
|
3435
3435
|
return ret
|
@@ -4283,7 +4283,7 @@ class Up2k(object):
|
|
4283
4283
|
self.log(t, 1)
|
4284
4284
|
raise Pebkac(405, t)
|
4285
4285
|
|
4286
|
-
bos.makedirs(os.path.dirname(dabs), dvn.flags
|
4286
|
+
bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
|
4287
4287
|
|
4288
4288
|
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
|
4289
4289
|
svn_dbv.realpath, srem_dbv
|
@@ -4458,7 +4458,10 @@ class Up2k(object):
|
|
4458
4458
|
vp = vjoin(dvp, rem)
|
4459
4459
|
try:
|
4460
4460
|
dvn, drem = self.vfs.get(vp, uname, False, True)
|
4461
|
-
|
4461
|
+
dap = dvn.canonical(drem)
|
4462
|
+
bos.mkdir(dap, dvn.flags["chmod_d"])
|
4463
|
+
if "chown" in dvn.flags:
|
4464
|
+
bos.chown(dap, dvn.flags["uid"], dvn.flags["gid"])
|
4462
4465
|
except:
|
4463
4466
|
pass
|
4464
4467
|
|
@@ -4528,7 +4531,7 @@ class Up2k(object):
|
|
4528
4531
|
|
4529
4532
|
is_xvol = svn.realpath != dvn.realpath
|
4530
4533
|
|
4531
|
-
bos.makedirs(os.path.dirname(dabs), dvn.flags
|
4534
|
+
bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
|
4532
4535
|
|
4533
4536
|
if is_dirlink:
|
4534
4537
|
dlabs = absreal(sabs)
|
@@ -5038,7 +5041,7 @@ class Up2k(object):
|
|
5038
5041
|
"wb",
|
5039
5042
|
fdir=pdir,
|
5040
5043
|
suffix="-%.6f-%s" % (job["t0"], dip),
|
5041
|
-
|
5044
|
+
vf=vf,
|
5042
5045
|
)
|
5043
5046
|
try:
|
5044
5047
|
abspath = djoin(pdir, job["tnam"])
|
copyparty/util.py
CHANGED
@@ -155,7 +155,9 @@ except:
|
|
155
155
|
HAVE_PSUTIL = False
|
156
156
|
|
157
157
|
try:
|
158
|
-
if os.environ.get("PRTY_NO_MAGIC")
|
158
|
+
if os.environ.get("PRTY_NO_MAGIC") or (
|
159
|
+
ANYWIN and not os.environ.get("PRTY_FORCE_MAGIC")
|
160
|
+
):
|
159
161
|
raise Exception()
|
160
162
|
|
161
163
|
import magic
|
@@ -220,7 +222,18 @@ except:
|
|
220
222
|
BITNESS = struct.calcsize("P") * 8
|
221
223
|
|
222
224
|
|
223
|
-
|
225
|
+
RE_ANSI = re.compile("\033\\[[^mK]*[mK]")
|
226
|
+
RE_HTML_SH = re.compile(r"[<>&$?`\"';]")
|
227
|
+
RE_CTYPE = re.compile(r"^content-type: *([^; ]+)", re.IGNORECASE)
|
228
|
+
RE_CDISP = re.compile(r"^content-disposition: *([^; ]+)", re.IGNORECASE)
|
229
|
+
RE_CDISP_FIELD = re.compile(
|
230
|
+
r'^content-disposition:(?: *|.*; *)name="([^"]+)"', re.IGNORECASE
|
231
|
+
)
|
232
|
+
RE_CDISP_FILE = re.compile(
|
233
|
+
r'^content-disposition:(?: *|.*; *)filename="(.*)"', re.IGNORECASE
|
234
|
+
)
|
235
|
+
RE_MEMTOTAL = re.compile("^MemTotal:.* kB")
|
236
|
+
RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
|
224
237
|
|
225
238
|
|
226
239
|
BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
|
@@ -465,11 +478,11 @@ def read_ram() :
|
|
465
478
|
with open("/proc/meminfo", "rb", 0x10000) as f:
|
466
479
|
zsl = f.read(0x10000).decode("ascii", "replace").split("\n")
|
467
480
|
|
468
|
-
p =
|
481
|
+
p = RE_MEMTOTAL
|
469
482
|
zs = next((x for x in zsl if p.match(x)))
|
470
483
|
a = int((int(zs.split()[1]) / 0x100000) * 100) / 100
|
471
484
|
|
472
|
-
p =
|
485
|
+
p = RE_MEMAVAIL
|
473
486
|
zs = next((x for x in zsl if p.match(x)))
|
474
487
|
b = int((int(zs.split()[1]) / 0x100000) * 100) / 100
|
475
488
|
except:
|
@@ -1503,7 +1516,8 @@ def ren_open(fname , *args , **kwargs ) :
|
|
1503
1516
|
fun = kwargs.pop("fun", open)
|
1504
1517
|
fdir = kwargs.pop("fdir", None)
|
1505
1518
|
suffix = kwargs.pop("suffix", None)
|
1506
|
-
|
1519
|
+
vf = kwargs.pop("vf", None)
|
1520
|
+
fperms = vf and "fperms" in vf
|
1507
1521
|
|
1508
1522
|
if fname == os.devnull:
|
1509
1523
|
return fun(fname, *args, **kwargs), fname
|
@@ -1546,11 +1560,11 @@ def ren_open(fname , *args , **kwargs ) :
|
|
1546
1560
|
fp2 = os.path.join(fdir, fp2)
|
1547
1561
|
with open(fsenc(fp2), "wb") as f2:
|
1548
1562
|
f2.write(orig_name.encode("utf-8"))
|
1549
|
-
if
|
1550
|
-
|
1563
|
+
if fperms:
|
1564
|
+
set_fperms(f2, vf)
|
1551
1565
|
|
1552
|
-
if
|
1553
|
-
|
1566
|
+
if fperms:
|
1567
|
+
set_fperms(f, vf)
|
1554
1568
|
|
1555
1569
|
return f, fname
|
1556
1570
|
|
@@ -1612,14 +1626,10 @@ class MultipartParser(object):
|
|
1612
1626
|
self.args = args
|
1613
1627
|
self.headers = http_headers
|
1614
1628
|
|
1615
|
-
self.re_ctype =
|
1616
|
-
self.re_cdisp =
|
1617
|
-
self.re_cdisp_field =
|
1618
|
-
|
1619
|
-
)
|
1620
|
-
self.re_cdisp_file = re.compile(
|
1621
|
-
r'^content-disposition:(?: *|.*; *)filename="(.*)"', re.IGNORECASE
|
1622
|
-
)
|
1629
|
+
self.re_ctype = RE_CTYPE
|
1630
|
+
self.re_cdisp = RE_CDISP
|
1631
|
+
self.re_cdisp_field = RE_CDISP_FIELD
|
1632
|
+
self.re_cdisp_file = RE_CDISP_FILE
|
1623
1633
|
|
1624
1634
|
self.boundary = b""
|
1625
1635
|
self.gen = None
|
@@ -2158,6 +2168,16 @@ def find_prefix(ips , cidrs ) :
|
|
2158
2168
|
return ret
|
2159
2169
|
|
2160
2170
|
|
2171
|
+
def html_sh_esc(s ) :
|
2172
|
+
s = re.sub(RE_HTML_SH, "_", s).replace(" ", "%20")
|
2173
|
+
s = s.replace("\r", "_").replace("\n", "_")
|
2174
|
+
return s
|
2175
|
+
|
2176
|
+
|
2177
|
+
def json_hesc(s ) :
|
2178
|
+
return s.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026")
|
2179
|
+
|
2180
|
+
|
2161
2181
|
def html_escape(s , quot = False, crlf = False) :
|
2162
2182
|
"""html.escape but also newlines"""
|
2163
2183
|
s = s.replace("&", "&").replace("<", "<").replace(">", ">")
|
@@ -2477,6 +2497,14 @@ def lsof(log , abspath ) :
|
|
2477
2497
|
log("lsof failed; " + min_ex(), 3)
|
2478
2498
|
|
2479
2499
|
|
2500
|
+
def set_fperms(f , vf ) :
|
2501
|
+
fno = f.fileno()
|
2502
|
+
if "chmod_f" in vf:
|
2503
|
+
os.fchmod(fno, vf["chmod_f"])
|
2504
|
+
if "chown" in vf:
|
2505
|
+
os.fchown(fno, vf["uid"], vf["gid"])
|
2506
|
+
|
2507
|
+
|
2480
2508
|
def _fs_mvrm(
|
2481
2509
|
log , src , dst , atomic , flags
|
2482
2510
|
) :
|
@@ -2586,17 +2614,22 @@ def get_df(abspath , prune ) :
|
|
2586
2614
|
if ANYWIN:
|
2587
2615
|
abspath = fsdec(ap)
|
2588
2616
|
bfree = ctypes.c_ulonglong(0)
|
2617
|
+
btotal = ctypes.c_ulonglong(0)
|
2618
|
+
bavail = ctypes.c_ulonglong(0)
|
2589
2619
|
ctypes.windll.kernel32.GetDiskFreeSpaceExW( # type: ignore
|
2590
|
-
ctypes.c_wchar_p(abspath),
|
2620
|
+
ctypes.c_wchar_p(abspath),
|
2621
|
+
ctypes.pointer(bavail),
|
2622
|
+
ctypes.pointer(btotal),
|
2623
|
+
ctypes.pointer(bfree),
|
2591
2624
|
)
|
2592
|
-
return (
|
2625
|
+
return (bavail.value, btotal.value, "")
|
2593
2626
|
else:
|
2594
2627
|
sv = os.statvfs(ap)
|
2595
|
-
free = sv.f_frsize * sv.
|
2628
|
+
free = sv.f_frsize * sv.f_bavail
|
2596
2629
|
total = sv.f_frsize * sv.f_blocks
|
2597
2630
|
return (free, total, "")
|
2598
2631
|
except Exception as ex:
|
2599
|
-
return (
|
2632
|
+
return (0, 0, repr(ex))
|
2600
2633
|
|
2601
2634
|
|
2602
2635
|
if not ANYWIN and not MACOS:
|
@@ -4068,7 +4101,14 @@ def load_resource(E , name , mode="rb") :
|
|
4068
4101
|
stream = codecs.getreader(enc)(stream)
|
4069
4102
|
return stream
|
4070
4103
|
|
4071
|
-
|
4104
|
+
ap = os.path.join(E.mod, name)
|
4105
|
+
|
4106
|
+
if PY2:
|
4107
|
+
import codecs
|
4108
|
+
|
4109
|
+
return codecs.open(ap, "r", encoding=enc) # type: ignore
|
4110
|
+
|
4111
|
+
return open(ap, mode, encoding=enc)
|
4072
4112
|
|
4073
4113
|
|
4074
4114
|
class Pebkac(Exception):
|
copyparty/web/browser.html
CHANGED
@@ -109,8 +109,8 @@
|
|
109
109
|
{%- for f in files %}
|
110
110
|
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td>
|
111
111
|
{%- if f.tags is defined %}
|
112
|
-
{%- for k in taglist %}<td>{{ f.tags[k] }}</td>{%- endfor %}
|
113
|
-
{%- endif %}<td>{{ f.ext }}</td><td>{{ f.dt }}</td></tr>
|
112
|
+
{%- for k in taglist %}<td>{{ f.tags[k]|e }}</td>{%- endfor %}
|
113
|
+
{%- endif %}<td>{{ f.ext|e }}</td><td>{{ f.dt }}</td></tr>
|
114
114
|
{%- endfor %}
|
115
115
|
|
116
116
|
</tbody>
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/shares.js.gz
CHANGED
Binary file
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.html
CHANGED
@@ -240,14 +240,26 @@
|
|
240
240
|
<div class="os win">
|
241
241
|
<h1>ShareX</h1>
|
242
242
|
|
243
|
-
<p>to upload screenshots using ShareX <a href="https://
|
243
|
+
<p>to upload screenshots using ShareX <a href="https://getsharex.com/">v15+</a>, save this as <code>copyparty.sxcu</code> and run it:</p>
|
244
|
+
|
245
|
+
<pre class="dl" name="copyparty.sxcu">
|
246
|
+
{ "Version": "15.0.0", "Name": "copyparty",
|
247
|
+
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
248
|
+
"Headers": {
|
249
|
+
{% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url"
|
250
|
+
},
|
251
|
+
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
252
|
+
"Body": "MultipartFormData", "URL": "{response}",
|
253
|
+
"RequestMethod": "POST", "FileFormName": "f" }
|
254
|
+
</pre>
|
255
|
+
|
256
|
+
<p>for ShareX <a href="https://github.com/ShareX/ShareX/releases/tag/v12.1.1">v12</a> specifically, save this as <code>copyparty.sxcu</code> and run it:</p>
|
244
257
|
|
245
258
|
<pre class="dl" name="copyparty.sxcu">
|
246
259
|
{ "Name": "copyparty",
|
247
260
|
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
248
261
|
"Headers": {
|
249
|
-
{% if accs %}"pw": "<b>{{ pw }}</b>",{% endif %}
|
250
|
-
"accept": "url"
|
262
|
+
{% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url"
|
251
263
|
},
|
252
264
|
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
253
265
|
"FileFormName": "f" }
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.18.
|
3
|
+
Version: 1.18.7
|
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
|
@@ -138,6 +138,7 @@ made in Norway 🇳🇴
|
|
138
138
|
* [periodic rescan](#periodic-rescan) - filesystem monitoring
|
139
139
|
* [upload rules](#upload-rules) - set upload rules using volflags
|
140
140
|
* [compress uploads](#compress-uploads) - files can be autocompressed on upload
|
141
|
+
* [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
|
141
142
|
* [other flags](#other-flags)
|
142
143
|
* [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
|
143
144
|
* [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
|
@@ -1707,6 +1708,26 @@ some examples,
|
|
1707
1708
|
allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
|
1708
1709
|
|
1709
1710
|
|
1711
|
+
## chmod and chown
|
1712
|
+
|
1713
|
+
per-volume filesystem-permissions and ownership
|
1714
|
+
|
1715
|
+
by default:
|
1716
|
+
* all folders are chmod 755
|
1717
|
+
* files are usually chmod 644 (umask-defined)
|
1718
|
+
* user/group is whatever copyparty is running as
|
1719
|
+
|
1720
|
+
this can be configured per-volume:
|
1721
|
+
* volflag `chmod_f` sets file permissions; default=`644` (usually)
|
1722
|
+
* volflag `chmod_d` sets directory permissions; default=`755`
|
1723
|
+
* volflag `uid` sets the owner user-id
|
1724
|
+
* volflag `gid` sets the owner group-id
|
1725
|
+
|
1726
|
+
notes:
|
1727
|
+
* `gid` can only be set to one of the groups which the copyparty process is a member of
|
1728
|
+
* `uid` can only be set if copyparty is running as root (i appreciate your faith)
|
1729
|
+
|
1730
|
+
|
1710
1731
|
## other flags
|
1711
1732
|
|
1712
1733
|
* `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
|
@@ -2284,6 +2305,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
|
|
2284
2305
|
| env-var | what it does |
|
2285
2306
|
| ------------------------ | ------------ |
|
2286
2307
|
| `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
|
2308
|
+
| `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
|
2287
2309
|
|
2288
2310
|
|
2289
2311
|
# packages
|
@@ -1,17 +1,17 @@
|
|
1
1
|
copyparty/__init__.py,sha256=4aJw_Mt3eSNMV8sJ95Nh4ris-tBUYhCOV094Rnxa5Xo,2651
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
2
|
+
copyparty/__main__.py,sha256=vs0_3Ba4k4BbYF34V3loxPB0Dds_T1akBr44mpXv3TQ,127567
|
3
|
+
copyparty/__version__.py,sha256=BmHBWBUUrc62D7mlf1y2spt-lRJLom0MewDTgdJGeaQ,249
|
4
|
+
copyparty/authsrv.py,sha256=luOWk3_6ZvCJI8bp6UmBPSeyQT02HH5Nf6vnakCwzGI,122483
|
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
9
|
copyparty/cert.py,sha256=pSSeVYticrDsnsrdRtfpUQN-8WRObsqrYtSRroXmgxo,7992
|
10
|
-
copyparty/cfg.py,sha256=
|
10
|
+
copyparty/cfg.py,sha256=THceFupFmsZWF8iJKDDHR_XUEU3TNQgDRJB50iLiC1k,15734
|
11
11
|
copyparty/dxml.py,sha256=vu5uZQtwvwoqnFHbULs2Zh_y2DETu0T-ENpMZ1i2CV4,2505
|
12
12
|
copyparty/fsutil.py,sha256=NC_CJC4TDag399vVDH9_uQfdfpTMwRFLNxERSWhlVvs,4594
|
13
|
-
copyparty/ftpd.py,sha256=
|
14
|
-
copyparty/httpcli.py,sha256=
|
13
|
+
copyparty/ftpd.py,sha256=S0w6iMR8AlzLc_Aqn-TKuUJ-vNbmeQF6SQs614-NFOE,18107
|
14
|
+
copyparty/httpcli.py,sha256=hoc9p9iJ_WegTrUbLTAOkwwqltnvw35eQLO0_ZzvN3I,231645
|
15
15
|
copyparty/httpconn.py,sha256=IA9fdCjigawZ4kWhgvVN3nSiy5pb3W2qaE6rFqUYdq0,6943
|
16
16
|
copyparty/httpsrv.py,sha256=MCNjOEH_xM2qXCLGcoN6W0RhWlikv68-zBx0nICIheU,18864
|
17
17
|
copyparty/ico.py,sha256=-7QjF_jIxnPo4Vr0oUPksQ_U_Ef0HRsSPm3s71idOz8,3879
|
@@ -20,21 +20,21 @@ copyparty/metrics.py,sha256=1dim0ShnsD5cfspRbeN9Mt14wOIxPRtxCZY2IScikKw,8974
|
|
20
20
|
copyparty/mtag.py,sha256=ljqkiUblKzmLF6NVSXBRcFvy61j8QdVOjXToi2xQcyM,19939
|
21
21
|
copyparty/multicast.py,sha256=Me4XEEJijvvK2lMRwmGU2hsaI5_E9AEpCjIC4b9UefA,12393
|
22
22
|
copyparty/pwhash.py,sha256=zHoz9FHGkFBxoRvSfG1XyjN3ibww_h5GE6_m5yS-fws,4246
|
23
|
-
copyparty/smbd.py,sha256=
|
23
|
+
copyparty/smbd.py,sha256=Czo8SRkkl4ndCwEUe9Cbr8v0YOnyQHzubGSguPizuTc,14651
|
24
24
|
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=E65jAaOzHlJYnqsOKDMPVT8kALPUVexpkSStWBdItkY,3231
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=LVhCj0IxHBlGdDbX-At_wAQioQ2MQD2WPbofHzA08B0,49083
|
28
28
|
copyparty/szip.py,sha256=9srQzjsTBrBadf6QMh4YRAL70rkZLevAOHqXWK5jvr8,8846
|
29
29
|
copyparty/tcpsrv.py,sha256=BCOqlT_mRu1ibHJpPzvf9c4h83AnIMEfd8nBBednCCg,20484
|
30
|
-
copyparty/tftpd.py,sha256=
|
30
|
+
copyparty/tftpd.py,sha256=QuPcdx77gLmEpit3lLc0x4Px6BrBBKJpJl4VqINc5O8,14254
|
31
31
|
copyparty/th_cli.py,sha256=IEX5tCb0gw9Z2aRIDL9bwdvJ6g5jhWZ8OEAAz16_xN4,5426
|
32
|
-
copyparty/th_srv.py,sha256=
|
32
|
+
copyparty/th_srv.py,sha256=S6ChazjXwXevUxkAajwIwHzA16PyNIwv6kYRFakvLmY,32759
|
33
33
|
copyparty/u2idx.py,sha256=4Y5OOPyVkc-pS0z6e3p4StXAMnjHobSOMmMsvNUTD34,13674
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=n1ZSEopnFmXbjPEU_B84T3i2nCXvIuLuY308uM4UaJQ,179150
|
35
|
+
copyparty/util.py,sha256=4Q7fgR4GDjiTUIRjOSEhmZGscKSimgeICGQFcVRyHRU,105446
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
-
copyparty/bos/bos.py,sha256=
|
37
|
+
copyparty/bos/bos.py,sha256=DYt5mJJNt-935rU7HRm8kt_whpcVSI0uSphvD7PXrJo,2247
|
38
38
|
copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
|
39
39
|
copyparty/res/COPYING.txt,sha256=1LnBxkwJuC8mRBxuoMF2UIcpCjcteIzFHotSUE1Xte0,9776
|
40
40
|
copyparty/res/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -56,8 +56,8 @@ copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43
|
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
57
|
copyparty/web/baguettebox.js.gz,sha256=MxRofvhXjmUN7RtXtC17_9AlROVNUT-66WwJ_pHLY9c,8258
|
58
58
|
copyparty/web/browser.css.gz,sha256=29D3F4uB-VMd6uJo-SxWAwLfn08jcETYZm0SOJFJLAE,11847
|
59
|
-
copyparty/web/browser.html,sha256=
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
59
|
+
copyparty/web/browser.html,sha256=FKyez1jN3I7iG7VlAjZDtNi6PenYue22mahThVv4BTA,4792
|
60
|
+
copyparty/web/browser.js.gz,sha256=yMy_s-cgUH8Vo0cos3lIZTqaBxyfJpphOGK0oM02jAc,111316
|
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
|
@@ -77,11 +77,11 @@ copyparty/web/rups.html,sha256=iPuz53jBT_mIWIfl1yrjjg5-P7oO2ada6fTFq8PgjGk,1479
|
|
77
77
|
copyparty/web/rups.js.gz,sha256=nvvcG8L-fkm7zkhjnlTGhBp_KD0j08mtHEW0sB7zy-Y,854
|
78
78
|
copyparty/web/shares.css.gz,sha256=SdPlZCBwz9tkPkgEo5pSPDOZSI079njxEfkJ64-iW3c,547
|
79
79
|
copyparty/web/shares.html,sha256=YctvUrKuBYu42kxVylyW2_DEHm7Ik6uHqzfzVZ4N0ac,2545
|
80
|
-
copyparty/web/shares.js.gz,sha256=
|
80
|
+
copyparty/web/shares.js.gz,sha256=NQzrF57cikU38NzoaWQhbrINWkQBZhu1sH0jAC2SESQ,967
|
81
81
|
copyparty/web/splash.css.gz,sha256=S8_A7JJl71xACRBYGzafeaD82OacW6Fa7oKPiNyrhAs,1087
|
82
82
|
copyparty/web/splash.html,sha256=0MvDe1lKfGqczi7d4nKjWjG0cRVyvs8J6sDEj3DCPSI,6376
|
83
|
-
copyparty/web/splash.js.gz,sha256=
|
84
|
-
copyparty/web/svcs.html,sha256=
|
83
|
+
copyparty/web/splash.js.gz,sha256=6ag8Szmjd_S1jr2D0-DgubM1F6Fq7dUU7A8E-qBY2gI,3702
|
84
|
+
copyparty/web/svcs.html,sha256=mamJdq0hsmHqG2BQsf9jg8G9bAl338wUhUZ2WtXOlGQ,14865
|
85
85
|
copyparty/web/svcs.js.gz,sha256=AYatNKyT_bKRWX8sb3WD_iujBY3L4P7HWBrsuMctsLs,722
|
86
86
|
copyparty/web/ui.css.gz,sha256=e3iIflzddmjoyPrun_1jsu9j7fbdonNQLyhEE2oKKOQ,2819
|
87
87
|
copyparty/web/up2k.js.gz,sha256=_uOZzORAFO91SG3TUHd9xhKhAIXwL3fUFBNEUKnEVHY,24812
|
@@ -110,9 +110,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
110
110
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
111
111
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
112
112
|
copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
|
113
|
-
copyparty-1.18.
|
114
|
-
copyparty-1.18.
|
115
|
-
copyparty-1.18.
|
116
|
-
copyparty-1.18.
|
117
|
-
copyparty-1.18.
|
118
|
-
copyparty-1.18.
|
113
|
+
copyparty-1.18.7.dist-info/licenses/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
114
|
+
copyparty-1.18.7.dist-info/METADATA,sha256=bpeGIM2brZAQDtodeY1BoLDFdRs2xm04ssv6qHf6zUM,167745
|
115
|
+
copyparty-1.18.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
116
|
+
copyparty-1.18.7.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
117
|
+
copyparty-1.18.7.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
118
|
+
copyparty-1.18.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|