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.
- {copyparty-1.16.6 → copyparty-1.16.8}/PKG-INFO +50 -7
- {copyparty-1.16.6 → copyparty-1.16.8}/README.md +48 -5
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__version__.py +2 -2
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/authsrv.py +2 -2
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpcli.py +116 -53
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpsrv.py +0 -4
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/tcpsrv.py +3 -3
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/up2k.py +21 -7
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/u2c.py +1 -1
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.html +1 -1
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.html +2 -2
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.html +2 -2
- copyparty-1.16.8/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/rups.html +8 -25
- copyparty-1.16.8/copyparty/web/rups.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.html +4 -3
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.html +1 -1
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/svcs.html +65 -4
- copyparty-1.16.8/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/ui.css.gz +0 -0
- copyparty-1.16.8/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.16.8/copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/PKG-INFO +50 -7
- copyparty-1.16.6/copyparty/web/rups.css.gz +0 -0
- copyparty-1.16.6/copyparty/web/rups.js.gz +0 -0
- copyparty-1.16.6/copyparty/web/svcs.js.gz +0 -0
- copyparty-1.16.6/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.16.6/copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/LICENSE +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/__main__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/bos.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/bos/path.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_mp.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_thr.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/broker_util.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/cert.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/cfg.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/dxml.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/fsutil.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ftpd.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/httpconn.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ico.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/mdns.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/metrics.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/mtag.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/multicast.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/pwhash.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/smbd.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/ssdp.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/star.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/sutil.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/svchub.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/szip.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/tftpd.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/th_cli.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/th_srv.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/u2idx.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/util.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/baguettebox.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/browser2.html +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/cf.html +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/2.png +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/3.png +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/4.png +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/5.png +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/dd/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/msg.html +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/shares.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/pyproject.toml +0 -0
- {copyparty-1.16.6 → copyparty-1.16.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
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
|
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
|
-
* [
|
1730
|
-
*
|
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
|
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
|
-
* [
|
1675
|
-
*
|
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)
|
@@ -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
|
-
|
445
|
-
k_safe =
|
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 =
|
497
|
+
m = ptn_cc.search(zs)
|
463
498
|
if not m:
|
464
499
|
continue
|
465
500
|
|
466
|
-
|
467
|
-
t
|
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("
|
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 =
|
915
|
+
m = ptn_cc.search(zs)
|
871
916
|
if m:
|
872
|
-
|
873
|
-
t
|
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 "?" + "&".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
|
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,
|
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
|
-
|
2240
|
-
|
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,
|
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
|
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 =
|
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 "
|
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 = "
|
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
|
-
|
5082
|
-
|
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["
|
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
|
-
|
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
|
-
|
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"
|