copyparty 1.16.6__tar.gz → 1.16.8__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.6 → copyparty-1.16.8}/PKG-INFO +50 -7
  2. {copyparty-1.16.6 → copyparty-1.16.8}/README.md +48 -5
  3. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__version__.py +2 -2
  4. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/authsrv.py +2 -2
  5. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpcli.py +116 -53
  6. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpsrv.py +0 -4
  7. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/tcpsrv.py +3 -3
  8. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/up2k.py +21 -7
  9. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/u2c.py +1 -1
  10. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.html +1 -1
  11. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.js.gz +0 -0
  12. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.html +2 -2
  13. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.html +2 -2
  14. copyparty-1.16.8/copyparty/web/rups.css.gz +0 -0
  15. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/rups.html +8 -25
  16. copyparty-1.16.8/copyparty/web/rups.js.gz +0 -0
  17. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.html +4 -3
  18. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.css.gz +0 -0
  19. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.html +1 -1
  20. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/svcs.html +65 -4
  21. copyparty-1.16.8/copyparty/web/svcs.js.gz +0 -0
  22. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/ui.css.gz +0 -0
  23. copyparty-1.16.8/copyparty/web/up2k.js.gz +0 -0
  24. copyparty-1.16.8/copyparty/web/util.js.gz +0 -0
  25. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/PKG-INFO +50 -7
  26. copyparty-1.16.6/copyparty/web/rups.css.gz +0 -0
  27. copyparty-1.16.6/copyparty/web/rups.js.gz +0 -0
  28. copyparty-1.16.6/copyparty/web/svcs.js.gz +0 -0
  29. copyparty-1.16.6/copyparty/web/up2k.js.gz +0 -0
  30. copyparty-1.16.6/copyparty/web/util.js.gz +0 -0
  31. {copyparty-1.16.6 → copyparty-1.16.8}/LICENSE +0 -0
  32. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__init__.py +0 -0
  33. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__main__.py +0 -0
  34. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/__init__.py +0 -0
  35. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/bos.py +0 -0
  36. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/path.py +0 -0
  37. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_mp.py +0 -0
  38. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_mpw.py +0 -0
  39. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_thr.py +0 -0
  40. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_util.py +0 -0
  41. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/cert.py +0 -0
  42. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/cfg.py +0 -0
  43. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/dxml.py +0 -0
  44. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/fsutil.py +0 -0
  45. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ftpd.py +0 -0
  46. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpconn.py +0 -0
  47. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ico.py +0 -0
  48. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/mdns.py +0 -0
  49. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/metrics.py +0 -0
  50. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/mtag.py +0 -0
  51. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/multicast.py +0 -0
  52. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/pwhash.py +0 -0
  53. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/COPYING.txt +0 -0
  54. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/__init__.py +0 -0
  55. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/insecure.pem +0 -0
  56. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/smbd.py +0 -0
  57. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ssdp.py +0 -0
  58. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/star.py +0 -0
  59. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/__init__.py +0 -0
  60. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/__init__.py +0 -0
  61. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/bimap.py +0 -0
  62. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/bit.py +0 -0
  63. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/buffer.py +0 -0
  64. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/dns.py +0 -0
  65. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/label.py +0 -0
  66. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/lex.py +0 -0
  67. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/ranges.py +0 -0
  68. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/__init__.py +0 -0
  69. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_posix.py +0 -0
  70. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_shared.py +0 -0
  71. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_win32.py +0 -0
  72. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/qrcodegen.py +0 -0
  73. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/surrogateescape.py +0 -0
  74. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/sutil.py +0 -0
  75. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/svchub.py +0 -0
  76. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/szip.py +0 -0
  77. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/tftpd.py +0 -0
  78. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/th_cli.py +0 -0
  79. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/th_srv.py +0 -0
  80. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/u2idx.py +0 -0
  81. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/util.py +0 -0
  82. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/__init__.py +0 -0
  83. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/partyfuse.py +0 -0
  84. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/webdav-cfg.bat +0 -0
  85. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/baguettebox.js.gz +0 -0
  86. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.css.gz +0 -0
  87. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser2.html +0 -0
  88. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/cf.html +0 -0
  89. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dbg-audio.js.gz +0 -0
  90. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/2.png +0 -0
  91. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/3.png +0 -0
  92. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/4.png +0 -0
  93. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/5.png +0 -0
  94. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/__init__.py +0 -0
  95. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/__init__.py +0 -0
  96. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/busy.mp3.gz +0 -0
  97. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/easymde.css.gz +0 -0
  98. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/easymde.js.gz +0 -0
  99. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/fuse.py +0 -0
  100. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/marked.js.gz +0 -0
  101. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/mini-fa.css.gz +0 -0
  102. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/mini-fa.woff +0 -0
  103. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prism.css.gz +0 -0
  104. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prism.js.gz +0 -0
  105. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prismd.css.gz +0 -0
  106. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/scp.woff2 +0 -0
  107. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  108. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  109. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.css.gz +0 -0
  110. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.js.gz +0 -0
  111. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md2.css.gz +0 -0
  112. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md2.js.gz +0 -0
  113. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.css.gz +0 -0
  114. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.js.gz +0 -0
  115. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/msg.css.gz +0 -0
  116. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/msg.html +0 -0
  117. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.css.gz +0 -0
  118. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.js.gz +0 -0
  119. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.js.gz +0 -0
  120. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/w.hash.js.gz +0 -0
  121. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/SOURCES.txt +0 -0
  122. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/dependency_links.txt +0 -0
  123. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/entry_points.txt +0 -0
  124. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/requires.txt +0 -0
  125. {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/top_level.txt +0 -0
  126. {copyparty-1.16.6 → copyparty-1.16.8}/pyproject.toml +0 -0
  127. {copyparty-1.16.6 → copyparty-1.16.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: copyparty
3
- Version: 1.16.6
3
+ Version: 1.16.8
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
@@ -147,6 +147,7 @@ turn almost any device into a file server with resumable uploads/downloads using
147
147
  * [listen on port 80 and 443](#listen-on-port-80-and-443) - become a *real* webserver
148
148
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
149
149
  * [real-ip](#real-ip) - teaching copyparty how to see client IPs
150
+ * [reverse-proxy performance](#reverse-proxy-performance)
150
151
  * [prometheus](#prometheus) - metrics/stats can be enabled
151
152
  * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
152
153
  * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
@@ -195,6 +196,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
195
196
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
196
197
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
197
198
  * or if you are on android, [install copyparty in termux](#install-on-android)
199
+ * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
198
200
  * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
199
201
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
200
202
  * docker has all deps built-in, so skip this step:
@@ -701,7 +703,7 @@ dragdrop is the recommended way, but you may also:
701
703
 
702
704
  * select some files (not folders) in your file explorer and press CTRL-V inside the browser window
703
705
  * use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
704
- * upload using [curl or sharex](#client-examples)
706
+ * upload using [curl, sharex, ishare, ...](#client-examples)
705
707
 
706
708
  when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
707
709
  * `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
@@ -1159,6 +1161,8 @@ on macos, connect from finder:
1159
1161
 
1160
1162
  in order to grant full write-access to webdav clients, the volflag `daw` must be set and the account must also have delete-access (otherwise the client won't be allowed to replace the contents of existing files, which is how webdav works)
1161
1163
 
1164
+ > note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
1165
+
1162
1166
 
1163
1167
  ### connecting to webdav from windows
1164
1168
 
@@ -1724,10 +1728,16 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical
1724
1728
 
1725
1729
  for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it)
1726
1730
 
1727
- example webserver configs:
1731
+ example webserver / reverse-proxy configs:
1728
1732
 
1729
- * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
1730
- * [apache2 config](contrib/apache/copyparty.conf) -- location-based
1733
+ * [apache config](contrib/apache/copyparty.conf)
1734
+ * caddy uds: `caddy reverse-proxy --from :8080 --to unix///dev/shm/party.sock`
1735
+ * caddy tcp: `caddy reverse-proxy --from :8081 --to http://127.0.0.1:3923`
1736
+ * [haproxy config](contrib/haproxy/copyparty.conf)
1737
+ * [lighttpd subdomain](contrib/lighttpd/subdomain.conf) -- entire domain/subdomain
1738
+ * [lighttpd subpath](contrib/lighttpd/subpath.conf) -- location-based (not optimal, but in case you need it)
1739
+ * [nginx config](contrib/nginx/copyparty.conf) -- recommended
1740
+ * [traefik config](contrib/traefik/copyparty.yaml)
1731
1741
 
1732
1742
 
1733
1743
  ### real-ip
@@ -1739,6 +1749,38 @@ if you (and maybe everybody else) keep getting a message that says `thank you fo
1739
1749
  for most common setups, there should be a helpful message in the server-log explaining what to do, but see [docs/xff.md](docs/xff.md) if you want to learn more, including a quick hack to **just make it work** (which is **not** recommended, but hey...)
1740
1750
 
1741
1751
 
1752
+ ### reverse-proxy performance
1753
+
1754
+ most reverse-proxies support connecting to copyparty either using uds/unix-sockets (`/dev/shm/party.sock`, faster/recommended) or using tcp (`127.0.0.1`)
1755
+
1756
+ with copyparty listening on a uds / unix-socket / unix-domain-socket and the reverse-proxy connecting to that:
1757
+
1758
+ | index.html | upload | download | software |
1759
+ | ------------ | ----------- | ----------- | -------- |
1760
+ | 28'900 req/s | 6'900 MiB/s | 7'400 MiB/s | no-proxy |
1761
+ | 18'750 req/s | 3'500 MiB/s | 2'370 MiB/s | haproxy |
1762
+ | 9'900 req/s | 3'750 MiB/s | 2'200 MiB/s | caddy |
1763
+ | 18'700 req/s | 2'200 MiB/s | 1'570 MiB/s | nginx |
1764
+ | 9'700 req/s | 1'750 MiB/s | 1'830 MiB/s | apache |
1765
+ | 9'900 req/s | 1'300 MiB/s | 1'470 MiB/s | lighttpd |
1766
+
1767
+ when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-fasioned way), speeds are a bit worse:
1768
+
1769
+ | index.html | upload | download | software |
1770
+ | ------------ | ----------- | ----------- | -------- |
1771
+ | 21'200 req/s | 5'700 MiB/s | 6'700 MiB/s | no-proxy |
1772
+ | 14'500 req/s | 1'700 MiB/s | 2'170 MiB/s | haproxy |
1773
+ | 11'100 req/s | 2'750 MiB/s | 2'000 MiB/s | traefik |
1774
+ | 8'400 req/s | 2'300 MiB/s | 1'950 MiB/s | caddy |
1775
+ | 13'400 req/s | 1'100 MiB/s | 1'480 MiB/s | nginx |
1776
+ | 8'400 req/s | 1'000 MiB/s | 1'000 MiB/s | apache |
1777
+ | 6'500 req/s | 1'270 MiB/s | 1'500 MiB/s | lighttpd |
1778
+
1779
+ in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)
1780
+
1781
+ * if these results are bullshit because my config exampels are bad, please submit corrections!
1782
+
1783
+
1742
1784
  ## prometheus
1743
1785
 
1744
1786
  metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
@@ -2052,7 +2094,8 @@ interact with copyparty using non-browser clients
2052
2094
  * can be downloaded from copyparty: controlpanel -> connect -> [partyfuse.py](http://127.0.0.1:3923/.cpr/a/partyfuse.py)
2053
2095
  * [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
2054
2096
 
2055
- * sharex (screenshot utility): see [./contrib/sharex.sxcu](contrib/#sharexsxcu)
2097
+ * sharex (screenshot utility): see [./contrib/sharex.sxcu](./contrib/#sharexsxcu)
2098
+ * and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
2056
2099
  * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
2057
2100
 
2058
2101
  * contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
@@ -92,6 +92,7 @@ turn almost any device into a file server with resumable uploads/downloads using
92
92
  * [listen on port 80 and 443](#listen-on-port-80-and-443) - become a *real* webserver
93
93
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
94
94
  * [real-ip](#real-ip) - teaching copyparty how to see client IPs
95
+ * [reverse-proxy performance](#reverse-proxy-performance)
95
96
  * [prometheus](#prometheus) - metrics/stats can be enabled
96
97
  * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
97
98
  * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
@@ -140,6 +141,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
140
141
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
141
142
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
142
143
  * or if you are on android, [install copyparty in termux](#install-on-android)
144
+ * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
143
145
  * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
144
146
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
145
147
  * docker has all deps built-in, so skip this step:
@@ -646,7 +648,7 @@ dragdrop is the recommended way, but you may also:
646
648
 
647
649
  * select some files (not folders) in your file explorer and press CTRL-V inside the browser window
648
650
  * use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
649
- * upload using [curl or sharex](#client-examples)
651
+ * upload using [curl, sharex, ishare, ...](#client-examples)
650
652
 
651
653
  when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
652
654
  * `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
@@ -1104,6 +1106,8 @@ on macos, connect from finder:
1104
1106
 
1105
1107
  in order to grant full write-access to webdav clients, the volflag `daw` must be set and the account must also have delete-access (otherwise the client won't be allowed to replace the contents of existing files, which is how webdav works)
1106
1108
 
1109
+ > note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
1110
+
1107
1111
 
1108
1112
  ### connecting to webdav from windows
1109
1113
 
@@ -1669,10 +1673,16 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical
1669
1673
 
1670
1674
  for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it)
1671
1675
 
1672
- example webserver configs:
1676
+ example webserver / reverse-proxy configs:
1673
1677
 
1674
- * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
1675
- * [apache2 config](contrib/apache/copyparty.conf) -- location-based
1678
+ * [apache config](contrib/apache/copyparty.conf)
1679
+ * caddy uds: `caddy reverse-proxy --from :8080 --to unix///dev/shm/party.sock`
1680
+ * caddy tcp: `caddy reverse-proxy --from :8081 --to http://127.0.0.1:3923`
1681
+ * [haproxy config](contrib/haproxy/copyparty.conf)
1682
+ * [lighttpd subdomain](contrib/lighttpd/subdomain.conf) -- entire domain/subdomain
1683
+ * [lighttpd subpath](contrib/lighttpd/subpath.conf) -- location-based (not optimal, but in case you need it)
1684
+ * [nginx config](contrib/nginx/copyparty.conf) -- recommended
1685
+ * [traefik config](contrib/traefik/copyparty.yaml)
1676
1686
 
1677
1687
 
1678
1688
  ### real-ip
@@ -1684,6 +1694,38 @@ if you (and maybe everybody else) keep getting a message that says `thank you fo
1684
1694
  for most common setups, there should be a helpful message in the server-log explaining what to do, but see [docs/xff.md](docs/xff.md) if you want to learn more, including a quick hack to **just make it work** (which is **not** recommended, but hey...)
1685
1695
 
1686
1696
 
1697
+ ### reverse-proxy performance
1698
+
1699
+ most reverse-proxies support connecting to copyparty either using uds/unix-sockets (`/dev/shm/party.sock`, faster/recommended) or using tcp (`127.0.0.1`)
1700
+
1701
+ with copyparty listening on a uds / unix-socket / unix-domain-socket and the reverse-proxy connecting to that:
1702
+
1703
+ | index.html | upload | download | software |
1704
+ | ------------ | ----------- | ----------- | -------- |
1705
+ | 28'900 req/s | 6'900 MiB/s | 7'400 MiB/s | no-proxy |
1706
+ | 18'750 req/s | 3'500 MiB/s | 2'370 MiB/s | haproxy |
1707
+ | 9'900 req/s | 3'750 MiB/s | 2'200 MiB/s | caddy |
1708
+ | 18'700 req/s | 2'200 MiB/s | 1'570 MiB/s | nginx |
1709
+ | 9'700 req/s | 1'750 MiB/s | 1'830 MiB/s | apache |
1710
+ | 9'900 req/s | 1'300 MiB/s | 1'470 MiB/s | lighttpd |
1711
+
1712
+ when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-fasioned way), speeds are a bit worse:
1713
+
1714
+ | index.html | upload | download | software |
1715
+ | ------------ | ----------- | ----------- | -------- |
1716
+ | 21'200 req/s | 5'700 MiB/s | 6'700 MiB/s | no-proxy |
1717
+ | 14'500 req/s | 1'700 MiB/s | 2'170 MiB/s | haproxy |
1718
+ | 11'100 req/s | 2'750 MiB/s | 2'000 MiB/s | traefik |
1719
+ | 8'400 req/s | 2'300 MiB/s | 1'950 MiB/s | caddy |
1720
+ | 13'400 req/s | 1'100 MiB/s | 1'480 MiB/s | nginx |
1721
+ | 8'400 req/s | 1'000 MiB/s | 1'000 MiB/s | apache |
1722
+ | 6'500 req/s | 1'270 MiB/s | 1'500 MiB/s | lighttpd |
1723
+
1724
+ in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)
1725
+
1726
+ * if these results are bullshit because my config exampels are bad, please submit corrections!
1727
+
1728
+
1687
1729
  ## prometheus
1688
1730
 
1689
1731
  metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
@@ -1997,7 +2039,8 @@ interact with copyparty using non-browser clients
1997
2039
  * can be downloaded from copyparty: controlpanel -> connect -> [partyfuse.py](http://127.0.0.1:3923/.cpr/a/partyfuse.py)
1998
2040
  * [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
1999
2041
 
2000
- * sharex (screenshot utility): see [./contrib/sharex.sxcu](contrib/#sharexsxcu)
2042
+ * sharex (screenshot utility): see [./contrib/sharex.sxcu](./contrib/#sharexsxcu)
2043
+ * and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
2001
2044
  * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
2002
2045
 
2003
2046
  * contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 16, 6)
3
+ VERSION = (1, 16, 8)
4
4
  CODENAME = "COPYparty"
5
- BUILD_DT = (2024, 12, 19)
5
+ BUILD_DT = (2025, 1, 11)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -2174,11 +2174,11 @@ class AuthSrv(object):
2174
2174
  if not self.args.no_voldump:
2175
2175
  self.log(t)
2176
2176
 
2177
- if have_e2d:
2177
+ if have_e2d or self.args.idp_h_usr:
2178
2178
  t = self.chk_sqlite_threadsafe()
2179
2179
  if t:
2180
2180
  self.log("\n\033[{}\033[0m\n".format(t))
2181
-
2181
+ if have_e2d:
2182
2182
  if not have_e2t:
2183
2183
  t = "hint: enable multimedia indexing (artist/title/...) with argument -e2ts"
2184
2184
  self.log(t, 6)
@@ -141,6 +141,14 @@ A_FILE = os.stat_result(
141
141
  (0o644, -1, -1, 1, 1000, 1000, 8, 0x39230101, 0x39230101, 0x39230101)
142
142
  )
143
143
 
144
+ RE_CC = re.compile(r"[\x00-\x1f]") # search always faster
145
+ RE_HSAFE = re.compile(r"[\x00-\x1f<>\"'&]") # search always much faster
146
+ RE_HOST = re.compile(r"[^][0-9a-zA-Z.:_-]") # search faster <=17ch
147
+ RE_MHOST = re.compile(r"^[][0-9a-zA-Z.:_-]+$") # match faster >=18ch
148
+ RE_K = re.compile(r"[^0-9a-zA-Z_-]") # search faster <=17ch
149
+
150
+ UPARAM_CC_OK = set("doc move tree".split())
151
+
144
152
 
145
153
  class HttpCli(object):
146
154
  """
@@ -384,6 +392,15 @@ class HttpCli(object):
384
392
  self.host = self.headers.get("x-forwarded-host") or self.host
385
393
  trusted_xff = True
386
394
 
395
+ m = RE_HOST.search(self.host)
396
+ if m and self.host != self.args.name:
397
+ zs = self.host
398
+ t = "malicious user; illegal Host header; req(%r) host(%r) => %r"
399
+ self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
400
+ self.cbonk(self.conn.hsrv.gmal, zs, "bad_host", "illegal Host header")
401
+ self.terse_reply(b"illegal Host header", 400)
402
+ return False
403
+
387
404
  if self.is_banned():
388
405
  return False
389
406
 
@@ -429,6 +446,16 @@ class HttpCli(object):
429
446
  self.loud_reply(t, status=400)
430
447
  return False
431
448
 
449
+ ptn_cc = RE_CC
450
+ m = ptn_cc.search(self.req)
451
+ if m:
452
+ zs = self.req
453
+ t = "malicious user; Cc in req0 %r => %r"
454
+ self.log(t % (zs, zs[m.span()[0] :]), 1)
455
+ self.cbonk(self.conn.hsrv.gmal, zs, "cc_r0", "Cc in req0")
456
+ self.terse_reply(b"", 500)
457
+ return False
458
+
432
459
  # split req into vpath + uparam
433
460
  uparam = {}
434
461
  if "?" not in self.req:
@@ -441,8 +468,8 @@ class HttpCli(object):
441
468
  self.trailing_slash = vpath.endswith("/")
442
469
  vpath = undot(vpath)
443
470
 
444
- ptn = self.conn.hsrv.ptn_cc
445
- k_safe = self.conn.hsrv.uparam_cc_ok
471
+ re_k = RE_K
472
+ k_safe = UPARAM_CC_OK
446
473
  for k in arglist.split("&"):
447
474
  if "=" in k:
448
475
  k, zs = k.split("=", 1)
@@ -452,6 +479,14 @@ class HttpCli(object):
452
479
  else:
453
480
  sv = ""
454
481
 
482
+ m = re_k.search(k)
483
+ if m:
484
+ t = "malicious user; bad char in query key; req(%r) qk(%r) => %r"
485
+ self.log(t % (self.req, k, k[m.span()[0] :]), 1)
486
+ self.cbonk(self.conn.hsrv.gmal, self.req, "bc_q", "illegal qkey")
487
+ self.terse_reply(b"", 500)
488
+ return False
489
+
455
490
  k = k.lower()
456
491
  uparam[k] = sv
457
492
 
@@ -459,17 +494,26 @@ class HttpCli(object):
459
494
  continue
460
495
 
461
496
  zs = "%s=%s" % (k, sv)
462
- m = ptn.search(zs)
497
+ m = ptn_cc.search(zs)
463
498
  if not m:
464
499
  continue
465
500
 
466
- hit = zs[m.span()[0] :]
467
- t = "malicious user; Cc in query [{}] => [{!r}]"
468
- self.log(t.format(self.req, hit), 1)
501
+ t = "malicious user; Cc in query; req(%r) qp(%r) => %r"
502
+ self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
469
503
  self.cbonk(self.conn.hsrv.gmal, self.req, "cc_q", "Cc in query")
470
504
  self.terse_reply(b"", 500)
471
505
  return False
472
506
 
507
+ if "k" in uparam:
508
+ m = RE_K.search(uparam["k"])
509
+ if m:
510
+ zs = uparam["k"]
511
+ t = "malicious user; illegal filekey; req(%r) k(%r) => %r"
512
+ self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
513
+ self.cbonk(self.conn.hsrv.gmal, zs, "bad_k", "illegal filekey")
514
+ self.terse_reply(b"illegal filekey", 400)
515
+ return False
516
+
473
517
  if self.is_vproxied:
474
518
  if vpath.startswith(self.args.R):
475
519
  vpath = vpath[len(self.args.R) + 1 :]
@@ -513,7 +557,7 @@ class HttpCli(object):
513
557
  return self.tx_qr()
514
558
 
515
559
  if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
516
- self.log("invalid relpath %r" % ("/" + self.vpath,))
560
+ self.log("illegal relpath; req(%r) => %r" % (self.req, "/" + self.vpath))
517
561
  self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
518
562
  return self.tx_404() and self.keepalive
519
563
 
@@ -866,12 +910,12 @@ class HttpCli(object):
866
910
  for k, zs in list(self.out_headers.items()) + self.out_headerlist:
867
911
  response.append("%s: %s" % (k, zs))
868
912
 
913
+ ptn_cc = RE_CC
869
914
  for zs in response:
870
- m = self.conn.hsrv.ptn_cc.search(zs)
915
+ m = ptn_cc.search(zs)
871
916
  if m:
872
- hit = zs[m.span()[0] :]
873
- t = "malicious user; Cc in out-hdr {!r} => [{!r}]"
874
- self.log(t.format(zs, hit), 1)
917
+ t = "malicious user; Cc in out-hdr; req(%r) hdr(%r) => %r"
918
+ self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
875
919
  self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
876
920
  raise Pebkac(999)
877
921
 
@@ -997,7 +1041,7 @@ class HttpCli(object):
997
1041
  if not kv:
998
1042
  return ""
999
1043
 
1000
- r = ["%s=%s" % (k, quotep(zs)) if zs else k for k, zs in kv.items()]
1044
+ r = ["%s=%s" % (quotep(k), quotep(zs)) if zs else k for k, zs in kv.items()]
1001
1045
  return "?" + "&amp;".join(r)
1002
1046
 
1003
1047
  def ourlq(self) :
@@ -1149,8 +1193,8 @@ class HttpCli(object):
1149
1193
  return self.tx_res(res_path)
1150
1194
 
1151
1195
  if res_path != undot(res_path):
1152
- t = "malicious user; attempted path traversal %r => %r"
1153
- self.log(t % ("/" + self.vpath, res_path), 1)
1196
+ t = "malicious user; attempted path traversal; req(%r) vp(%r) => %r"
1197
+ self.log(t % (self.req, "/" + self.vpath, res_path), 1)
1154
1198
  self.cbonk(self.conn.hsrv.gmal, self.req, "trav", "path traversal")
1155
1199
 
1156
1200
  self.tx_404()
@@ -1293,8 +1337,8 @@ class HttpCli(object):
1293
1337
 
1294
1338
  pw = self.ouparam.get("pw")
1295
1339
  if pw:
1296
- q_pw = "?pw=%s" % (pw,)
1297
- a_pw = "&pw=%s" % (pw,)
1340
+ q_pw = "?pw=%s" % (html_escape(pw, True, True),)
1341
+ a_pw = "&pw=%s" % (html_escape(pw, True, True),)
1298
1342
  for i in hits:
1299
1343
  i["rp"] += a_pw if "?" in i["rp"] else q_pw
1300
1344
  else:
@@ -1655,7 +1699,7 @@ class HttpCli(object):
1655
1699
 
1656
1700
  token = str(uuid.uuid4())
1657
1701
 
1658
- if not lk.find(r"./{DAV:}depth"):
1702
+ if lk.find(r"./{DAV:}depth") is None:
1659
1703
  depth = self.headers.get("depth", "infinity")
1660
1704
  lk.append(mktnod("D:depth", depth))
1661
1705
 
@@ -1845,7 +1889,7 @@ class HttpCli(object):
1845
1889
  return self.handle_stash(False)
1846
1890
 
1847
1891
  if "save" in opt:
1848
- post_sz, _, _, _, path, _ = self.dump_to_file(False)
1892
+ post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
1849
1893
  self.log("urlform: %d bytes, %r" % (post_sz, path))
1850
1894
  elif "print" in opt:
1851
1895
  reader, _ = self.get_body_reader()
@@ -1926,11 +1970,11 @@ class HttpCli(object):
1926
1970
  else:
1927
1971
  return read_socket(self.sr, bufsz, remains), remains
1928
1972
 
1929
- def dump_to_file(self, is_put ) :
1930
- # post_sz, sha_hex, sha_b64, remains, path, url
1973
+ def dump_to_file(self, is_put ) :
1974
+ # post_sz, halg, sha_hex, sha_b64, remains, path, url
1931
1975
  reader, remains = self.get_body_reader()
1932
1976
  vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
1933
- rnd, _, lifetime, xbu, xau = self.upload_flags(vfs)
1977
+ rnd, lifetime, xbu, xau = self.upload_flags(vfs)
1934
1978
  lim = vfs.get_dbv(rem)[0].lim
1935
1979
  fdir = vfs.canonical(rem)
1936
1980
  if lim:
@@ -2078,12 +2122,14 @@ class HttpCli(object):
2078
2122
  # small toctou, but better than clobbering a hardlink
2079
2123
  wunlink(self.log, path, vfs.flags)
2080
2124
 
2125
+ halg = "sha512"
2081
2126
  hasher = None
2082
2127
  copier = hashcopy
2083
2128
  if "ck" in self.ouparam or "ck" in self.headers:
2084
- zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
2129
+ halg = zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
2085
2130
  if not zs or zs == "no":
2086
2131
  copier = justcopy
2132
+ halg = ""
2087
2133
  elif zs == "md5":
2088
2134
  hasher = hashlib.md5(**USED4SEC)
2089
2135
  elif zs == "sha1":
@@ -2117,7 +2163,7 @@ class HttpCli(object):
2117
2163
  raise
2118
2164
 
2119
2165
  if self.args.nw:
2120
- return post_sz, sha_hex, sha_b64, remains, path, ""
2166
+ return post_sz, halg, sha_hex, sha_b64, remains, path, ""
2121
2167
 
2122
2168
  at = mt = time.time() - lifetime
2123
2169
  cli_mt = self.headers.get("x-oc-mtime")
@@ -2228,19 +2274,30 @@ class HttpCli(object):
2228
2274
  self.args.RS + vpath + vsuf,
2229
2275
  )
2230
2276
 
2231
- return post_sz, sha_hex, sha_b64, remains, path, url
2277
+ return post_sz, halg, sha_hex, sha_b64, remains, path, url
2232
2278
 
2233
2279
  def handle_stash(self, is_put ) :
2234
- post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
2280
+ post_sz, halg, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
2235
2281
  spd = self._spd(post_sz)
2236
2282
  t = "%s wrote %d/%d bytes to %r # %s"
2237
2283
  self.log(t % (spd, post_sz, remains, path, sha_b64[:28])) # 21
2238
2284
 
2239
- ac = self.uparam.get(
2240
- "want", self.headers.get("accept", "").lower().split(";")[-1]
2241
- )
2285
+ mime = "text/plain; charset=utf-8"
2286
+ ac = self.uparam.get("want") or self.headers.get("accept") or ""
2287
+ if ac:
2288
+ ac = ac.split(";", 1)[0].lower()
2289
+ if ac == "application/json":
2290
+ ac = "json"
2242
2291
  if ac == "url":
2243
2292
  t = url
2293
+ elif ac == "json" or "j" in self.uparam:
2294
+ jmsg = {"fileurl": url, "filesz": post_sz}
2295
+ if halg:
2296
+ jmsg[halg] = sha_hex[:56]
2297
+ jmsg["sha_b64"] = sha_b64
2298
+
2299
+ mime = "application/json"
2300
+ t = json.dumps(jmsg, indent=2, sort_keys=True)
2244
2301
  else:
2245
2302
  t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url)
2246
2303
 
@@ -2250,7 +2307,7 @@ class HttpCli(object):
2250
2307
  h["X-OC-MTime"] = "accepted"
2251
2308
  t = "" # some webdav clients expect/prefer this
2252
2309
 
2253
- self.reply(t.encode("utf-8"), 201, headers=h)
2310
+ self.reply(t.encode("utf-8", "replace"), 201, mime=mime, headers=h)
2254
2311
  return True
2255
2312
 
2256
2313
  def bakflip(
@@ -2923,7 +2980,7 @@ class HttpCli(object):
2923
2980
  self.redirect(vpath, "?edit")
2924
2981
  return True
2925
2982
 
2926
- def upload_flags(self, vfs ) :
2983
+ def upload_flags(self, vfs ) :
2927
2984
  if self.args.nw:
2928
2985
  rnd = 0
2929
2986
  else:
@@ -2931,10 +2988,6 @@ class HttpCli(object):
2931
2988
  if vfs.flags.get("rand"): # force-enable
2932
2989
  rnd = max(rnd, vfs.flags["nrand"])
2933
2990
 
2934
- ac = self.uparam.get(
2935
- "want", self.headers.get("accept", "").lower().split(";")[-1]
2936
- )
2937
- want_url = ac == "url"
2938
2991
  zs = self.uparam.get("life", self.headers.get("life", ""))
2939
2992
  if zs:
2940
2993
  vlife = vfs.flags.get("lifetime") or 0
@@ -2944,7 +2997,6 @@ class HttpCli(object):
2944
2997
 
2945
2998
  return (
2946
2999
  rnd,
2947
- want_url,
2948
3000
  lifetime,
2949
3001
  vfs.flags.get("xbu") or [],
2950
3002
  vfs.flags.get("xau") or [],
@@ -2997,7 +3049,14 @@ class HttpCli(object):
2997
3049
  if not nullwrite:
2998
3050
  bos.makedirs(fdir_base)
2999
3051
 
3000
- rnd, want_url, lifetime, xbu, xau = self.upload_flags(vfs)
3052
+ rnd, lifetime, xbu, xau = self.upload_flags(vfs)
3053
+ zs = self.uparam.get("want") or self.headers.get("accept") or ""
3054
+ if zs:
3055
+ zs = zs.split(";", 1)[0].lower()
3056
+ if zs == "application/json":
3057
+ zs = "json"
3058
+ want_url = zs == "url"
3059
+ want_json = zs == "json" or "j" in self.uparam
3001
3060
 
3002
3061
  files = []
3003
3062
  # sz, sha_hex, sha_b64, p_file, fname, abspath
@@ -3319,7 +3378,9 @@ class HttpCli(object):
3319
3378
  msg += "\n" + errmsg
3320
3379
 
3321
3380
  self.reply(msg.encode("utf-8", "replace"), status=sc)
3322
- elif "j" in self.uparam:
3381
+ elif want_json:
3382
+ if len(jmsg["files"]) == 1:
3383
+ jmsg["fileurl"] = jmsg["files"][0]["url"]
3323
3384
  jtxt = json.dumps(jmsg, indent=2, sort_keys=True).encode("utf-8", "replace")
3324
3385
  self.reply(jtxt, mime="application/json", status=sc)
3325
3386
  else:
@@ -3636,6 +3697,7 @@ class HttpCli(object):
3636
3697
  return logues, readmes
3637
3698
 
3638
3699
  def _expand(self, txt , phs ) :
3700
+ ptn_hsafe = RE_HSAFE
3639
3701
  for ph in phs:
3640
3702
  if ph.startswith("hdr."):
3641
3703
  sv = str(self.headers.get(ph[4:], ""))
@@ -3653,7 +3715,7 @@ class HttpCli(object):
3653
3715
  self.log("unknown placeholder in server config: [%s]" % (ph,), 3)
3654
3716
  continue
3655
3717
 
3656
- sv = self.conn.hsrv.ptn_hsafe.sub("_", sv)
3718
+ sv = ptn_hsafe.sub("_", sv)
3657
3719
  txt = txt.replace("{{%s}}" % (ph,), sv)
3658
3720
 
3659
3721
  return txt
@@ -4486,12 +4548,12 @@ class HttpCli(object):
4486
4548
  else self.conn.hsrv.nm.map(self.ip) or host
4487
4549
  )
4488
4550
  # safer than html_escape/quotep since this avoids both XSS and shell-stuff
4489
- pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "pw")
4551
+ pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
4490
4552
  vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
4491
4553
  pw = pw.replace(" ", "%20")
4492
4554
  vp = vp.replace(" ", "%20")
4493
4555
  if pw in self.asrv.sesa:
4494
- pw = "pwd"
4556
+ pw = "hunter2"
4495
4557
 
4496
4558
  html = self.j2s(
4497
4559
  "svcs",
@@ -4965,11 +5027,11 @@ class HttpCli(object):
4965
5027
  ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
4966
5028
  ret = ret[:2000]
4967
5029
 
5030
+ ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
5031
+
4968
5032
  if len(ret) > 2000:
4969
5033
  ret = ret[:2000]
4970
5034
 
4971
- ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
4972
-
4973
5035
  for rv in ret:
4974
5036
  rv["vp"] = quotep(rv["vp"])
4975
5037
  nfk = rv.pop("nfk")
@@ -5057,10 +5119,6 @@ class HttpCli(object):
5057
5119
  if not dots and "/." in vp:
5058
5120
  continue
5059
5121
 
5060
- n -= 1
5061
- if not n:
5062
- break
5063
-
5064
5122
  rv = {
5065
5123
  "vp": vp,
5066
5124
  "sz": sz,
@@ -5078,13 +5136,17 @@ class HttpCli(object):
5078
5136
  ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
5079
5137
  ret = ret[:1000]
5080
5138
 
5081
- if len(ret) > 1000:
5082
- ret = ret[:1000]
5139
+ n -= 1
5140
+ if not n:
5141
+ break
5083
5142
 
5084
5143
  ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
5085
5144
 
5145
+ if len(ret) > 1000:
5146
+ ret = ret[:1000]
5147
+
5086
5148
  for rv in ret:
5087
- rv["evp"] = quotep(rv["vp"])
5149
+ rv["vp"] = quotep(rv["vp"])
5088
5150
  nfk = rv.pop("nfk")
5089
5151
  if not nfk:
5090
5152
  continue
@@ -5117,15 +5179,16 @@ class HttpCli(object):
5117
5179
  for v in ret:
5118
5180
  v["vp"] = self.args.SR + v["vp"]
5119
5181
 
5120
- self.log("%s #%d %.2fsec" % (lm, len(ret), time.time() - t0))
5182
+ now = time.time()
5183
+ self.log("%s #%d %.2fsec" % (lm, len(ret), now - t0))
5121
5184
 
5185
+ ret2 = {"now": int(now), "filter": sfilt, "ups": ret}
5186
+ jtxt = json.dumps(ret2, separators=(",\n", ": "))
5122
5187
  if "j" in self.ouparam:
5123
- jtxt = json.dumps(ret, separators=(",\n", ": "))
5124
5188
  self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
5125
5189
  return True
5126
5190
 
5127
- rows = [[x["vp"], x["evp"], x["sz"], x["ip"], x["at"]] for x in ret]
5128
- html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
5191
+ html = self.j2s("rups", this=self, v=jtxt)
5129
5192
  self.reply(html.encode("utf-8"), status=200)
5130
5193
  return True
5131
5194
 
@@ -191,10 +191,6 @@ class HttpSrv(object):
191
191
  self.xff_nm = build_netmap(self.args.xff_src)
192
192
  self.xff_lan = build_netmap("lan")
193
193
 
194
- self.ptn_cc = re.compile(r"[\x00-\x1f]")
195
- self.ptn_hsafe = re.compile(r"[\x00-\x1f<>\"'&]")
196
- self.uparam_cc_ok = set("doc move tree".split())
197
-
198
194
  self.mallow = "GET HEAD POST PUT DELETE OPTIONS".split()
199
195
  if not self.args.no_dav:
200
196
  zs = "PROPFIND PROPPATCH LOCK UNLOCK MKCOL COPY MOVE"