copyparty 1.18.8__tar.gz → 1.18.10__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.18.8 → copyparty-1.18.10}/PKG-INFO +30 -2
- {copyparty-1.18.8 → copyparty-1.18.10}/README.md +22 -1
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/__init__.py +0 -4
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/__main__.py +13 -5
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/__version__.py +2 -2
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/authsrv.py +10 -3
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/httpcli.py +85 -35
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/httpsrv.py +2 -1
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/mtag.py +5 -1
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/svchub.py +2 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/tcpsrv.py +2 -3
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/th_cli.py +2 -2
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/th_srv.py +68 -2
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/up2k.py +27 -21
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/util.py +15 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/a/u2c.py +3 -1
- copyparty-1.18.10/copyparty/web/browser.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/browser.html +1 -1
- copyparty-1.18.10/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/rups.html +1 -0
- copyparty-1.18.10/copyparty/web/rups.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/shares.html +1 -0
- copyparty-1.18.10/copyparty/web/shares.js.gz +0 -0
- copyparty-1.18.10/copyparty/web/splash.js.gz +0 -0
- copyparty-1.18.10/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.18.10/copyparty/web/util.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/PKG-INFO +30 -2
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/SOURCES.txt +0 -5
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/requires.txt +8 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/pyproject.toml +8 -1
- copyparty-1.18.8/copyparty/web/browser.css.gz +0 -0
- copyparty-1.18.8/copyparty/web/browser.js.gz +0 -0
- copyparty-1.18.8/copyparty/web/dd/2.png +0 -0
- copyparty-1.18.8/copyparty/web/dd/3.png +0 -0
- copyparty-1.18.8/copyparty/web/dd/4.png +0 -0
- copyparty-1.18.8/copyparty/web/dd/5.png +0 -0
- copyparty-1.18.8/copyparty/web/deps/__init__.py +0 -0
- copyparty-1.18.8/copyparty/web/rups.js.gz +0 -0
- copyparty-1.18.8/copyparty/web/shares.js.gz +0 -0
- copyparty-1.18.8/copyparty/web/splash.js.gz +0 -0
- copyparty-1.18.8/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.18.8/copyparty/web/util.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/LICENSE +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/bos/bos.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/bos/path.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/broker_mp.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/broker_thr.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/broker_util.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/cert.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/cfg.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/dxml.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/fsutil.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/ftpd.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/httpconn.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/ico.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/mdns.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/metrics.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/multicast.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/pwhash.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/res/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/smbd.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/ssdp.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/star.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/sutil.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/szip.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/tftpd.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/u2idx.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/baguettebox.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/browser2.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/cf.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.18.8/copyparty/web/dd → copyparty-1.18.10/copyparty/web/deps}/__init__.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/idp.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/md.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/mde.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/msg.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/splash.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/svcs.html +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/ui.css.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.18.8 → copyparty-1.18.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.18.
|
3
|
+
Version: 1.18.10
|
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
|
@@ -37,6 +37,13 @@ Requires-Python: >=3.3
|
|
37
37
|
Description-Content-Type: text/markdown
|
38
38
|
License-File: LICENSE
|
39
39
|
Requires-Dist: Jinja2
|
40
|
+
Provides-Extra: all
|
41
|
+
Requires-Dist: argon2-cffi; extra == "all"
|
42
|
+
Requires-Dist: partftpy>=0.4.0; extra == "all"
|
43
|
+
Requires-Dist: Pillow; extra == "all"
|
44
|
+
Requires-Dist: pyftpdlib; extra == "all"
|
45
|
+
Requires-Dist: pyopenssl; extra == "all"
|
46
|
+
Requires-Dist: pyzmq; extra == "all"
|
40
47
|
Provides-Extra: thumbnails
|
41
48
|
Requires-Dist: Pillow; extra == "thumbnails"
|
42
49
|
Provides-Extra: thumbnails2
|
@@ -205,11 +212,14 @@ made in Norway 🇳🇴
|
|
205
212
|
|
206
213
|
just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
207
214
|
|
215
|
+
> ℹ️ the sfx is a [self-extractor](https://github.com/9001/copyparty/issues/270) which unpacks an embedded `tar.gz` into `$TEMP` -- if this looks too scary, you can use the [zipapp](#zipapp) which has slightly worse performance
|
216
|
+
|
208
217
|
* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
|
209
218
|
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
210
219
|
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
211
220
|
* or if you are on android, [install copyparty in termux](#install-on-android)
|
212
221
|
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
|
222
|
+
* or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
|
213
223
|
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
214
224
|
* or if your OS is dead, give the [bootable flashdrive / cd-rom](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) a spin
|
215
225
|
* or if you don't trust copyparty yet and want to isolate it a little, then...
|
@@ -570,12 +580,17 @@ anyone trying to bruteforce a password gets banned according to `--ban-pw`; defa
|
|
570
580
|
|
571
581
|
and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
|
572
582
|
|
583
|
+
* you can also `PRTY_CONFIG=foobar.conf python copyparty-sfx.py` (convenient in docker etc)
|
584
|
+
|
573
585
|
```yaml
|
574
586
|
[accounts]
|
575
587
|
u1: p1 # create account "u1" with password "p1"
|
576
588
|
u2: p2 # (note that comments must have
|
577
589
|
u3: p3 # two spaces before the # sign)
|
578
590
|
|
591
|
+
[groups]
|
592
|
+
g1: u1, u2 # create a group
|
593
|
+
|
579
594
|
[/] # this URL will be mapped to...
|
580
595
|
/srv # ...this folder on the server filesystem
|
581
596
|
accs:
|
@@ -585,6 +600,7 @@ and if you want to use config files instead of commandline args (good!) then her
|
|
585
600
|
/mnt/music # which is mapped to this folder
|
586
601
|
accs:
|
587
602
|
r: u1, u2 # only these accounts can read,
|
603
|
+
r: @g1 # (exactly the same, just with a group instead)
|
588
604
|
rw: u3 # and only u3 can read-write
|
589
605
|
|
590
606
|
[/inc]
|
@@ -1141,6 +1157,9 @@ open the `[🎺]` media-player-settings tab to configure it,
|
|
1141
1157
|
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
1142
1158
|
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the most part
|
1143
1159
|
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
1160
|
+
* `[flac]` -- lossless but compressed, for LAN and/or fiber playback on electrostatic headphones
|
1161
|
+
* `[wav]` -- lossless and uncompressed, for LAN and/or fiber playback on electrostatic headphones connected to very old equipment
|
1162
|
+
* `flac` and `wav` must be enabled with `--allow-flac` / `--allow-wav` to allow spending the disk space
|
1144
1163
|
* "tint" reduces the contrast of the playback bar
|
1145
1164
|
|
1146
1165
|
|
@@ -1482,6 +1501,8 @@ note that this disables hotlinking because the opengraph spec demands it; to sne
|
|
1482
1501
|
|
1483
1502
|
you can also hotlink files regardless by appending `?raw` to the url
|
1484
1503
|
|
1504
|
+
> WARNING: if you plan to use WebDAV, then `--og-ua` / `og_ua` must be configured
|
1505
|
+
|
1485
1506
|
if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
|
1486
1507
|
|
1487
1508
|
|
@@ -1938,6 +1959,8 @@ you can disable the built-in password-based login system, and instead replace it
|
|
1938
1959
|
|
1939
1960
|
* the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
|
1940
1961
|
|
1962
|
+
* if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party
|
1963
|
+
|
1941
1964
|
some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
|
1942
1965
|
|
1943
1966
|
there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)
|
@@ -2486,6 +2509,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|
2486
2509
|
| **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
|
2487
2510
|
| **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
|
2488
2511
|
| **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
|
2512
|
+
| **Nintendo Wii (Opera 9.0 "Internet Channel")** | can browse, can't upload or download (no local storage), can view images - works best with `?b=u`, default view broken |
|
2489
2513
|
|
2490
2514
|
<p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
|
2491
2515
|
|
@@ -2756,6 +2780,10 @@ optionally also specify `--ah-cli` to enter an interactive mode where it will ha
|
|
2756
2780
|
|
2757
2781
|
the default configs take about 0.4 sec and 256 MiB RAM to process a new password on a decent laptop
|
2758
2782
|
|
2783
|
+
when generating hashes using `--ah-cli` for docker or systemd services, make sure it is using the same `--ah-salt` by:
|
2784
|
+
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
2785
|
+
* setting the same `--ah-salt` in both environments
|
2786
|
+
|
2759
2787
|
|
2760
2788
|
## https
|
2761
2789
|
|
@@ -2900,7 +2928,7 @@ then again, if you are already into downloading shady binaries from the internet
|
|
2900
2928
|
|
2901
2929
|
## zipapp
|
2902
2930
|
|
2903
|
-
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2931
|
+
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but, unlike the sfx, it is a completely normal zipfile which does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2904
2932
|
|
2905
2933
|
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
|
2906
2934
|
|
@@ -147,11 +147,14 @@ made in Norway 🇳🇴
|
|
147
147
|
|
148
148
|
just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
149
149
|
|
150
|
+
> ℹ️ the sfx is a [self-extractor](https://github.com/9001/copyparty/issues/270) which unpacks an embedded `tar.gz` into `$TEMP` -- if this looks too scary, you can use the [zipapp](#zipapp) which has slightly worse performance
|
151
|
+
|
150
152
|
* or install through [pypi](https://pypi.org/project/copyparty/): `python3 -m pip install --user -U copyparty`
|
151
153
|
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
152
154
|
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
153
155
|
* or if you are on android, [install copyparty in termux](#install-on-android)
|
154
156
|
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
|
157
|
+
* or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
|
155
158
|
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
156
159
|
* or if your OS is dead, give the [bootable flashdrive / cd-rom](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) a spin
|
157
160
|
* or if you don't trust copyparty yet and want to isolate it a little, then...
|
@@ -512,12 +515,17 @@ anyone trying to bruteforce a password gets banned according to `--ban-pw`; defa
|
|
512
515
|
|
513
516
|
and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
|
514
517
|
|
518
|
+
* you can also `PRTY_CONFIG=foobar.conf python copyparty-sfx.py` (convenient in docker etc)
|
519
|
+
|
515
520
|
```yaml
|
516
521
|
[accounts]
|
517
522
|
u1: p1 # create account "u1" with password "p1"
|
518
523
|
u2: p2 # (note that comments must have
|
519
524
|
u3: p3 # two spaces before the # sign)
|
520
525
|
|
526
|
+
[groups]
|
527
|
+
g1: u1, u2 # create a group
|
528
|
+
|
521
529
|
[/] # this URL will be mapped to...
|
522
530
|
/srv # ...this folder on the server filesystem
|
523
531
|
accs:
|
@@ -527,6 +535,7 @@ and if you want to use config files instead of commandline args (good!) then her
|
|
527
535
|
/mnt/music # which is mapped to this folder
|
528
536
|
accs:
|
529
537
|
r: u1, u2 # only these accounts can read,
|
538
|
+
r: @g1 # (exactly the same, just with a group instead)
|
530
539
|
rw: u3 # and only u3 can read-write
|
531
540
|
|
532
541
|
[/inc]
|
@@ -1083,6 +1092,9 @@ open the `[🎺]` media-player-settings tab to configure it,
|
|
1083
1092
|
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
1084
1093
|
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the most part
|
1085
1094
|
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
1095
|
+
* `[flac]` -- lossless but compressed, for LAN and/or fiber playback on electrostatic headphones
|
1096
|
+
* `[wav]` -- lossless and uncompressed, for LAN and/or fiber playback on electrostatic headphones connected to very old equipment
|
1097
|
+
* `flac` and `wav` must be enabled with `--allow-flac` / `--allow-wav` to allow spending the disk space
|
1086
1098
|
* "tint" reduces the contrast of the playback bar
|
1087
1099
|
|
1088
1100
|
|
@@ -1424,6 +1436,8 @@ note that this disables hotlinking because the opengraph spec demands it; to sne
|
|
1424
1436
|
|
1425
1437
|
you can also hotlink files regardless by appending `?raw` to the url
|
1426
1438
|
|
1439
|
+
> WARNING: if you plan to use WebDAV, then `--og-ua` / `og_ua` must be configured
|
1440
|
+
|
1427
1441
|
if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
|
1428
1442
|
|
1429
1443
|
|
@@ -1880,6 +1894,8 @@ you can disable the built-in password-based login system, and instead replace it
|
|
1880
1894
|
|
1881
1895
|
* the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
|
1882
1896
|
|
1897
|
+
* if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party
|
1898
|
+
|
1883
1899
|
some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
|
1884
1900
|
|
1885
1901
|
there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)
|
@@ -2428,6 +2444,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|
2428
2444
|
| **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
|
2429
2445
|
| **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
|
2430
2446
|
| **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
|
2447
|
+
| **Nintendo Wii (Opera 9.0 "Internet Channel")** | can browse, can't upload or download (no local storage), can view images - works best with `?b=u`, default view broken |
|
2431
2448
|
|
2432
2449
|
<p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
|
2433
2450
|
|
@@ -2698,6 +2715,10 @@ optionally also specify `--ah-cli` to enter an interactive mode where it will ha
|
|
2698
2715
|
|
2699
2716
|
the default configs take about 0.4 sec and 256 MiB RAM to process a new password on a decent laptop
|
2700
2717
|
|
2718
|
+
when generating hashes using `--ah-cli` for docker or systemd services, make sure it is using the same `--ah-salt` by:
|
2719
|
+
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
2720
|
+
* setting the same `--ah-salt` in both environments
|
2721
|
+
|
2701
2722
|
|
2702
2723
|
## https
|
2703
2724
|
|
@@ -2842,7 +2863,7 @@ then again, if you are already into downloading shady binaries from the internet
|
|
2842
2863
|
|
2843
2864
|
## zipapp
|
2844
2865
|
|
2845
|
-
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2866
|
+
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, is slow, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but, unlike the sfx, it is a completely normal zipfile which does not unpack any temporary files to disk, so it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2846
2867
|
|
2847
2868
|
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
|
2848
2869
|
|
@@ -87,6 +87,10 @@ u = unicode
|
|
87
87
|
printed = []
|
88
88
|
zsid = uuid.uuid4().urn[4:]
|
89
89
|
|
90
|
+
CFG_DEF = [os.environ.get("PRTY_CONFIG", "")]
|
91
|
+
if not CFG_DEF[0]:
|
92
|
+
CFG_DEF.pop()
|
93
|
+
|
90
94
|
|
91
95
|
class RiceFormatter(argparse.HelpFormatter):
|
92
96
|
def __init__(self, *args , **kwargs ) :
|
@@ -566,7 +570,7 @@ def get_sects():
|
|
566
570
|
|
567
571
|
--grp takes groupname:username1,username2,...
|
568
572
|
and groupnames can be used instead of usernames in -v
|
569
|
-
by prefixing the groupname with
|
573
|
+
by prefixing the groupname with @
|
570
574
|
|
571
575
|
list of permissions:
|
572
576
|
"r" (read): list folder contents, download files
|
@@ -982,7 +986,7 @@ def build_flags_desc():
|
|
982
986
|
|
983
987
|
def add_general(ap, nc, srvname):
|
984
988
|
ap2 = ap.add_argument_group('general options')
|
985
|
-
ap2.add_argument("-c", metavar="PATH", type=u, action="append", help="add config file")
|
989
|
+
ap2.add_argument("-c", metavar="PATH", type=u, default=CFG_DEF, action="append", help="add config file")
|
986
990
|
ap2.add_argument("-nc", metavar="NUM", type=int, default=nc, help="max num clients")
|
987
991
|
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all")
|
988
992
|
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
|
@@ -1101,7 +1105,7 @@ def add_tls(ap, cert_path):
|
|
1101
1105
|
ap2 = ap.add_argument_group('SSL/TLS options')
|
1102
1106
|
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
1103
1107
|
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
1104
|
-
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to TLS certificate")
|
1108
|
+
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to file containing a concatenation of TLS key and certificate chain")
|
1105
1109
|
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, default="", help="set allowed ssl/tls versions; [\033[32mhelp\033[0m] shows available versions; default is what your python version considers safe")
|
1106
1110
|
ap2.add_argument("--ciphers", metavar="LIST", type=u, default="", help="set allowed ssl/tls ciphers; [\033[32mhelp\033[0m] shows available ciphers")
|
1107
1111
|
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
@@ -1138,6 +1142,7 @@ def add_auth(ap):
|
|
1138
1142
|
ap2.add_argument("--idp-db", metavar="PATH", type=u, default=idp_db, help="where to store the known IdP users/groups (if you run multiple copyparty instances, make sure they use different DBs)")
|
1139
1143
|
ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP")
|
1140
1144
|
ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)")
|
1145
|
+
ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n └─note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)")
|
1141
1146
|
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
|
1142
1147
|
ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins")
|
1143
1148
|
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
@@ -1435,6 +1440,8 @@ def add_transcoding(ap):
|
|
1435
1440
|
ap2 = ap.add_argument_group('transcoding options')
|
1436
1441
|
ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
|
1437
1442
|
ap2.add_argument("--q-mp3", metavar="QUALITY", type=u, default="q2", help="target quality for transcoding to mp3, for example [\033[32m192k\033[0m] (CBR) or [\033[32mq0\033[0m] (CQ/CRF, q0=maxquality, q9=smallest); set 0 to disable")
|
1443
|
+
ap2.add_argument("--allow-wav", action="store_true", help="allow transcoding to wav (lossless, uncompressed)")
|
1444
|
+
ap2.add_argument("--allow-flac", action="store_true", help="allow transcoding to flac (lossless, compressed)")
|
1438
1445
|
ap2.add_argument("--no-caf", action="store_true", help="disable transcoding to caf-opus (affects iOS v12~v17), will use mp3 instead")
|
1439
1446
|
ap2.add_argument("--no-owa", action="store_true", help="disable transcoding to webm-opus (iOS v18 and later), will use mp3 instead")
|
1440
1447
|
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
@@ -1540,6 +1547,7 @@ def add_ui(ap, retry):
|
|
1540
1547
|
ap2 = ap.add_argument_group('ui options')
|
1541
1548
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1542
1549
|
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
1550
|
+
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
1543
1551
|
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor chi\033[0m")
|
1544
1552
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
1545
1553
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
@@ -1552,7 +1560,7 @@ def add_ui(ap, retry):
|
|
1552
1560
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1553
1561
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
1554
1562
|
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
1555
|
-
ap2.add_argument("--mpmc",
|
1563
|
+
ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS)
|
1556
1564
|
ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
|
1557
1565
|
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
|
1558
1566
|
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
@@ -1777,7 +1785,7 @@ def main(argv = None) :
|
|
1777
1785
|
argv[idx] = nk + ov
|
1778
1786
|
time.sleep(2)
|
1779
1787
|
|
1780
|
-
da = len(argv) == 1
|
1788
|
+
da = len(argv) == 1 and not CFG_DEF
|
1781
1789
|
try:
|
1782
1790
|
if da:
|
1783
1791
|
argv.extend(["--qr"])
|
@@ -1705,7 +1705,8 @@ class AuthSrv(object):
|
|
1705
1705
|
t = "Read-access has been disabled due to failsafe: No volumes were defined by the config-file. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to the working-directory by adding the following arguments: -v .::rw"
|
1706
1706
|
self.log(t, 1)
|
1707
1707
|
axs = AXS()
|
1708
|
-
|
1708
|
+
zvf = {"tcolor": self.args.tcolor}
|
1709
|
+
vfs = VFS(self.log_func, absreal("."), "", "", axs, zvf)
|
1709
1710
|
if not axs.uread:
|
1710
1711
|
self.badcfg1 = True
|
1711
1712
|
elif "" not in mount:
|
@@ -2742,6 +2743,8 @@ class AuthSrv(object):
|
|
2742
2743
|
"s_name": self.args.bname,
|
2743
2744
|
"have_up2k_idx": "e2d" in vf,
|
2744
2745
|
"have_acode": not self.args.no_acode,
|
2746
|
+
"have_c2flac": self.args.allow_flac,
|
2747
|
+
"have_c2wav": self.args.allow_wav,
|
2745
2748
|
"have_shr": self.args.shr,
|
2746
2749
|
"have_zip": not self.args.no_zip,
|
2747
2750
|
"have_mv": not self.args.no_mv,
|
@@ -2766,6 +2769,7 @@ class AuthSrv(object):
|
|
2766
2769
|
"dth3x": vf["th3x"],
|
2767
2770
|
"dvol": self.args.au_vol,
|
2768
2771
|
"idxh": int(self.args.ih),
|
2772
|
+
"dutc": not self.args.localtime,
|
2769
2773
|
"themes": self.args.themes,
|
2770
2774
|
"turbolvl": self.args.turbo,
|
2771
2775
|
"nosubtle": self.args.nosubtle,
|
@@ -2853,7 +2857,10 @@ class AuthSrv(object):
|
|
2853
2857
|
|
2854
2858
|
n = []
|
2855
2859
|
q = "insert into us values (?,?,?)"
|
2856
|
-
|
2860
|
+
accs = list(self.acct)
|
2861
|
+
if self.args.idp_h_usr and self.args.idp_cookie:
|
2862
|
+
accs.extend(self.idp_accs.keys())
|
2863
|
+
for uname in accs:
|
2857
2864
|
if uname not in ases:
|
2858
2865
|
sid = ub64enc(os.urandom(blen)).decode("ascii")
|
2859
2866
|
cur.execute(q, (uname, sid, int(time.time())))
|
@@ -3444,7 +3451,7 @@ def expand_config_file(
|
|
3444
3451
|
ipath += " -> " + fp
|
3445
3452
|
ret.append("#\033[36m opening cfg file{}\033[0m".format(ipath))
|
3446
3453
|
|
3447
|
-
cfg_lines = read_utf8(log, fp, True).split("\n")
|
3454
|
+
cfg_lines = read_utf8(log, fp, True).replace("\t", " ").split("\n")
|
3448
3455
|
if True: # diff-golf
|
3449
3456
|
for oln in [x.rstrip() for x in cfg_lines]:
|
3450
3457
|
ln = oln.split(" #")[0].strip()
|
@@ -107,6 +107,7 @@ from .util import (
|
|
107
107
|
sendfile_py,
|
108
108
|
set_fperms,
|
109
109
|
stat_resource,
|
110
|
+
str_anchor,
|
110
111
|
ub64dec,
|
111
112
|
ub64enc,
|
112
113
|
ujoin,
|
@@ -652,6 +653,9 @@ class HttpCli(object):
|
|
652
653
|
self.pw = ""
|
653
654
|
self.uname = idp_usr
|
654
655
|
self.html_head += "<script>var is_idp=1</script>\n"
|
656
|
+
zs = self.asrv.ases.get(idp_usr)
|
657
|
+
if zs:
|
658
|
+
self.set_idp_cookie(zs)
|
655
659
|
else:
|
656
660
|
self.log("unknown username: %r" % (idp_usr,), 1)
|
657
661
|
|
@@ -906,7 +910,7 @@ class HttpCli(object):
|
|
906
910
|
if status == 304:
|
907
911
|
self.out_headers.pop("Content-Length", None)
|
908
912
|
self.out_headers.pop("Content-Type", None)
|
909
|
-
self.out_headerlist
|
913
|
+
self.out_headerlist[:] = []
|
910
914
|
if self.k304():
|
911
915
|
self.keepalive = False
|
912
916
|
else:
|
@@ -1190,15 +1194,6 @@ class HttpCli(object):
|
|
1190
1194
|
self.reply(b"ssdp is disabled in server config", 404)
|
1191
1195
|
return False
|
1192
1196
|
|
1193
|
-
if self.vpath.startswith(".cpr/dd/") and self.args.mpmc:
|
1194
|
-
if self.args.mpmc == ".":
|
1195
|
-
raise Pebkac(404)
|
1196
|
-
|
1197
|
-
loc = self.args.mpmc.rstrip("/") + self.vpath[self.vpath.rfind("/") :]
|
1198
|
-
h = {"Location": loc, "Cache-Control": "max-age=39"}
|
1199
|
-
self.reply(b"", 301, headers=h)
|
1200
|
-
return True
|
1201
|
-
|
1202
1197
|
if self.vpath == ".cpr/metrics":
|
1203
1198
|
return self.conn.hsrv.metrics.tx(self)
|
1204
1199
|
|
@@ -2074,16 +2069,16 @@ class HttpCli(object):
|
|
2074
2069
|
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
2075
2070
|
lim = vfs.get_dbv(rem)[0].lim
|
2076
2071
|
fdir = vfs.canonical(rem)
|
2077
|
-
if lim:
|
2078
|
-
fdir, rem = lim.all(
|
2079
|
-
self.ip, rem, remains, vfs.realpath, fdir, self.conn.hsrv.broker
|
2080
|
-
)
|
2081
|
-
|
2082
2072
|
fn = None
|
2083
2073
|
if rem and not self.trailing_slash and not bos.path.isdir(fdir):
|
2084
2074
|
fdir, fn = os.path.split(fdir)
|
2085
2075
|
rem, _ = vsplit(rem)
|
2086
2076
|
|
2077
|
+
if lim:
|
2078
|
+
fdir, rem = lim.all(
|
2079
|
+
self.ip, rem, remains, vfs.realpath, fdir, self.conn.hsrv.broker
|
2080
|
+
)
|
2081
|
+
|
2087
2082
|
bos.makedirs(fdir, vf=vfs.flags)
|
2088
2083
|
|
2089
2084
|
open_ka = {"fun": open}
|
@@ -2928,7 +2923,7 @@ class HttpCli(object):
|
|
2928
2923
|
msg = "new password OK"
|
2929
2924
|
|
2930
2925
|
redir = (self.args.SRS + "?h") if ok else ""
|
2931
|
-
h2 = '<a href="' + self.args.SRS + '?h">
|
2926
|
+
h2 = '<a href="' + self.args.SRS + '?h">continue</a>'
|
2932
2927
|
html = self.j2s("msg", h1=msg, h2=h2, redir=redir)
|
2933
2928
|
self.reply(html.encode("utf-8"))
|
2934
2929
|
return True
|
@@ -2956,7 +2951,8 @@ class HttpCli(object):
|
|
2956
2951
|
dst += "_=1#" + html_escape(uhash, True, True)
|
2957
2952
|
|
2958
2953
|
_, msg = self.get_pwd_cookie(pwd)
|
2959
|
-
|
2954
|
+
h2 = '<a href="' + dst + '">continue</a>'
|
2955
|
+
html = self.j2s("msg", h1=msg, h2=h2, redir=dst)
|
2960
2956
|
self.reply(html.encode("utf-8"))
|
2961
2957
|
return True
|
2962
2958
|
|
@@ -2969,7 +2965,7 @@ class HttpCli(object):
|
|
2969
2965
|
self.get_pwd_cookie("x")
|
2970
2966
|
|
2971
2967
|
dst = self.args.SRS + "?h"
|
2972
|
-
h2 = '<a href="' + dst + '">
|
2968
|
+
h2 = '<a href="' + dst + '">continue</a>'
|
2973
2969
|
html = self.j2s("msg", h1="ok bye", h2=h2, redir=dst)
|
2974
2970
|
self.reply(html.encode("utf-8"))
|
2975
2971
|
return True
|
@@ -3022,6 +3018,19 @@ class HttpCli(object):
|
|
3022
3018
|
|
3023
3019
|
return dur > 0, msg
|
3024
3020
|
|
3021
|
+
def set_idp_cookie(self, ases) :
|
3022
|
+
k = "cppws" if self.is_https else "cppwd"
|
3023
|
+
ck = gencookie(
|
3024
|
+
k,
|
3025
|
+
ases,
|
3026
|
+
self.args.R,
|
3027
|
+
self.args.cookie_lax,
|
3028
|
+
self.is_https,
|
3029
|
+
self.args.idp_cookie,
|
3030
|
+
"; HttpOnly",
|
3031
|
+
)
|
3032
|
+
self.out_headers["Set-Cookie"] = ck
|
3033
|
+
|
3025
3034
|
def handle_mkdir(self) :
|
3026
3035
|
new_dir = self.parser.require("name", 512)
|
3027
3036
|
self.parser.drop()
|
@@ -3093,6 +3102,20 @@ class HttpCli(object):
|
|
3093
3102
|
if "fperms" in vfs.flags:
|
3094
3103
|
set_fperms(f, vfs.flags)
|
3095
3104
|
|
3105
|
+
dbv, vrem = vfs.get_dbv(rem)
|
3106
|
+
self.conn.hsrv.broker.say(
|
3107
|
+
"up2k.hash_file",
|
3108
|
+
dbv.realpath,
|
3109
|
+
dbv.vpath,
|
3110
|
+
dbv.flags,
|
3111
|
+
vrem,
|
3112
|
+
sanitized,
|
3113
|
+
self.ip,
|
3114
|
+
bos.stat(fn).st_mtime,
|
3115
|
+
self.uname,
|
3116
|
+
True,
|
3117
|
+
)
|
3118
|
+
|
3096
3119
|
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
3097
3120
|
self.redirect(vpath, "?edit")
|
3098
3121
|
return True
|
@@ -4646,7 +4669,7 @@ class HttpCli(object):
|
|
4646
4669
|
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
4647
4670
|
cfmt = ""
|
4648
4671
|
if self.thumbcli and not self.args.no_bacode:
|
4649
|
-
for zs in ("opus", "mp3", "w", "j", "p"):
|
4672
|
+
for zs in ("opus", "mp3", "flac", "wav", "w", "j", "p"):
|
4650
4673
|
if zs in self.ouparam or uarg == zs:
|
4651
4674
|
cfmt = zs
|
4652
4675
|
|
@@ -5331,15 +5354,16 @@ class HttpCli(object):
|
|
5331
5354
|
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
5332
5355
|
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
5333
5356
|
|
5334
|
-
|
5335
|
-
|
5336
|
-
lm = "ups %r" % (
|
5357
|
+
sfilt = self.uparam.get("filter") or ""
|
5358
|
+
nfi, vfi = str_anchor(sfilt)
|
5359
|
+
lm = "ups %d%r" % (nfi, sfilt)
|
5337
5360
|
|
5338
5361
|
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
5339
5362
|
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
5340
5363
|
else:
|
5341
5364
|
shr_dbv = None
|
5342
5365
|
|
5366
|
+
wret = {}
|
5343
5367
|
ret = []
|
5344
5368
|
t0 = time.time()
|
5345
5369
|
lim = time.time() - self.args.unpost
|
@@ -5361,7 +5385,13 @@ class HttpCli(object):
|
|
5361
5385
|
x = self.conn.hsrv.broker.ask(
|
5362
5386
|
"up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
|
5363
5387
|
)
|
5364
|
-
|
5388
|
+
zdsa = x.get()
|
5389
|
+
uret = []
|
5390
|
+
if "timeout" in zdsa:
|
5391
|
+
wret["nou"] = 1
|
5392
|
+
else:
|
5393
|
+
uret = zdsa["f"]
|
5394
|
+
nu = len(uret)
|
5365
5395
|
|
5366
5396
|
if not self.args.unpost:
|
5367
5397
|
allvols = []
|
@@ -5386,8 +5416,14 @@ class HttpCli(object):
|
|
5386
5416
|
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
5387
5417
|
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
5388
5418
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
5389
|
-
if
|
5390
|
-
|
5419
|
+
if nfi == 0 or (nfi == 1 and vfi in vp):
|
5420
|
+
pass
|
5421
|
+
elif nfi == 2:
|
5422
|
+
if not vp.startswith(vfi):
|
5423
|
+
continue
|
5424
|
+
elif nfi == 3:
|
5425
|
+
if not vp.endswith(vfi):
|
5426
|
+
continue
|
5391
5427
|
|
5392
5428
|
n -= 1
|
5393
5429
|
if not n:
|
@@ -5407,6 +5443,8 @@ class HttpCli(object):
|
|
5407
5443
|
|
5408
5444
|
if len(ret) > 2000:
|
5409
5445
|
ret = ret[:2000]
|
5446
|
+
if len(ret) >= 2000:
|
5447
|
+
wret["oc"] = 1
|
5410
5448
|
|
5411
5449
|
for rv in ret:
|
5412
5450
|
rv["vp"] = quotep(rv["vp"])
|
@@ -5426,6 +5464,13 @@ class HttpCli(object):
|
|
5426
5464
|
)
|
5427
5465
|
rv["vp"] += "?k=" + fk[:nfk]
|
5428
5466
|
|
5467
|
+
if not allvols:
|
5468
|
+
wret["noc"] = 1
|
5469
|
+
ret = []
|
5470
|
+
|
5471
|
+
nc = len(ret)
|
5472
|
+
ret = uret + ret
|
5473
|
+
|
5429
5474
|
if shr_dbv:
|
5430
5475
|
# translate vpaths from share-target to share-url
|
5431
5476
|
# to satisfy access checks
|
@@ -5439,12 +5484,11 @@ class HttpCli(object):
|
|
5439
5484
|
for v in ret:
|
5440
5485
|
v["vp"] = self.args.SR + v["vp"]
|
5441
5486
|
|
5442
|
-
|
5443
|
-
|
5444
|
-
|
5445
|
-
jtxt =
|
5446
|
-
|
5447
|
-
self.log("%s #%d+%d %.2fsec" % (lm, zi, len(ret), time.time() - t0))
|
5487
|
+
wret["f"] = ret
|
5488
|
+
wret["nu"] = nu
|
5489
|
+
wret["nc"] = nc
|
5490
|
+
jtxt = json.dumps(wret, separators=(",\n", ": "))
|
5491
|
+
self.log("%s #%d+%d %.2fsec" % (lm, nu, nc, time.time() - t0))
|
5448
5492
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
5449
5493
|
return True
|
5450
5494
|
|
@@ -5459,8 +5503,8 @@ class HttpCli(object):
|
|
5459
5503
|
raise Pebkac(500, "server busy, cannot list recent uploads; please retry")
|
5460
5504
|
|
5461
5505
|
sfilt = self.uparam.get("filter") or ""
|
5462
|
-
|
5463
|
-
lm = "ru %r" % (sfilt
|
5506
|
+
nfi, vfi = str_anchor(sfilt)
|
5507
|
+
lm = "ru %d%r" % (nfi, sfilt)
|
5464
5508
|
self.log(lm)
|
5465
5509
|
|
5466
5510
|
ret = []
|
@@ -5495,8 +5539,14 @@ class HttpCli(object):
|
|
5495
5539
|
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
5496
5540
|
for sz, rd, fn, ip, at in cur.execute(q):
|
5497
5541
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
5498
|
-
if
|
5499
|
-
|
5542
|
+
if nfi == 0 or (nfi == 1 and vfi in vp):
|
5543
|
+
pass
|
5544
|
+
elif nfi == 2:
|
5545
|
+
if not vp.startswith(vfi):
|
5546
|
+
continue
|
5547
|
+
elif nfi == 3:
|
5548
|
+
if not vp.endswith(vfi):
|
5549
|
+
continue
|
5500
5550
|
|
5501
5551
|
if not dots and "/." in vp:
|
5502
5552
|
continue
|
@@ -319,7 +319,8 @@ class HttpSrv(object):
|
|
319
319
|
spins = 0
|
320
320
|
while self.ncli >= self.nclimax:
|
321
321
|
if not spins:
|
322
|
-
|
322
|
+
t = "at connection limit (global-option 'nc'); waiting"
|
323
|
+
self.log(self.name, t, 3)
|
323
324
|
|
324
325
|
spins += 1
|
325
326
|
time.sleep(0.1)
|