copyparty 1.16.13__tar.gz → 1.16.15__tar.gz

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.
Files changed (127) hide show
  1. {copyparty-1.16.13 → copyparty-1.16.15}/PKG-INFO +59 -4
  2. {copyparty-1.16.13 → copyparty-1.16.15}/README.md +58 -3
  3. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/__main__.py +3 -1
  4. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/__version__.py +2 -2
  5. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/authsrv.py +18 -7
  6. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/cfg.py +8 -1
  7. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/httpcli.py +11 -5
  8. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/up2k.py +51 -3
  9. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/a/u2c.py +6 -3
  10. copyparty-1.16.15/copyparty/web/browser.css.gz +0 -0
  11. copyparty-1.16.15/copyparty/web/browser.js.gz +0 -0
  12. copyparty-1.16.15/copyparty/web/md2.js.gz +0 -0
  13. copyparty-1.16.15/copyparty/web/up2k.js.gz +0 -0
  14. copyparty-1.16.15/copyparty/web/w.hash.js.gz +0 -0
  15. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/PKG-INFO +59 -4
  16. copyparty-1.16.13/copyparty/web/browser.css.gz +0 -0
  17. copyparty-1.16.13/copyparty/web/browser.js.gz +0 -0
  18. copyparty-1.16.13/copyparty/web/md2.js.gz +0 -0
  19. copyparty-1.16.13/copyparty/web/up2k.js.gz +0 -0
  20. copyparty-1.16.13/copyparty/web/w.hash.js.gz +0 -0
  21. {copyparty-1.16.13 → copyparty-1.16.15}/LICENSE +0 -0
  22. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/__init__.py +0 -0
  23. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/bos/__init__.py +0 -0
  24. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/bos/bos.py +0 -0
  25. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/bos/path.py +0 -0
  26. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/broker_mp.py +0 -0
  27. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/broker_mpw.py +0 -0
  28. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/broker_thr.py +0 -0
  29. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/broker_util.py +0 -0
  30. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/cert.py +0 -0
  31. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/dxml.py +0 -0
  32. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/fsutil.py +0 -0
  33. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/ftpd.py +0 -0
  34. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/httpconn.py +0 -0
  35. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/httpsrv.py +0 -0
  36. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/ico.py +0 -0
  37. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/mdns.py +0 -0
  38. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/metrics.py +0 -0
  39. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/mtag.py +0 -0
  40. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/multicast.py +0 -0
  41. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/pwhash.py +0 -0
  42. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/res/COPYING.txt +0 -0
  43. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/res/__init__.py +0 -0
  44. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/res/insecure.pem +0 -0
  45. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/smbd.py +0 -0
  46. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/ssdp.py +0 -0
  47. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/star.py +0 -0
  48. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/__init__.py +0 -0
  49. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/__init__.py +0 -0
  50. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/bimap.py +0 -0
  51. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/bit.py +0 -0
  52. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/buffer.py +0 -0
  53. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/dns.py +0 -0
  54. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/label.py +0 -0
  55. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/lex.py +0 -0
  56. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/dnslib/ranges.py +0 -0
  57. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/ifaddr/__init__.py +0 -0
  58. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/ifaddr/_posix.py +0 -0
  59. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/ifaddr/_shared.py +0 -0
  60. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/ifaddr/_win32.py +0 -0
  61. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/qrcodegen.py +0 -0
  62. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/stolen/surrogateescape.py +0 -0
  63. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/sutil.py +0 -0
  64. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/svchub.py +0 -0
  65. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/szip.py +0 -0
  66. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/tcpsrv.py +0 -0
  67. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/tftpd.py +0 -0
  68. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/th_cli.py +0 -0
  69. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/th_srv.py +0 -0
  70. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/u2idx.py +0 -0
  71. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/util.py +0 -0
  72. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/a/__init__.py +0 -0
  73. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/a/partyfuse.py +0 -0
  74. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/a/webdav-cfg.bat +0 -0
  75. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/baguettebox.js.gz +0 -0
  76. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/browser.html +0 -0
  77. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/browser2.html +0 -0
  78. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/cf.html +0 -0
  79. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dbg-audio.js.gz +0 -0
  80. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dd/2.png +0 -0
  81. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dd/3.png +0 -0
  82. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dd/4.png +0 -0
  83. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dd/5.png +0 -0
  84. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/dd/__init__.py +0 -0
  85. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/__init__.py +0 -0
  86. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/busy.mp3.gz +0 -0
  87. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/easymde.css.gz +0 -0
  88. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/easymde.js.gz +0 -0
  89. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/fuse.py +0 -0
  90. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/marked.js.gz +0 -0
  91. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/mini-fa.css.gz +0 -0
  92. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/mini-fa.woff +0 -0
  93. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/prism.css.gz +0 -0
  94. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/prism.js.gz +0 -0
  95. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/prismd.css.gz +0 -0
  96. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/scp.woff2 +0 -0
  97. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  98. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  99. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/md.css.gz +0 -0
  100. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/md.html +0 -0
  101. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/md.js.gz +0 -0
  102. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/md2.css.gz +0 -0
  103. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/mde.css.gz +0 -0
  104. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/mde.html +0 -0
  105. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/mde.js.gz +0 -0
  106. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/msg.css.gz +0 -0
  107. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/msg.html +0 -0
  108. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/rups.css.gz +0 -0
  109. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/rups.html +0 -0
  110. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/rups.js.gz +0 -0
  111. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/shares.css.gz +0 -0
  112. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/shares.html +0 -0
  113. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/shares.js.gz +0 -0
  114. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/splash.css.gz +0 -0
  115. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/splash.html +0 -0
  116. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/splash.js.gz +0 -0
  117. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/svcs.html +0 -0
  118. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/svcs.js.gz +0 -0
  119. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/ui.css.gz +0 -0
  120. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty/web/util.js.gz +0 -0
  121. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/SOURCES.txt +0 -0
  122. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/dependency_links.txt +0 -0
  123. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/entry_points.txt +0 -0
  124. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/requires.txt +0 -0
  125. {copyparty-1.16.13 → copyparty-1.16.15}/copyparty.egg-info/top_level.txt +0 -0
  126. {copyparty-1.16.13 → copyparty-1.16.15}/pyproject.toml +0 -0
  127. {copyparty-1.16.13 → copyparty-1.16.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: copyparty
3
- Version: 1.16.13
3
+ Version: 1.16.15
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
@@ -151,9 +151,11 @@ turn almost any device into a file server with resumable uploads/downloads using
151
151
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
152
152
  * [real-ip](#real-ip) - teaching copyparty how to see client IPs
153
153
  * [reverse-proxy performance](#reverse-proxy-performance)
154
+ * [permanent cloudflare tunnel](#permanent-cloudflare-tunnel) - if you have a domain and want to get your copyparty online real quick
154
155
  * [prometheus](#prometheus) - metrics/stats can be enabled
155
156
  * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
156
157
  * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
158
+ * [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
157
159
  * [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
158
160
  * [packages](#packages) - the party might be closer than you think
159
161
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
@@ -216,8 +218,8 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
216
218
  * **MacOS:** `port install py-Pillow ffmpeg`
217
219
  * **MacOS** (alternative): `brew install pillow ffmpeg`
218
220
  * **Windows:** `python -m pip install --user -U Pillow`
219
- * install python and ffmpeg manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
220
- * copyparty.exe comes with `Pillow` and only needs `ffmpeg`
221
+ * install [python](https://www.python.org/downloads/windows/) and [ffmpeg](#optional-dependencies) manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
222
+ * copyparty.exe comes with `Pillow` and only needs [ffmpeg](#optional-dependencies) for mediatags/videothumbs
221
223
  * see [optional dependencies](#optional-dependencies) to enable even more features
222
224
 
223
225
  running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)
@@ -240,6 +242,8 @@ first download [cloudflared](https://developers.cloudflare.com/cloudflare-one/co
240
242
 
241
243
  as the tunnel starts, it will show a URL which you can share to let anyone browse your stash or upload files to you
242
244
 
245
+ but if you have a domain, then you probably want to skip the random autogenerated URL and instead make a [permanent cloudflare tunnel](#permanent-cloudflare-tunnel)
246
+
243
247
  since people will be connecting through cloudflare, run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
244
248
 
245
249
 
@@ -281,6 +285,7 @@ also see [comparison to similar software](./docs/versus.md)
281
285
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
282
286
  * ☑ [event hooks](#event-hooks) / script runner
283
287
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
288
+ * ☑ cross-platform (Windows, Linux, Macos, Android, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
284
289
  * upload
285
290
  * ☑ basic: plain multipart, ie6 support
286
291
  * ☑ [up2k](#uploading): js, resumable, multithreaded
@@ -458,6 +463,9 @@ upgrade notes
458
463
 
459
464
  "frequently" asked questions
460
465
 
466
+ * can I change the 🌲 spinning pine-tree loading animation?
467
+ * [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
468
+
461
469
  * is it possible to block read-access to folders unless you know the exact URL for a particular file inside?
462
470
  * yes, using the [`g` permission](#accounts-and-volumes), see the examples there
463
471
  * you can also do this with linux filesystem permissions; `chmod 111 music` will make it possible to access files and folders inside the `music` folder but not list the immediate contents -- also works with other software, not just copyparty
@@ -480,6 +488,14 @@ upgrade notes
480
488
  * copyparty seems to think I am using http, even though the URL is https
481
489
  * your reverse-proxy is not sending the `X-Forwarded-Proto: https` header; this could be because your reverse-proxy itself is confused. Ensure that none of the intermediates (such as cloudflare) are terminating https before the traffic hits your entrypoint
482
490
 
491
+ * thumbnails are broken (you get a colorful square which says the filetype instead)
492
+ * you need to install `FFmpeg` or `Pillow`; see [thumbnails](#thumbnails)
493
+
494
+ * thumbnails are broken (some images appear, but other files just get a blank box, and/or the broken-image placeholder)
495
+ * probably due to a reverse-proxy messing with the request URLs and stripping the query parameters (`?th=w`), so check your URL rewrite rules
496
+ * could also be due to incorrect caching settings in reverse-proxies and/or CDNs, so make sure that nothing is set to ignore the query string
497
+ * could also be due to misbehaving privacy-related browser extensions, so try to disable those
498
+
483
499
  * i want to learn python and/or programming and am considering looking at the copyparty source code in that occasion
484
500
  * ```bash
485
501
  _| _ __ _ _|_
@@ -710,6 +726,7 @@ press `g` or `田` to toggle grid-view instead of the file listing and `t` togg
710
726
  it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video files, so you may want to `--no-thumb` or maybe just `--no-vthumb` depending on how dangerous your users are
711
727
  * pyvips is 3x faster than Pillow, Pillow is 3x faster than FFmpeg
712
728
  * disable thumbnails for specific volumes with volflag `dthumb` for all, or `dvthumb` / `dathumb` / `dithumb` for video/audio/images only
729
+ * for installing FFmpeg on windows, see [optional dependencies](#optional-dependencies)
713
730
 
714
731
  audio files are converted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
715
732
 
@@ -821,8 +838,11 @@ the up2k UI is the epitome of polished intuitive experiences:
821
838
  * "parallel uploads" specifies how many chunks to upload at the same time
822
839
  * `[🏃]` analysis of other files should continue while one is uploading
823
840
  * `[🥔]` shows a simpler UI for faster uploads from slow devices
841
+ * `[🛡️]` decides when to overwrite existing files on the server
842
+ * `🛡️` = never (generate a new filename instead)
843
+ * `🕒` = overwrite if the server-file is older
844
+ * `♻️` = always overwrite if the files are different
824
845
  * `[🎲]` generate random filenames during upload
825
- * `[📅]` preserve last-modified timestamps; server times will match yours
826
846
  * `[🔎]` switch between upload and [file-search](#file-search) mode
827
847
  * ignore `[🔎]` if you add files by dragging them into the browser
828
848
 
@@ -2039,6 +2059,26 @@ in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds
2039
2059
  * if these results are bullshit because my config exampels are bad, please submit corrections!
2040
2060
 
2041
2061
 
2062
+ ## permanent cloudflare tunnel
2063
+
2064
+ if you have a domain and want to get your copyparty online real quick, either from your home-PC behind a CGNAT or from a server without an existing [reverse-proxy](#reverse-proxy) setup, one approach is to create a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/) (formerly "Argo Tunnel")
2065
+
2066
+ I'd recommend making a `Locally-managed tunnel` for more control, but if you prefer to make a `Remotely-managed tunnel` then this is currently how:
2067
+
2068
+ * `cloudflare dashboard` » `zero trust` » `networks` » `tunnels` » `create a tunnel` » `cloudflared` » choose a cool `subdomain` and leave the `path` blank, and use `service type` = `http` and `URL` = `127.0.0.1:3923`
2069
+
2070
+ * and if you want to just run the tunnel without installing it, skip the `cloudflared service install BASE64` step and instead do `cloudflared --no-autoupdate tunnel run --token BASE64`
2071
+
2072
+ NOTE: since people will be connecting through cloudflare, as mentioned in [real-ip](#real-ip) you should run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
2073
+
2074
+ config file example:
2075
+
2076
+ ```yaml
2077
+ [global]
2078
+ xff-hdr: cf-connecting-ip
2079
+ ```
2080
+
2081
+
2042
2082
  ## prometheus
2043
2083
 
2044
2084
  metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
@@ -2125,6 +2165,18 @@ in a config file, this is the same as:
2125
2165
  run copyparty with `--mimes` to list all the default mappings
2126
2166
 
2127
2167
 
2168
+ ### GDPR compliance
2169
+
2170
+ imagine using copyparty professionally... **TINLA/IANAL; EU laws are hella confusing**
2171
+
2172
+ * remember to disable logging, or configure logrotation to an acceptable timeframe with `-lo cpp-%Y-%m%d.txt.xz` or similar
2173
+
2174
+ * if running with the database enabled (recommended), then have it forget uploader-IPs after some time using `--forget-ip 43200`
2175
+ * don't set it too low; [unposting](#unpost) a file is no longer possible after this takes effect
2176
+
2177
+ * if you actually *are* a lawyer then I'm open for feedback, would be fun
2178
+
2179
+
2128
2180
  ### feature chickenbits
2129
2181
 
2130
2182
  buggy feature? rip it out by setting any of the following environment variables to disable its associated bell or whistle,
@@ -2312,6 +2364,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
2312
2364
  | **ie4** and **netscape** 4.0 | can browse, upload with `?b=u`, auth with `&pw=wark` |
2313
2365
  | **ncsa mosaic** 2.7 | does not get a pass, [pic1](https://user-images.githubusercontent.com/241032/174189227-ae816026-cf6f-4be5-a26e-1b3b072c1b2f.png) - [pic2](https://user-images.githubusercontent.com/241032/174189225-5651c059-5152-46e9-ac26-7e98e497901b.png) |
2314
2366
  | **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
2367
+ | **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
2315
2368
  | **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
2316
2369
 
2317
2370
  <p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
@@ -2642,6 +2695,8 @@ enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
2642
2695
 
2643
2696
  `pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
2644
2697
 
2698
+ to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
2699
+
2645
2700
 
2646
2701
  ### dependency chickenbits
2647
2702
 
@@ -94,9 +94,11 @@ turn almost any device into a file server with resumable uploads/downloads using
94
94
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
95
95
  * [real-ip](#real-ip) - teaching copyparty how to see client IPs
96
96
  * [reverse-proxy performance](#reverse-proxy-performance)
97
+ * [permanent cloudflare tunnel](#permanent-cloudflare-tunnel) - if you have a domain and want to get your copyparty online real quick
97
98
  * [prometheus](#prometheus) - metrics/stats can be enabled
98
99
  * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
99
100
  * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
101
+ * [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
100
102
  * [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
101
103
  * [packages](#packages) - the party might be closer than you think
102
104
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
@@ -159,8 +161,8 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
159
161
  * **MacOS:** `port install py-Pillow ffmpeg`
160
162
  * **MacOS** (alternative): `brew install pillow ffmpeg`
161
163
  * **Windows:** `python -m pip install --user -U Pillow`
162
- * install python and ffmpeg manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
163
- * copyparty.exe comes with `Pillow` and only needs `ffmpeg`
164
+ * install [python](https://www.python.org/downloads/windows/) and [ffmpeg](#optional-dependencies) manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
165
+ * copyparty.exe comes with `Pillow` and only needs [ffmpeg](#optional-dependencies) for mediatags/videothumbs
164
166
  * see [optional dependencies](#optional-dependencies) to enable even more features
165
167
 
166
168
  running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)
@@ -183,6 +185,8 @@ first download [cloudflared](https://developers.cloudflare.com/cloudflare-one/co
183
185
 
184
186
  as the tunnel starts, it will show a URL which you can share to let anyone browse your stash or upload files to you
185
187
 
188
+ but if you have a domain, then you probably want to skip the random autogenerated URL and instead make a [permanent cloudflare tunnel](#permanent-cloudflare-tunnel)
189
+
186
190
  since people will be connecting through cloudflare, run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
187
191
 
188
192
 
@@ -224,6 +228,7 @@ also see [comparison to similar software](./docs/versus.md)
224
228
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
225
229
  * ☑ [event hooks](#event-hooks) / script runner
226
230
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
231
+ * ☑ cross-platform (Windows, Linux, Macos, Android, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
227
232
  * upload
228
233
  * ☑ basic: plain multipart, ie6 support
229
234
  * ☑ [up2k](#uploading): js, resumable, multithreaded
@@ -401,6 +406,9 @@ upgrade notes
401
406
 
402
407
  "frequently" asked questions
403
408
 
409
+ * can I change the 🌲 spinning pine-tree loading animation?
410
+ * [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
411
+
404
412
  * is it possible to block read-access to folders unless you know the exact URL for a particular file inside?
405
413
  * yes, using the [`g` permission](#accounts-and-volumes), see the examples there
406
414
  * you can also do this with linux filesystem permissions; `chmod 111 music` will make it possible to access files and folders inside the `music` folder but not list the immediate contents -- also works with other software, not just copyparty
@@ -423,6 +431,14 @@ upgrade notes
423
431
  * copyparty seems to think I am using http, even though the URL is https
424
432
  * your reverse-proxy is not sending the `X-Forwarded-Proto: https` header; this could be because your reverse-proxy itself is confused. Ensure that none of the intermediates (such as cloudflare) are terminating https before the traffic hits your entrypoint
425
433
 
434
+ * thumbnails are broken (you get a colorful square which says the filetype instead)
435
+ * you need to install `FFmpeg` or `Pillow`; see [thumbnails](#thumbnails)
436
+
437
+ * thumbnails are broken (some images appear, but other files just get a blank box, and/or the broken-image placeholder)
438
+ * probably due to a reverse-proxy messing with the request URLs and stripping the query parameters (`?th=w`), so check your URL rewrite rules
439
+ * could also be due to incorrect caching settings in reverse-proxies and/or CDNs, so make sure that nothing is set to ignore the query string
440
+ * could also be due to misbehaving privacy-related browser extensions, so try to disable those
441
+
426
442
  * i want to learn python and/or programming and am considering looking at the copyparty source code in that occasion
427
443
  * ```bash
428
444
  _| _ __ _ _|_
@@ -653,6 +669,7 @@ press `g` or `田` to toggle grid-view instead of the file listing and `t` togg
653
669
  it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video files, so you may want to `--no-thumb` or maybe just `--no-vthumb` depending on how dangerous your users are
654
670
  * pyvips is 3x faster than Pillow, Pillow is 3x faster than FFmpeg
655
671
  * disable thumbnails for specific volumes with volflag `dthumb` for all, or `dvthumb` / `dathumb` / `dithumb` for video/audio/images only
672
+ * for installing FFmpeg on windows, see [optional dependencies](#optional-dependencies)
656
673
 
657
674
  audio files are converted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
658
675
 
@@ -764,8 +781,11 @@ the up2k UI is the epitome of polished intuitive experiences:
764
781
  * "parallel uploads" specifies how many chunks to upload at the same time
765
782
  * `[🏃]` analysis of other files should continue while one is uploading
766
783
  * `[🥔]` shows a simpler UI for faster uploads from slow devices
784
+ * `[🛡️]` decides when to overwrite existing files on the server
785
+ * `🛡️` = never (generate a new filename instead)
786
+ * `🕒` = overwrite if the server-file is older
787
+ * `♻️` = always overwrite if the files are different
767
788
  * `[🎲]` generate random filenames during upload
768
- * `[📅]` preserve last-modified timestamps; server times will match yours
769
789
  * `[🔎]` switch between upload and [file-search](#file-search) mode
770
790
  * ignore `[🔎]` if you add files by dragging them into the browser
771
791
 
@@ -1982,6 +2002,26 @@ in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds
1982
2002
  * if these results are bullshit because my config exampels are bad, please submit corrections!
1983
2003
 
1984
2004
 
2005
+ ## permanent cloudflare tunnel
2006
+
2007
+ if you have a domain and want to get your copyparty online real quick, either from your home-PC behind a CGNAT or from a server without an existing [reverse-proxy](#reverse-proxy) setup, one approach is to create a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/) (formerly "Argo Tunnel")
2008
+
2009
+ I'd recommend making a `Locally-managed tunnel` for more control, but if you prefer to make a `Remotely-managed tunnel` then this is currently how:
2010
+
2011
+ * `cloudflare dashboard` » `zero trust` » `networks` » `tunnels` » `create a tunnel` » `cloudflared` » choose a cool `subdomain` and leave the `path` blank, and use `service type` = `http` and `URL` = `127.0.0.1:3923`
2012
+
2013
+ * and if you want to just run the tunnel without installing it, skip the `cloudflared service install BASE64` step and instead do `cloudflared --no-autoupdate tunnel run --token BASE64`
2014
+
2015
+ NOTE: since people will be connecting through cloudflare, as mentioned in [real-ip](#real-ip) you should run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
2016
+
2017
+ config file example:
2018
+
2019
+ ```yaml
2020
+ [global]
2021
+ xff-hdr: cf-connecting-ip
2022
+ ```
2023
+
2024
+
1985
2025
  ## prometheus
1986
2026
 
1987
2027
  metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
@@ -2068,6 +2108,18 @@ in a config file, this is the same as:
2068
2108
  run copyparty with `--mimes` to list all the default mappings
2069
2109
 
2070
2110
 
2111
+ ### GDPR compliance
2112
+
2113
+ imagine using copyparty professionally... **TINLA/IANAL; EU laws are hella confusing**
2114
+
2115
+ * remember to disable logging, or configure logrotation to an acceptable timeframe with `-lo cpp-%Y-%m%d.txt.xz` or similar
2116
+
2117
+ * if running with the database enabled (recommended), then have it forget uploader-IPs after some time using `--forget-ip 43200`
2118
+ * don't set it too low; [unposting](#unpost) a file is no longer possible after this takes effect
2119
+
2120
+ * if you actually *are* a lawyer then I'm open for feedback, would be fun
2121
+
2122
+
2071
2123
  ### feature chickenbits
2072
2124
 
2073
2125
  buggy feature? rip it out by setting any of the following environment variables to disable its associated bell or whistle,
@@ -2255,6 +2307,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
2255
2307
  | **ie4** and **netscape** 4.0 | can browse, upload with `?b=u`, auth with `&pw=wark` |
2256
2308
  | **ncsa mosaic** 2.7 | does not get a pass, [pic1](https://user-images.githubusercontent.com/241032/174189227-ae816026-cf6f-4be5-a26e-1b3b072c1b2f.png) - [pic2](https://user-images.githubusercontent.com/241032/174189225-5651c059-5152-46e9-ac26-7e98e497901b.png) |
2257
2309
  | **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
2310
+ | **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
2258
2311
  | **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
2259
2312
 
2260
2313
  <p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
@@ -2585,6 +2638,8 @@ enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
2585
2638
 
2586
2639
  `pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
2587
2640
 
2641
+ to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
2642
+
2588
2643
 
2589
2644
  ### dependency chickenbits
2590
2645
 
@@ -1031,6 +1031,7 @@ def add_upload(ap):
1031
1031
  ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
1032
1032
  ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good for low-latency (same-country) connections, 4-8 for android clients, 16 for cross-atlantic (max=64)")
1033
1033
  ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
1034
+ ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)")
1034
1035
  ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
1035
1036
  ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory")
1036
1037
 
@@ -1261,7 +1262,7 @@ def add_optouts(ap):
1261
1262
  ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
1262
1263
  ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
1263
1264
  ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1264
- ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
1265
+ ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader-IP into the database; will also disable unpost, you may want \033[32m--forget-ip\033[0m instead (volflag=no_db_ip)")
1265
1266
 
1266
1267
 
1267
1268
  def add_safety(ap):
@@ -1411,6 +1412,7 @@ def add_db_general(ap, hcores):
1411
1412
  ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower")
1412
1413
  ap2.add_argument("--re-dhash", action="store_true", help="force a cache rebuild on startup; enable this once if it gets out of sync (should never be necessary)")
1413
1414
  ap2.add_argument("--no-forget", action="store_true", help="never forget indexed files, even when deleted from disk -- makes it impossible to ever upload the same file twice -- only useful for offloading uploads to a cloud service or something (volflag=noforget)")
1415
+ ap2.add_argument("--forget-ip", metavar="MIN", type=int, default=0, help="remove uploader-IP from database (and make unpost impossible) \033[33mMIN\033[0m minutes after upload, for GDPR reasons. Default [\033[32m0\033[0m] is never-forget. [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week, [\033[32m43200\033[0m]=month. (volflag=forget_ip)")
1414
1416
  ap2.add_argument("--dbd", metavar="PROFILE", default="wal", help="database durability profile; sets the tradeoff between robustness and speed, see \033[33m--help-dbd\033[0m (volflag=dbd)")
1415
1417
  ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (probably buggy, not recommended) (volflag=xlink)")
1416
1418
  ap2.add_argument("--hash-mt", metavar="CORES", type=int, default=hcores, help="num cpu cores to use for file hashing; set 0 or 1 for single-core hashing")
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 16, 13)
3
+ VERSION = (1, 16, 15)
4
4
  CODENAME = "COPYparty"
5
- BUILD_DT = (2025, 2, 13)
5
+ BUILD_DT = (2025, 2, 25)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -1382,8 +1382,16 @@ class AuthSrv(object):
1382
1382
  name = name.lower()
1383
1383
 
1384
1384
  # volflags are snake_case, but a leading dash is the removal operator
1385
- if name not in flagdescs and "-" in name[1:]:
1386
- name = name[:1] + name[1:].replace("-", "_")
1385
+ stripped = name.lstrip("-")
1386
+ zi = len(name) - len(stripped)
1387
+ if zi > 1:
1388
+ t = "WARNING: the config for volume [/%s] specified a volflag with multiple leading hyphens (%s); use one hyphen to remove, or zero hyphens to add a flag. Will now enable flag [%s]"
1389
+ self.log(t % (vpath, name, stripped), 3)
1390
+ name = stripped
1391
+ zi = 0
1392
+
1393
+ if stripped not in flagdescs and "-" in stripped:
1394
+ name = ("-" * zi) + stripped.replace("-", "_")
1387
1395
 
1388
1396
  desc = flagdescs.get(name.lstrip("-"), "?").replace("\n", " ")
1389
1397
 
@@ -1569,6 +1577,11 @@ class AuthSrv(object):
1569
1577
  for vol in vfs.all_vols.values():
1570
1578
  unknown_flags = set()
1571
1579
  for k, v in vol.flags.items():
1580
+ stripped = k.lstrip("-")
1581
+ if k != stripped and stripped not in vol.flags:
1582
+ t = "WARNING: the config for volume [/%s] tried to remove volflag [%s] by specifying [%s] but that volflag was not already set"
1583
+ self.log(t % (vol.vpath, stripped, k), 3)
1584
+ k = stripped
1572
1585
  if k not in flagdescs and k not in k_ign:
1573
1586
  unknown_flags.add(k)
1574
1587
  if unknown_flags:
@@ -1936,11 +1949,8 @@ class AuthSrv(object):
1936
1949
  if vf not in vol.flags:
1937
1950
  vol.flags[vf] = getattr(self.args, ga)
1938
1951
 
1939
- for k in ("nrand",):
1940
- if k not in vol.flags:
1941
- vol.flags[k] = getattr(self.args, k)
1942
-
1943
- for k in ("nrand", "u2abort", "ups_who", "zip_who"):
1952
+ zs = "forget_ip nrand u2abort u2ow ups_who zip_who"
1953
+ for k in zs.split():
1944
1954
  if k in vol.flags:
1945
1955
  vol.flags[k] = int(vol.flags[k])
1946
1956
 
@@ -2415,6 +2425,7 @@ class AuthSrv(object):
2415
2425
  "u2j": self.args.u2j,
2416
2426
  "u2sz": self.args.u2sz,
2417
2427
  "u2ts": vf["u2ts"],
2428
+ "u2ow": vf["u2ow"],
2418
2429
  "frand": bool(vf.get("rand")),
2419
2430
  "lifetime": vn.js_ls["lifetime"],
2420
2431
  "u2sort": self.args.u2sort,
@@ -43,6 +43,7 @@ def vf_bmap() :
43
43
  "gsel",
44
44
  "hardlink",
45
45
  "magic",
46
+ "no_db_ip",
46
47
  "no_sb_md",
47
48
  "no_sb_lg",
48
49
  "nsort",
@@ -73,6 +74,7 @@ def vf_vmap() :
73
74
  }
74
75
  for k in (
75
76
  "dbd",
77
+ "forget_ip",
76
78
  "hsortn",
77
79
  "html_head",
78
80
  "lg_sbf",
@@ -80,6 +82,7 @@ def vf_vmap() :
80
82
  "lg_sba",
81
83
  "md_sba",
82
84
  "nrand",
85
+ "u2ow",
83
86
  "og_desc",
84
87
  "og_site",
85
88
  "og_th",
@@ -156,7 +159,8 @@ flagcats = {
156
159
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
157
160
  "nosub": "forces all uploads into the top folder of the vfs",
158
161
  "magic": "enables filetype detection for nameless uploads",
159
- "gz": "allows server-side gzip of uploads with ?gz (also c,xz)",
162
+ "gz": "allows server-side gzip compression of uploads with ?gz",
163
+ "xz": "allows server-side lzma compression of uploads with ?xz",
160
164
  "pk": "forces server-side compression, optional arg: xz,9",
161
165
  },
162
166
  "upload rules": {
@@ -167,6 +171,7 @@ flagcats = {
167
171
  "medialinks": "return medialinks for non-up2k uploads (not hotlinks)",
168
172
  "rand": "force randomized filenames, 9 chars long by default",
169
173
  "nrand=N": "randomized filenames are N chars long",
174
+ "u2ow=N": "overwrite existing files? 0=no 1=if-older 2=always",
170
175
  "u2ts=fc": "[f]orce [c]lient-last-modified or [u]pload-time",
171
176
  "u2abort=1": "allow aborting unfinished uploads? 0=no 1=strict 2=ip-chk 3=acct-chk",
172
177
  "sz=1k-3m": "allow filesizes between 1 KiB and 3MiB",
@@ -197,6 +202,8 @@ flagcats = {
197
202
  "nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
198
203
  "noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",
199
204
  "noforget": "don't forget files when deleted from disk",
205
+ "forget_ip=43200": "forget uploader-IP after 30 days (GDPR)",
206
+ "no_db_ip": "never store uploader-IP in the db; disables unpost",
200
207
  "fat32": "avoid excessive reindexing on android sdcardfs",
201
208
  "dbd=[acid|swal|wal|yolo]": "database speed-durability tradeoff",
202
209
  "xlink": "cross-volume dupe detection / linking (dangerous)",
@@ -148,6 +148,8 @@ RE_HSAFE = re.compile(r"[\x00-\x1f<>\"'&]") # search always much faster
148
148
  RE_HOST = re.compile(r"[^][0-9a-zA-Z.:_-]") # search faster <=17ch
149
149
  RE_MHOST = re.compile(r"^[][0-9a-zA-Z.:_-]+$") # match faster >=18ch
150
150
  RE_K = re.compile(r"[^0-9a-zA-Z_-]") # search faster <=17ch
151
+ RE_HR = re.compile(r"[<>\"'&]")
152
+ RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
151
153
 
152
154
  UPARAM_CC_OK = set("doc move tree".split())
153
155
 
@@ -1803,7 +1805,8 @@ class HttpCli(object):
1803
1805
  dst = unquotep(dst)
1804
1806
 
1805
1807
  # overwrite=True is default; rfc4918 9.8.4
1806
- overwrite = self.headers.get("overwrite", "").lower() != "f"
1808
+ zs = self.headers.get("overwrite", "").lower()
1809
+ overwrite = zs not in ["f", "false"]
1807
1810
 
1808
1811
  try:
1809
1812
  fun = self._cp if self.mode == "COPY" else self._mv
@@ -5946,7 +5949,7 @@ class HttpCli(object):
5946
5949
  # [num-backups, most-recent, hist-path]
5947
5950
  hist = {}
5948
5951
  histdir = os.path.join(fsroot, ".hist")
5949
- ptn = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[^\.]+)$")
5952
+ ptn = RE_MDV
5950
5953
  try:
5951
5954
  for hfn in bos.listdir(histdir):
5952
5955
  m = ptn.match(hfn)
@@ -5979,6 +5982,7 @@ class HttpCli(object):
5979
5982
 
5980
5983
  dirs = []
5981
5984
  files = []
5985
+ ptn_hr = RE_HR
5982
5986
  for fn in ls_names:
5983
5987
  base = ""
5984
5988
  href = fn
@@ -6033,11 +6037,13 @@ class HttpCli(object):
6033
6037
  zd.second,
6034
6038
  )
6035
6039
 
6036
- try:
6037
- ext = "---" if is_dir else fn.rsplit(".", 1)[1]
6040
+ if is_dir:
6041
+ ext = "---"
6042
+ elif "." in fn:
6043
+ ext = ptn_hr.sub("@", fn.rsplit(".", 1)[1])
6038
6044
  if len(ext) > 16:
6039
6045
  ext = ext[:16]
6040
- except:
6046
+ else:
6041
6047
  ext = "%"
6042
6048
 
6043
6049
  if add_fk and not is_dir:
@@ -553,6 +553,7 @@ class Up2k(object):
553
553
  else:
554
554
  # important; not deferred by db_act
555
555
  timeout = self._check_lifetimes()
556
+ timeout = min(self._check_forget_ip(), timeout)
556
557
  try:
557
558
  if self.args.shr:
558
559
  timeout = min(self._check_shares(), timeout)
@@ -613,6 +614,43 @@ class Up2k(object):
613
614
  for v in vols:
614
615
  volage[v] = now
615
616
 
617
+ def _check_forget_ip(self) :
618
+ now = time.time()
619
+ timeout = now + 9001
620
+ for vp, vol in sorted(self.vfs.all_vols.items()):
621
+ maxage = vol.flags["forget_ip"]
622
+ if not maxage:
623
+ continue
624
+
625
+ cur = self.cur.get(vol.realpath)
626
+ if not cur:
627
+ continue
628
+
629
+ cutoff = now - maxage * 60
630
+
631
+ for _ in range(2):
632
+ q = "select ip, at from up where ip > '' order by +at limit 1"
633
+ hits = cur.execute(q).fetchall()
634
+ if not hits:
635
+ break
636
+
637
+ remains = hits[0][1] - cutoff
638
+ if remains > 0:
639
+ timeout = min(timeout, now + remains)
640
+ break
641
+
642
+ q = "update up set ip = '' where ip > '' and at <= %d"
643
+ cur.execute(q % (cutoff,))
644
+ zi = cur.rowcount
645
+ cur.connection.commit()
646
+
647
+ t = "forget-ip(%d) removed %d IPs from db [/%s]"
648
+ self.log(t % (maxage, zi, vol.vpath))
649
+
650
+ timeout = min(timeout, now + 900)
651
+
652
+ return timeout
653
+
616
654
  def _check_lifetimes(self) :
617
655
  now = time.time()
618
656
  timeout = now + 9001
@@ -1074,7 +1112,7 @@ class Up2k(object):
1074
1112
  ft = "\033[0;32m{}{:.0}"
1075
1113
  ff = "\033[0;35m{}{:.0}"
1076
1114
  fv = "\033[0;36m{}:\033[90m{}"
1077
- zs = "html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
1115
+ zs = "ext_th_d html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
1078
1116
  fx = set(zs.split())
1079
1117
  fd = vf_bmap()
1080
1118
  fd.update(vf_cmap())
@@ -3319,7 +3357,17 @@ class Up2k(object):
3319
3357
  return fname
3320
3358
 
3321
3359
  fp = djoin(fdir, fname)
3322
- if job.get("replace") and bos.path.exists(fp):
3360
+
3361
+ ow = job.get("replace") and bos.path.exists(fp)
3362
+ if ow and "mt" in str(job["replace"]).lower():
3363
+ mts = bos.stat(fp).st_mtime
3364
+ mtc = job["lmod"]
3365
+ if mtc < mts:
3366
+ t = "will not overwrite; server %d sec newer than client; %d > %d %r"
3367
+ self.log(t % (mts - mtc, mts, mtc, fp))
3368
+ ow = False
3369
+
3370
+ if ow:
3323
3371
  self.log("replacing existing file at %r" % (fp,))
3324
3372
  cur = None
3325
3373
  ptop = job["ptop"]
@@ -3770,7 +3818,7 @@ class Up2k(object):
3770
3818
  db_ip = ""
3771
3819
  else:
3772
3820
  # plugins may expect this to look like an actual IP
3773
- db_ip = "1.1.1.1" if self.args.no_db_ip else ip
3821
+ db_ip = "1.1.1.1" if "no_db_ip" in vflags else ip
3774
3822
 
3775
3823
  sql = "insert into up values (?,?,?,?,?,?,?)"
3776
3824
  v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
- S_VERSION = "2.9"
5
- S_BUILD_DT = "2025-01-27"
4
+ S_VERSION = "2.10"
5
+ S_BUILD_DT = "2025-02-19"
6
6
 
7
7
  """
8
8
  u2c.py: upload to copyparty
@@ -805,7 +805,9 @@ def handshake(ar, file, search):
805
805
  else:
806
806
  if ar.touch:
807
807
  req["umod"] = True
808
- if ar.ow:
808
+ if ar.owo:
809
+ req["replace"] = "mt"
810
+ elif ar.ow:
809
811
  req["replace"] = True
810
812
 
811
813
  file.recheck = False
@@ -1536,6 +1538,7 @@ source file/folder selection uses rsync syntax, meaning that:
1536
1538
  ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
1537
1539
  ap.add_argument("--touch", action="store_true", help="if last-modified timestamps differ, push local to server (need write+delete perms)")
1538
1540
  ap.add_argument("--ow", action="store_true", help="overwrite existing files instead of autorenaming")
1541
+ ap.add_argument("--owo", action="store_true", help="overwrite existing files if server-file is older")
1539
1542
  ap.add_argument("--spd", action="store_true", help="print speeds for each file")
1540
1543
  ap.add_argument("--version", action="store_true", help="show version and exit")
1541
1544