copyparty 1.18.10__py3-none-any.whl → 1.19.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/__main__.py +74 -65
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +23 -0
- copyparty/cfg.py +2 -0
- copyparty/ftpd.py +7 -2
- copyparty/httpcli.py +65 -16
- copyparty/multicast.py +1 -5
- copyparty/pwhash.py +4 -0
- copyparty/svchub.py +2 -11
- copyparty/tcpsrv.py +14 -3
- copyparty/tftpd.py +1 -1
- copyparty/up2k.py +3 -2
- copyparty/util.py +11 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/mde.js.gz +0 -0
- copyparty/web/rups.html +1 -8
- copyparty/web/rups.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.html +6 -1
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -0
- copyparty/web/svcs.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/METADATA +22 -8
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/RECORD +32 -32
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/WHEEL +0 -0
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/entry_points.txt +0 -0
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.18.10.dist-info → copyparty-1.19.0.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -62,6 +62,7 @@ from .util import (
|
|
62
62
|
alltrace,
|
63
63
|
atomic_move,
|
64
64
|
b64dec,
|
65
|
+
eol_conv,
|
65
66
|
exclude_dotfiles,
|
66
67
|
formatdate,
|
67
68
|
fsenc,
|
@@ -257,7 +258,8 @@ class HttpCli(object):
|
|
257
258
|
|
258
259
|
def _assert_safe_rem(self, rem ) :
|
259
260
|
# sanity check to prevent any disasters
|
260
|
-
|
261
|
+
# (this function hopefully serves no purpose; validation has already happened at this point, this only exists as a last-ditch effort just in case)
|
262
|
+
if rem.startswith(("/", "../")) or "/../" in rem:
|
261
263
|
raise Exception("that was close")
|
262
264
|
|
263
265
|
def _gen_fk(self, alg , salt , fspath , fsize , inode ) :
|
@@ -378,9 +380,20 @@ class HttpCli(object):
|
|
378
380
|
try:
|
379
381
|
cli_ip = zsl[n].strip()
|
380
382
|
except:
|
381
|
-
cli_ip =
|
382
|
-
|
383
|
-
self.
|
383
|
+
cli_ip = self.ip
|
384
|
+
self.bad_xff = True
|
385
|
+
if self.args.rproxy != 9999999:
|
386
|
+
t = "global-option --rproxy %d could not be used (out-of-bounds) for the received header [%s]"
|
387
|
+
self.log(t % (self.args.rproxy, zso), c=3)
|
388
|
+
else:
|
389
|
+
zsl = [
|
390
|
+
" rproxy: %d if this client's IP-address is [%s]"
|
391
|
+
% (-1 - zd, zs.strip())
|
392
|
+
for zd, zs in enumerate(zsl)
|
393
|
+
]
|
394
|
+
t = 'could not determine the client\'s IP-address because the global-option --rproxy has not been configured, so the request-header [%s] specified by global-option --xff-hdr cannot be used safely! Please see the "reverse-proxy" section in the readme. The best approach is to configure your reverse-proxy to give copyparty the exact IP-address to assume (perhaps in another header), but you may also try the following:'
|
395
|
+
t = t % (self.args.xff_hdr,)
|
396
|
+
self.log("%s\n\n%s\n" % (t, "\n".join(zsl)), 3)
|
384
397
|
|
385
398
|
pip = self.conn.addr[0]
|
386
399
|
xffs = self.conn.xff_nm
|
@@ -2912,12 +2925,16 @@ class HttpCli(object):
|
|
2912
2925
|
return True
|
2913
2926
|
|
2914
2927
|
def handle_chpw(self) :
|
2928
|
+
if self.args.usernames:
|
2929
|
+
self.parser.require("uname", 64)
|
2915
2930
|
pwd = self.parser.require("pw", 64)
|
2916
2931
|
self.parser.drop()
|
2917
2932
|
|
2918
2933
|
ok, msg = self.asrv.chpw(self.conn.hsrv.broker, self.uname, pwd)
|
2919
2934
|
if ok:
|
2920
2935
|
self.cbonk(self.conn.hsrv.gpwc, pwd, "pw", "too many password changes")
|
2936
|
+
if self.args.usernames:
|
2937
|
+
pwd = "%s:%s" % (self.uname, pwd)
|
2921
2938
|
ok, msg = self.get_pwd_cookie(pwd)
|
2922
2939
|
if ok:
|
2923
2940
|
msg = "new password OK"
|
@@ -2929,6 +2946,15 @@ class HttpCli(object):
|
|
2929
2946
|
return True
|
2930
2947
|
|
2931
2948
|
def handle_login(self) :
|
2949
|
+
if self.args.usernames and not (
|
2950
|
+
self.args.shr and self.vpath.startswith(self.args.shr1)
|
2951
|
+
):
|
2952
|
+
try:
|
2953
|
+
un = self.parser.require("uname", 64)
|
2954
|
+
except:
|
2955
|
+
un = ""
|
2956
|
+
else:
|
2957
|
+
un = ""
|
2932
2958
|
pwd = self.parser.require("cppwd", 64)
|
2933
2959
|
try:
|
2934
2960
|
uhash = self.parser.require("uhash", 256)
|
@@ -2939,6 +2965,9 @@ class HttpCli(object):
|
|
2939
2965
|
if not pwd:
|
2940
2966
|
raise Pebkac(422, "password cannot be blank")
|
2941
2967
|
|
2968
|
+
if un:
|
2969
|
+
pwd = "%s:%s" % (un, pwd)
|
2970
|
+
|
2942
2971
|
dst = self.args.SRS
|
2943
2972
|
if self.vpath:
|
2944
2973
|
dst += quotep(self.vpaths)
|
@@ -3566,7 +3595,7 @@ class HttpCli(object):
|
|
3566
3595
|
rem = "{}/{}".format(rp, fn).strip("/")
|
3567
3596
|
dbv, vrem = vfs.get_dbv(rem)
|
3568
3597
|
|
3569
|
-
if not rem.endswith(".md") and not self.can_delete:
|
3598
|
+
if not rem.lower().endswith(".md") and not self.can_delete:
|
3570
3599
|
raise Pebkac(400, "only markdown pls")
|
3571
3600
|
|
3572
3601
|
if nullwrite:
|
@@ -3649,6 +3678,9 @@ class HttpCli(object):
|
|
3649
3678
|
if p_field != "body":
|
3650
3679
|
raise Pebkac(400, "expected body, got {}".format(p_field))
|
3651
3680
|
|
3681
|
+
if "txt_eol" in vfs.flags:
|
3682
|
+
p_data = eol_conv(p_data, vfs.flags["txt_eol"])
|
3683
|
+
|
3652
3684
|
xbu = vfs.flags.get("xbu")
|
3653
3685
|
if xbu:
|
3654
3686
|
if not runhook(
|
@@ -4615,7 +4647,9 @@ class HttpCli(object):
|
|
4615
4647
|
else:
|
4616
4648
|
fn = self.host.split(":")[0]
|
4617
4649
|
|
4618
|
-
if vn.flags.get("zipmax") and
|
4650
|
+
if vn.flags.get("zipmax") and not (
|
4651
|
+
vn.flags.get("zipmaxu") and self.uname != "*"
|
4652
|
+
):
|
4619
4653
|
maxs = vn.flags.get("zipmaxs_v") or 0
|
4620
4654
|
maxn = vn.flags.get("zipmaxn_v") or 0
|
4621
4655
|
nf = 0
|
@@ -5008,7 +5042,7 @@ class HttpCli(object):
|
|
5008
5042
|
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
|
5009
5043
|
|
5010
5044
|
fmt = self.uparam.get("ls", "")
|
5011
|
-
if not fmt and
|
5045
|
+
if not fmt and self.ua.startswith(("curl/", "fetch")):
|
5012
5046
|
fmt = "v"
|
5013
5047
|
|
5014
5048
|
if fmt in ["v", "t", "txt"]:
|
@@ -5048,6 +5082,13 @@ class HttpCli(object):
|
|
5048
5082
|
self.reply(zb, mime="text/plain; charset=utf-8")
|
5049
5083
|
return True
|
5050
5084
|
|
5085
|
+
re_btn = ""
|
5086
|
+
nre = self.args.ctl_re
|
5087
|
+
if "re" in self.uparam:
|
5088
|
+
self.out_headers["Refresh"] = str(nre)
|
5089
|
+
elif nre:
|
5090
|
+
re_btn = "&re=%s" % (nre,)
|
5091
|
+
|
5051
5092
|
html = self.j2s(
|
5052
5093
|
"splash",
|
5053
5094
|
this=self,
|
@@ -5065,6 +5106,7 @@ class HttpCli(object):
|
|
5065
5106
|
mtpq=vs["mtpq"],
|
5066
5107
|
dbwt=vs["dbwt"],
|
5067
5108
|
url_suf=suf,
|
5109
|
+
re=re_btn,
|
5068
5110
|
k304=self.k304(),
|
5069
5111
|
no304=self.no304(),
|
5070
5112
|
k304vis=self.args.k304 > 0,
|
@@ -5110,7 +5152,7 @@ class HttpCli(object):
|
|
5110
5152
|
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p><a id="r" href="{}/?h">go home</a></p>'
|
5111
5153
|
pt = "404 not found ┐( ´ -`)┌"
|
5112
5154
|
|
5113
|
-
if self.ua.startswith("curl/"
|
5155
|
+
if self.ua.startswith(("curl/", "fetch")):
|
5114
5156
|
pt = "# acct: %s\n%s\n" % (self.uname, pt)
|
5115
5157
|
self.reply(pt.encode("utf-8"), status=rc)
|
5116
5158
|
return True
|
@@ -5424,6 +5466,8 @@ class HttpCli(object):
|
|
5424
5466
|
elif nfi == 3:
|
5425
5467
|
if not vp.endswith(vfi):
|
5426
5468
|
continue
|
5469
|
+
else:
|
5470
|
+
continue
|
5427
5471
|
|
5428
5472
|
n -= 1
|
5429
5473
|
if not n:
|
@@ -5547,6 +5591,8 @@ class HttpCli(object):
|
|
5547
5591
|
elif nfi == 3:
|
5548
5592
|
if not vp.endswith(vfi):
|
5549
5593
|
continue
|
5594
|
+
else:
|
5595
|
+
continue
|
5550
5596
|
|
5551
5597
|
if not dots and "/." in vp:
|
5552
5598
|
continue
|
@@ -5977,6 +6023,12 @@ class HttpCli(object):
|
|
5977
6023
|
else:
|
5978
6024
|
[x.pop(k) for k in ["name", "dt"] for y in [dirs, files] for x in y]
|
5979
6025
|
|
6026
|
+
# nonce (tlnote: norwegian for flake as in snowflake)
|
6027
|
+
if self.args.no_fnugg:
|
6028
|
+
ls["fnugg"] = "nei"
|
6029
|
+
elif "fnugg" in self.headers:
|
6030
|
+
ls["fnugg"] = self.headers["fnugg"]
|
6031
|
+
|
5980
6032
|
ret = json.dumps(ls)
|
5981
6033
|
mime = "application/json"
|
5982
6034
|
|
@@ -6159,7 +6211,8 @@ class HttpCli(object):
|
|
6159
6211
|
if not use_filekey:
|
6160
6212
|
return self.tx_404(True)
|
6161
6213
|
|
6162
|
-
|
6214
|
+
is_md = abspath.lower().endswith(".md")
|
6215
|
+
if add_og and not is_md:
|
6163
6216
|
if og_ua or self.host not in self.headers.get("referer", ""):
|
6164
6217
|
self.vpath, og_fn = vsplit(self.vpath)
|
6165
6218
|
vpath = self.vpath
|
@@ -6171,10 +6224,10 @@ class HttpCli(object):
|
|
6171
6224
|
vpnodes.pop()
|
6172
6225
|
|
6173
6226
|
if (
|
6174
|
-
(
|
6227
|
+
(is_md or self.can_delete)
|
6175
6228
|
and "nohtml" not in vn.flags
|
6176
6229
|
and (
|
6177
|
-
("v" in self.uparam
|
6230
|
+
(is_md and "v" in self.uparam)
|
6178
6231
|
or "edit" in self.uparam
|
6179
6232
|
or "edit2" in self.uparam
|
6180
6233
|
)
|
@@ -6231,11 +6284,7 @@ class HttpCli(object):
|
|
6231
6284
|
is_ls = "ls" in self.uparam
|
6232
6285
|
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
6233
6286
|
|
6234
|
-
if (
|
6235
|
-
not is_ls
|
6236
|
-
and not add_og
|
6237
|
-
and (self.ua.startswith("curl/") or self.ua.startswith("fetch"))
|
6238
|
-
):
|
6287
|
+
if not is_ls and not add_og and self.ua.startswith(("curl/", "fetch")):
|
6239
6288
|
self.uparam["ls"] = "v"
|
6240
6289
|
is_ls = True
|
6241
6290
|
|
copyparty/multicast.py
CHANGED
@@ -180,11 +180,7 @@ class MCast(object):
|
|
180
180
|
srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False)
|
181
181
|
|
182
182
|
# gvfs breaks if a linklocal ip appears in a dns reply
|
183
|
-
ll = {
|
184
|
-
k: v
|
185
|
-
for k, v in srv.ips.items()
|
186
|
-
if k.startswith("169.254") or k.startswith("fe80")
|
187
|
-
}
|
183
|
+
ll = {k: v for k, v in srv.ips.items() if k.startswith(("169.254", "fe80"))}
|
188
184
|
rt = {k: v for k, v in srv.ips.items() if k not in ll}
|
189
185
|
|
190
186
|
if self.args.ll or not rt:
|
copyparty/pwhash.py
CHANGED
@@ -147,6 +147,10 @@ class PWHash(object):
|
|
147
147
|
def cli(self) :
|
148
148
|
import getpass
|
149
149
|
|
150
|
+
if self.args.usernames:
|
151
|
+
t = "since you have enabled --usernames, please provide username:password"
|
152
|
+
print(t)
|
153
|
+
|
150
154
|
while True:
|
151
155
|
try:
|
152
156
|
p1 = getpass.getpass("password> ")
|
copyparty/svchub.py
CHANGED
@@ -840,15 +840,6 @@ class SvcHub(object):
|
|
840
840
|
|
841
841
|
def _check_env(self) :
|
842
842
|
al = self.args
|
843
|
-
try:
|
844
|
-
files = os.listdir(E.cfg)
|
845
|
-
except:
|
846
|
-
files = []
|
847
|
-
|
848
|
-
hits = [x for x in files if x.lower().endswith(".conf")]
|
849
|
-
if hits:
|
850
|
-
t = "WARNING: found config files in [%s]: %s\n config files are not expected here, and will NOT be loaded (unless your setup is intentionally hella funky)"
|
851
|
-
self.log("root", t % (E.cfg, ", ".join(hits)), 3)
|
852
843
|
|
853
844
|
if self.args.no_bauth:
|
854
845
|
t = "WARNING: --no-bauth disables support for the Android app; you may want to use --bauth-last instead"
|
@@ -858,7 +849,7 @@ class SvcHub(object):
|
|
858
849
|
|
859
850
|
have_tcp = False
|
860
851
|
for zs in al.i:
|
861
|
-
if not zs.startswith("unix:"):
|
852
|
+
if not zs.startswith(("unix:", "fd:")):
|
862
853
|
have_tcp = True
|
863
854
|
if not have_tcp:
|
864
855
|
zb = False
|
@@ -868,7 +859,7 @@ class SvcHub(object):
|
|
868
859
|
setattr(al, zs, False)
|
869
860
|
zb = True
|
870
861
|
if zb:
|
871
|
-
t = "
|
862
|
+
t = "not listening on any ip-addresses (only unix-sockets and/or FDs); cannot enable zeroconf/mdns/ssdp as requested"
|
872
863
|
self.log("root", t, 3)
|
873
864
|
|
874
865
|
if not self.args.no_dav:
|
copyparty/tcpsrv.py
CHANGED
@@ -242,8 +242,10 @@ class TcpSrv(object):
|
|
242
242
|
|
243
243
|
def _listen(self, ip , port ) :
|
244
244
|
uds_perm = uds_gid = -1
|
245
|
+
bound = None
|
246
|
+
tcp = False
|
247
|
+
|
245
248
|
if "unix:" in ip:
|
246
|
-
tcp = False
|
247
249
|
ipv = socket.AF_UNIX
|
248
250
|
uds = ip.split(":")
|
249
251
|
ip = uds[-1]
|
@@ -256,7 +258,12 @@ class TcpSrv(object):
|
|
256
258
|
import grp
|
257
259
|
|
258
260
|
uds_gid = grp.getgrnam(uds[2]).gr_gid
|
261
|
+
elif "fd:" in ip:
|
262
|
+
fd = ip[3:]
|
263
|
+
bound = socket.socket(fileno=int(fd))
|
259
264
|
|
265
|
+
tcp = bound.proto == socket.IPPROTO_TCP
|
266
|
+
ipv = bound.family
|
260
267
|
elif ":" in ip:
|
261
268
|
tcp = True
|
262
269
|
ipv = socket.AF_INET6
|
@@ -264,7 +271,7 @@ class TcpSrv(object):
|
|
264
271
|
tcp = True
|
265
272
|
ipv = socket.AF_INET
|
266
273
|
|
267
|
-
srv = socket.socket(ipv, socket.SOCK_STREAM)
|
274
|
+
srv = bound or socket.socket(ipv, socket.SOCK_STREAM)
|
268
275
|
|
269
276
|
if not ANYWIN or self.args.reuseaddr:
|
270
277
|
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
@@ -282,6 +289,10 @@ class TcpSrv(object):
|
|
282
289
|
if getattr(self.args, "freebind", False):
|
283
290
|
srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1)
|
284
291
|
|
292
|
+
if bound:
|
293
|
+
self.srv.append(srv)
|
294
|
+
return
|
295
|
+
|
285
296
|
try:
|
286
297
|
if tcp:
|
287
298
|
srv.bind((ip, port))
|
@@ -434,7 +445,7 @@ class TcpSrv(object):
|
|
434
445
|
def detect_interfaces(self, listen_ips ) :
|
435
446
|
from .stolen.ifaddr import get_adapters
|
436
447
|
|
437
|
-
listen_ips = [x for x in listen_ips if "unix:"
|
448
|
+
listen_ips = [x for x in listen_ips if not x.startswith(("unix:", "fd:"))]
|
438
449
|
|
439
450
|
nics = get_adapters(True)
|
440
451
|
eps = {}
|
copyparty/tftpd.py
CHANGED
@@ -176,7 +176,7 @@ class Tftpd(object):
|
|
176
176
|
if "::" in ips:
|
177
177
|
ips.append("0.0.0.0")
|
178
178
|
|
179
|
-
ips = [x for x in ips if "unix:"
|
179
|
+
ips = [x for x in ips if not x.startswith(("unix:", "fd:"))]
|
180
180
|
|
181
181
|
if self.args.tftp4:
|
182
182
|
ips = [x for x in ips if ":" not in x]
|
copyparty/up2k.py
CHANGED
@@ -371,11 +371,12 @@ class Up2k(object):
|
|
371
371
|
if ineed == ihash or not ineed:
|
372
372
|
continue
|
373
373
|
|
374
|
+
poke = job["poke"]
|
374
375
|
zt = (
|
375
376
|
ineed / ihash,
|
376
377
|
job["size"],
|
377
|
-
int(job
|
378
|
-
int(
|
378
|
+
int(job.get("t0c", poke)),
|
379
|
+
int(poke),
|
379
380
|
djoin(vtop, job["prel"], job["name"]),
|
380
381
|
)
|
381
382
|
ret.append(zt)
|
copyparty/util.py
CHANGED
@@ -2895,6 +2895,17 @@ def justcopy(
|
|
2895
2895
|
return tlen, "checksum-disabled", "checksum-disabled"
|
2896
2896
|
|
2897
2897
|
|
2898
|
+
def eol_conv(
|
2899
|
+
fin , conv
|
2900
|
+
) :
|
2901
|
+
crlf = conv.lower() == "crlf"
|
2902
|
+
for buf in fin:
|
2903
|
+
buf = buf.replace(b"\r", b"")
|
2904
|
+
if crlf:
|
2905
|
+
buf = buf.replace(b"\n", b"\r\n")
|
2906
|
+
yield buf
|
2907
|
+
|
2908
|
+
|
2898
2909
|
def hashcopy(
|
2899
2910
|
fin ,
|
2900
2911
|
fout ,
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/md2.js.gz
CHANGED
Binary file
|
copyparty/web/mde.js.gz
CHANGED
Binary file
|
copyparty/web/rups.html
CHANGED
@@ -19,14 +19,7 @@
|
|
19
19
|
<a href="{{ r }}/?h">control-panel</a>
|
20
20
|
Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
|
21
21
|
<span id="hits"></span>
|
22
|
-
<
|
23
|
-
<th>size</th>
|
24
|
-
<th>who</th>
|
25
|
-
<th>when</th>
|
26
|
-
<th>age</th>
|
27
|
-
<th>dir</th>
|
28
|
-
<th>file</th>
|
29
|
-
</tr></thead><tbody id="tb"></tbody></table>
|
22
|
+
<div id="tw"></div>
|
30
23
|
</div>
|
31
24
|
<a href="#" id="repl">π</a>
|
32
25
|
<script>
|
copyparty/web/rups.js.gz
CHANGED
Binary file
|
copyparty/web/shares.js.gz
CHANGED
Binary file
|
copyparty/web/splash.html
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
<body>
|
16
16
|
<div id="wrap">
|
17
17
|
{%- if not in_shr %}
|
18
|
-
<a id="a" href="{{ r }}/?h" class="af">refresh</a>
|
18
|
+
<a id="a" href="{{ r }}/?h{{ re }}" class="af">refresh</a>
|
19
19
|
<a id="v" href="{{ r }}/?hc" class="af">connect</a>
|
20
20
|
|
21
21
|
{%- if this.uname == '*' %}
|
@@ -120,7 +120,12 @@
|
|
120
120
|
<div>
|
121
121
|
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
122
122
|
<input type="hidden" id="la" name="act" value="login" />
|
123
|
+
{% if this.args.usernames %}
|
124
|
+
<input type="text" id="lu" name="uname" placeholder=" username" size="12" />
|
125
|
+
<input type="password" id="lp" name="cppwd" placeholder=" password" size="12" />
|
126
|
+
{% else %}
|
123
127
|
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
128
|
+
{% endif %}
|
124
129
|
<input type="hidden" name="uhash" id="uhash" value="x" />
|
125
130
|
<input type="submit" id="ls" value="login" />
|
126
131
|
{% if chpw %}
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.html
CHANGED
copyparty/web/svcs.js.gz
CHANGED
Binary file
|
copyparty/web/up2k.js.gz
CHANGED
Binary file
|
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.
|
3
|
+
Version: 1.19.0
|
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
|
@@ -502,6 +502,7 @@ upgrade notes
|
|
502
502
|
|
503
503
|
* can I link someone to a password-protected volume/file by including the password in the URL?
|
504
504
|
* yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
|
505
|
+
* if you have enabled `--usernames` then do `?pw=username:password` instead
|
505
506
|
|
506
507
|
* how do I stop `.hist` folders from appearing everywhere on my HDD?
|
507
508
|
* by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)
|
@@ -1081,6 +1082,7 @@ a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3
|
|
1081
1082
|
url parameters:
|
1082
1083
|
|
1083
1084
|
* `pw=hunter2` for password auth
|
1085
|
+
* if you enabled `--usernames` then do `pw=username:password` instead
|
1084
1086
|
* `recursive` to also include subfolders
|
1085
1087
|
* `title=foo` changes the feed title (default: folder name)
|
1086
1088
|
* `fext=mp3,opus` only include mp3 and opus files (default: all)
|
@@ -1294,7 +1296,7 @@ using arguments or config files, or a mix of both:
|
|
1294
1296
|
|
1295
1297
|
**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with `--help` to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in `--help-flags` can be used in volumes as volflags.
|
1296
1298
|
* if running in docker/podman, try this: `docker run --rm -it copyparty/ac --help`
|
1297
|
-
* or see this
|
1299
|
+
* or see this: https://ocv.me/copyparty/helptext.html
|
1298
1300
|
* or if you prefer plaintext, https://ocv.me/copyparty/helptext.txt
|
1299
1301
|
|
1300
1302
|
|
@@ -1366,6 +1368,7 @@ an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit T
|
|
1366
1368
|
* if you enable both `ftp` and `ftps`, the port-range will be divided in half
|
1367
1369
|
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
1368
1370
|
* login with any username + your password, or put your password in the username field
|
1371
|
+
* unless you enabled `--usernames`
|
1369
1372
|
|
1370
1373
|
some recommended FTP / FTPS clients; `wark` = example password:
|
1371
1374
|
* https://winscp.net/eng/download.php
|
@@ -1383,6 +1386,7 @@ click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to se
|
|
1383
1386
|
|
1384
1387
|
general usage:
|
1385
1388
|
* login with any username + your password, or put your password in the username field (password field can be empty/whatever)
|
1389
|
+
* unless you enabled `--usernames`
|
1386
1390
|
|
1387
1391
|
on macos, connect from finder:
|
1388
1392
|
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
@@ -1398,6 +1402,7 @@ using the GUI (winXP or later):
|
|
1398
1402
|
* rightclick [my computer] -> [map network drive] -> Folder: `http://192.168.123.1:3923/`
|
1399
1403
|
* on winXP only, click the `Sign up for online storage` hyperlink instead and put the URL there
|
1400
1404
|
* providing your password as the username is recommended; the password field can be anything or empty
|
1405
|
+
* unless you enabled `--usernames`
|
1401
1406
|
|
1402
1407
|
the webdav client that's built into windows has the following list of bugs; you can avoid all of these by connecting with rclone instead:
|
1403
1408
|
* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password
|
@@ -1455,6 +1460,7 @@ some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
|
|
1455
1460
|
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
|
1456
1461
|
* account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
|
1457
1462
|
* [shadowing](#shadowing) probably works as expected but no guarantees
|
1463
|
+
* not compatible with pw-hashing or `--usernames`
|
1458
1464
|
|
1459
1465
|
and some minor issues,
|
1460
1466
|
* clients only see the first ~400 files in big folders;
|
@@ -2123,7 +2129,11 @@ you can either:
|
|
2123
2129
|
* or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
|
2124
2130
|
* if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
|
2125
2131
|
|
2126
|
-
when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP.
|
2132
|
+
when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP. The best/safest approach is to configure your reverse-proxy so it gives copyparty a header which only contains the client's true/real IP-address, and then setting `--xff-hdr theHeaderName --rproxy 1` but alternatively, if you want/need to let copyparty handle this, look out for red and yellow log messages which explain how to do that. Basically, the log will say this:
|
2133
|
+
|
2134
|
+
> set `--xff-hdr` to the name of the http-header to read the IP from (usually `x-forwarded-for`, but cloudflare uses `cf-connecting-ip`), and then `--xff-src` to the IP of the reverse-proxy so copyparty will trust the xff-hdr. You will also need to configure `--rproxy` to `1` if the header only contains one IP (the correct one) or to a *negative value* if it contains multiple; `-1` being the rightmost and most trusted IP (the nearest proxy, so usually not the correct one), `-2` being the second-closest hop, and so on
|
2135
|
+
|
2136
|
+
Note that `--rp-loc` in particular will not work at all unless you configure the above correctly
|
2127
2137
|
|
2128
2138
|
some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which *could* be a nice speed boost, depending on a lot of factors
|
2129
2139
|
* **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
|
@@ -2342,11 +2352,9 @@ if your distro/OS is not mentioned below, there might be some hints in the [«on
|
|
2342
2352
|
|
2343
2353
|
`pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
|
2344
2354
|
|
2345
|
-
it comes with a [systemd service](./contrib/
|
2346
|
-
|
2347
|
-
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)
|
2355
|
+
it comes with a [systemd service](./contrib/systemd/copyparty@.service) as well as a [user service](./contrib/systemd/copyparty-user.service), and expects to find a [config file](./contrib/systemd/copyparty.example.conf) in `/etc/copyparty/copyparty.conf` or `~/.config/copyparty/copyparty.conf`
|
2348
2356
|
|
2349
|
-
|
2357
|
+
after installing, start either the system service or the user service and navigate to http://127.0.0.1:3923 for further instructions (unless you already edited the config files, in which case you are good to go, probably)
|
2350
2358
|
|
2351
2359
|
|
2352
2360
|
## fedora package
|
@@ -2569,6 +2577,8 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
|
|
2569
2577
|
|
2570
2578
|
> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)
|
2571
2579
|
|
2580
|
+
* unless you've enabled `--usernames`, then it's `PW: usr:pwd`, cookie `cppwd=usr:pwd`, url-param `?pw=usr:pwd`
|
2581
|
+
|
2572
2582
|
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
2573
2583
|
|
2574
2584
|
|
@@ -2680,7 +2690,7 @@ there is a [discord server](https://discord.gg/25J8CdTT6G) with an `@everyone`
|
|
2680
2690
|
|
2681
2691
|
some notes on hardening
|
2682
2692
|
|
2683
|
-
* set `--rproxy 0` if your copyparty is directly facing the internet (not through a reverse-proxy)
|
2693
|
+
* set `--rproxy 0` *if and only if* your copyparty is directly facing the internet (not through a reverse-proxy)
|
2684
2694
|
* cors doesn't work right otherwise
|
2685
2695
|
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
|
2686
2696
|
* this returns html documents as plaintext, and also disables markdown rendering
|
@@ -2784,6 +2794,8 @@ when generating hashes using `--ah-cli` for docker or systemd services, make sur
|
|
2784
2794
|
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
2785
2795
|
* setting the same `--ah-salt` in both environments
|
2786
2796
|
|
2797
|
+
> ⚠️ if you have enabled `--usernames` then provide the password as `username:password` when hashing it, for example `ed:hunter2`
|
2798
|
+
|
2787
2799
|
|
2788
2800
|
## https
|
2789
2801
|
|
@@ -2901,6 +2913,8 @@ these are standalone programs and will never be imported / evaluated by copypart
|
|
2901
2913
|
|
2902
2914
|
the self-contained "binary" (recommended!) [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
|
2903
2915
|
|
2916
|
+
if you only need english, [copyparty-en.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.py) is the same thing but smaller
|
2917
|
+
|
2904
2918
|
you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
|
2905
2919
|
|
2906
2920
|
|