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/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
- if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
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 = zsl[0].strip()
382
- t = "rproxy={} oob x-fwd {}"
383
- self.log(t.format(self.args.rproxy, zso), c=3)
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 (not self.uname or not "zipmaxu" in vn.flags):
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 (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
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 &nbsp;┐( ´ -`)┌</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/") or self.ua.startswith("fetch"):
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
- if add_og and not abspath.lower().endswith(".md"):
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
- (abspath.endswith(".md") or self.can_delete)
6227
+ (is_md or self.can_delete)
6175
6228
  and "nohtml" not in vn.flags
6176
6229
  and (
6177
- ("v" in self.uparam and abspath.endswith(".md"))
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 = "only listening on unix-sockets; cannot enable zeroconf/mdns/ssdp as requested"
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:" not in x]
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:" not in x]
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["t0c"]),
378
- int(job["poke"]),
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 ,
Binary file
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
  &nbsp; Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
21
21
  &nbsp; <span id="hits"></span>
22
- <table id="tab"><thead><tr>
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
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 %}
Binary file
copyparty/web/svcs.html CHANGED
@@ -37,6 +37,7 @@
37
37
  {% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint
38
38
  </span>
39
39
  {% if accs %}<a href="#" id="setpw">use real password</a>{% endif %}
40
+ <a href="#" id="qr">show qr</a>
40
41
  </p>
41
42
 
42
43
 
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.18.10
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 (probably outdated): https://ocv.me/copyparty/helptext.html
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. Look out for red and yellow log messages which explain how to do this. But basically, 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. Note that `--rp-loc` in particular will not work at all unless you do this
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/package/arch/copyparty.service) and expects to find one or more [config files](./docs/example.conf) in `/etc/copyparty.d/`
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
- 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
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