copyparty 1.19.4__tar.gz → 1.19.6__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 (125) hide show
  1. {copyparty-1.19.4 → copyparty-1.19.6}/PKG-INFO +95 -7
  2. {copyparty-1.19.4 → copyparty-1.19.6}/README.md +94 -6
  3. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/__main__.py +85 -32
  4. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/__version__.py +2 -2
  5. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/authsrv.py +28 -1
  6. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/broker_util.py +0 -1
  7. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/cert.py +1 -0
  8. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/cfg.py +2 -0
  9. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/ftpd.py +5 -5
  10. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/httpcli.py +46 -30
  11. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/mdns.py +2 -2
  12. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/multicast.py +3 -3
  13. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/pwhash.py +1 -0
  14. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/smbd.py +1 -1
  15. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/qrcodegen.py +19 -0
  16. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/svchub.py +54 -10
  17. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/tcpsrv.py +37 -4
  18. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/up2k.py +13 -2
  19. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/util.py +30 -0
  20. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/a/u2c.py +6 -6
  21. copyparty-1.19.6/copyparty/web/browser.css.gz +0 -0
  22. copyparty-1.19.6/copyparty/web/browser.js.gz +0 -0
  23. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/md.html +2 -1
  24. copyparty-1.19.6/copyparty/web/md.js.gz +0 -0
  25. copyparty-1.19.6/copyparty/web/md2.js.gz +0 -0
  26. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/mde.html +2 -1
  27. copyparty-1.19.6/copyparty/web/splash.js.gz +0 -0
  28. copyparty-1.19.6/copyparty/web/ui.css.gz +0 -0
  29. copyparty-1.19.6/copyparty/web/up2k.js.gz +0 -0
  30. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/PKG-INFO +95 -7
  31. copyparty-1.19.4/copyparty/web/browser.css.gz +0 -0
  32. copyparty-1.19.4/copyparty/web/browser.js.gz +0 -0
  33. copyparty-1.19.4/copyparty/web/md.js.gz +0 -0
  34. copyparty-1.19.4/copyparty/web/md2.js.gz +0 -0
  35. copyparty-1.19.4/copyparty/web/splash.js.gz +0 -0
  36. copyparty-1.19.4/copyparty/web/ui.css.gz +0 -0
  37. copyparty-1.19.4/copyparty/web/up2k.js.gz +0 -0
  38. {copyparty-1.19.4 → copyparty-1.19.6}/LICENSE +0 -0
  39. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/__init__.py +0 -0
  40. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/bos/__init__.py +0 -0
  41. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/bos/bos.py +0 -0
  42. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/bos/path.py +0 -0
  43. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/broker_mp.py +0 -0
  44. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/broker_mpw.py +0 -0
  45. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/broker_thr.py +0 -0
  46. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/dxml.py +0 -0
  47. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/fsutil.py +0 -0
  48. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/httpconn.py +0 -0
  49. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/httpsrv.py +0 -0
  50. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/ico.py +0 -0
  51. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/metrics.py +0 -0
  52. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/mtag.py +0 -0
  53. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/res/COPYING.txt +0 -0
  54. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/res/__init__.py +0 -0
  55. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/res/insecure.pem +0 -0
  56. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/ssdp.py +0 -0
  57. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/star.py +0 -0
  58. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/__init__.py +0 -0
  59. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/__init__.py +0 -0
  60. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/bimap.py +0 -0
  61. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/bit.py +0 -0
  62. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/buffer.py +0 -0
  63. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/dns.py +0 -0
  64. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/label.py +0 -0
  65. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/lex.py +0 -0
  66. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/dnslib/ranges.py +0 -0
  67. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/ifaddr/__init__.py +0 -0
  68. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/ifaddr/_posix.py +0 -0
  69. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/ifaddr/_shared.py +0 -0
  70. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/ifaddr/_win32.py +0 -0
  71. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/stolen/surrogateescape.py +0 -0
  72. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/sutil.py +0 -0
  73. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/szip.py +0 -0
  74. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/tftpd.py +0 -0
  75. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/th_cli.py +0 -0
  76. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/th_srv.py +0 -0
  77. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/u2idx.py +0 -0
  78. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/a/__init__.py +0 -0
  79. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/a/partyfuse.py +0 -0
  80. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/a/webdav-cfg.bat +0 -0
  81. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/baguettebox.js.gz +0 -0
  82. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/browser.html +0 -0
  83. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/browser2.html +0 -0
  84. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/cf.html +0 -0
  85. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/dbg-audio.js.gz +0 -0
  86. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/__init__.py +0 -0
  87. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/busy.mp3.gz +0 -0
  88. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/easymde.css.gz +0 -0
  89. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/easymde.js.gz +0 -0
  90. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/fuse.py +0 -0
  91. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/marked.js.gz +0 -0
  92. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/mini-fa.css.gz +0 -0
  93. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/mini-fa.woff +0 -0
  94. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/prism.css.gz +0 -0
  95. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/prism.js.gz +0 -0
  96. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/prismd.css.gz +0 -0
  97. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/scp.woff2 +0 -0
  98. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  99. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  100. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/idp.html +0 -0
  101. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/md.css.gz +0 -0
  102. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/md2.css.gz +0 -0
  103. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/mde.css.gz +0 -0
  104. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/mde.js.gz +0 -0
  105. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/msg.css.gz +0 -0
  106. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/msg.html +0 -0
  107. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/rups.css.gz +0 -0
  108. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/rups.html +0 -0
  109. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/rups.js.gz +0 -0
  110. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/shares.css.gz +0 -0
  111. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/shares.html +0 -0
  112. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/shares.js.gz +0 -0
  113. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/splash.css.gz +0 -0
  114. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/splash.html +0 -0
  115. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/svcs.html +0 -0
  116. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/svcs.js.gz +0 -0
  117. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/util.js.gz +0 -0
  118. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty/web/w.hash.js.gz +0 -0
  119. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/SOURCES.txt +0 -0
  120. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/dependency_links.txt +0 -0
  121. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/entry_points.txt +0 -0
  122. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/requires.txt +0 -0
  123. {copyparty-1.19.4 → copyparty-1.19.6}/copyparty.egg-info/top_level.txt +0 -0
  124. {copyparty-1.19.4 → copyparty-1.19.6}/pyproject.toml +0 -0
  125. {copyparty-1.19.4 → copyparty-1.19.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.19.4
3
+ Version: 1.19.6
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
@@ -177,6 +177,7 @@ made in Norway 🇳🇴
177
177
  * [packages](#packages) - the party might be closer than you think
178
178
  * [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
179
179
  * [fedora package](#fedora-package) - does not exist yet
180
+ * [homebrew formulae](#homebrew-formulae) - `brew install copyparty ffmpeg`
180
181
  * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
181
182
  * [nixos module](#nixos-module)
182
183
  * [browser support](#browser-support) - TLDR: yes
@@ -206,6 +207,7 @@ made in Norway 🇳🇴
206
207
  * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
207
208
  * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
208
209
  * [install on android](#install-on-android)
210
+ * [install on iOS](#install-on-iOS)
209
211
  * [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
210
212
  * [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
211
213
 
@@ -220,6 +222,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
220
222
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
221
223
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
222
224
  * or if you are on android, [install copyparty in termux](#install-on-android)
225
+ * or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS)
223
226
  * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
224
227
  * or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
225
228
  * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
@@ -306,7 +309,7 @@ also see [comparison to similar software](./docs/versus.md)
306
309
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
307
310
  * ☑ [event hooks](#event-hooks) / script runner
308
311
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
309
- * ☑ cross-platform (Windows, Linux, Macos, Android, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
312
+ * ☑ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
310
313
  * upload
311
314
  * ☑ basic: plain multipart, ie6 support
312
315
  * ☑ [up2k](#uploading): js, resumable, multithreaded
@@ -329,7 +332,7 @@ also see [comparison to similar software](./docs/versus.md)
329
332
  * ☑ play video files as audio (converted on server)
330
333
  * ☑ create and play [m3u8 playlists](#playlists)
331
334
  * ☑ image gallery with webm player
332
- * ☑ [textfile browser](#textfile-viewer) with syntax hilighting
335
+ * ☑ [textfile browser](#textfile-viewer) with syntax highlighting
333
336
  * ☑ realtime streaming of growing files (logfiles and such)
334
337
  * ☑ [thumbnails](#thumbnails)
335
338
  * ☑ ...of images using Pillow, pyvips, or FFmpeg
@@ -634,6 +637,8 @@ for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mn
634
637
 
635
638
  the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead
636
639
 
640
+ > ℹ️ this also works for single files, because files can also be volumes
641
+
637
642
 
638
643
  ## dotfiles
639
644
 
@@ -896,7 +901,7 @@ the up2k UI is the epitome of polished intuitive experiences:
896
901
  * `[🔎]` switch between upload and [file-search](#file-search) mode
897
902
  * ignore `[🔎]` if you add files by dragging them into the browser
898
903
 
899
- and then theres the tabs below it,
904
+ and then there's the tabs below it,
900
905
  * `[ok]` is the files which completed successfully
901
906
  * `[ng]` is the ones that failed / got rejected (already exists, ...)
902
907
  * `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order
@@ -1128,7 +1133,7 @@ plays almost every audio format there is (if the server has FFmpeg installed fo
1128
1133
 
1129
1134
  the following audio formats are usually always playable, even without FFmpeg: `aac|flac|m4a|mp3|ogg|opus|wav`
1130
1135
 
1131
- some hilights:
1136
+ some highlights:
1132
1137
  * OS integration; control playback from your phone's lockscreen ([windows](https://user-images.githubusercontent.com/241032/233213022-298a98ba-721a-4cf1-a3d4-f62634bc53d5.png) // [iOS](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) // [android](https://user-images.githubusercontent.com/241032/233212311-a7368590-08c7-4f9f-a1af-48ccf3f36fad.png))
1133
1138
  * shows the audio waveform in the seekbar
1134
1139
  * not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
@@ -1359,6 +1364,12 @@ print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/
1359
1364
  * `--qrl lootbox/?pw=hunter2` appends to the url, linking to the `lootbox` folder with password `hunter2`
1360
1365
  * `--qrz 1` forces 1x zoom instead of autoscaling to fit the terminal size
1361
1366
  * 1x may render incorrectly on some terminals/fonts, but 2x should always work
1367
+ * `--qr-pin 1` makes the qr-code stick to the bottom of the console (never scrolls away)
1368
+ * `--qr-file qr.txt:1:2` writes a small qr-code to `qr.txt`
1369
+ * `--qr-file qr.txt:2:2` writes a big qr-code to `qr.txt`
1370
+ * `--qr-file qr.svg:1:2` writes a vector-graphics qr-code to `qr.svg`
1371
+ * `--qr-file qr.png:8:4:333333:ffcc55` writes an 8x-magnified yellow-on-gray `qr.png`
1372
+ * `--qr-file qr.png:8:4::ffffff` writes an 8x-magnified white-on-transparent `qr.png`
1362
1373
 
1363
1374
  it uses the server hostname if [mdns](#mdns) is enabled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain
1364
1375
 
@@ -1383,6 +1394,14 @@ some recommended FTP / FTPS clients; `wark` = example password:
1383
1394
  * `lftp -u k,wark -p 3921 127.0.0.1 -e ls`
1384
1395
  * `lftp -u k,wark -p 3990 127.0.0.1 -e 'set ssl:verify-certificate no; ls'`
1385
1396
 
1397
+ config file example, which restricts FTP to only use ports 3921 and 12000-12099 so all of those ports must be opened in your firewall:
1398
+
1399
+ ```yaml
1400
+ [global]
1401
+ ftp: 3921
1402
+ ftp-pr: 12000-12099
1403
+ ```
1404
+
1386
1405
 
1387
1406
  ## webdav server
1388
1407
 
@@ -1477,6 +1496,7 @@ and some minor issues,
1477
1496
  * win10 onwards does not allow connecting anonymously / without accounts
1478
1497
  * python3 only
1479
1498
  * slow (the builtin webdav support in windows is 5x faster, and rclone-webdav is 30x faster)
1499
+ * those numbers are specifically for copyparty's smb-server (because it sucks); other smb-servers should be similar to webdav
1480
1500
 
1481
1501
  known client bugs:
1482
1502
  * on win7 only, `--smb1` is much faster than smb2 (default) because it keeps rescanning folders on smb2
@@ -2225,7 +2245,7 @@ when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-f
2225
2245
 
2226
2246
  in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)
2227
2247
 
2228
- * if these results are bullshit because my config exampels are bad, please submit corrections!
2248
+ * if these results are bullshit because my config examples are bad, please submit corrections!
2229
2249
 
2230
2250
 
2231
2251
  ## permanent cloudflare tunnel
@@ -2396,6 +2416,15 @@ after installing, start either the system service or the user service and naviga
2396
2416
  does not exist yet; there are rumours that it is being packaged! keep an eye on this space...
2397
2417
 
2398
2418
 
2419
+ ## homebrew formulae
2420
+
2421
+ `brew install copyparty ffmpeg` -- https://formulae.brew.sh/formula/copyparty
2422
+
2423
+ should work on all macs (both intel and apple silicon) and all relevant macos versions
2424
+
2425
+ the homebrew package is maintained by the homebrew team (thanks!)
2426
+
2427
+
2399
2428
  ## nix package
2400
2429
 
2401
2430
  `nix profile install github:9001/copyparty`
@@ -2409,7 +2438,7 @@ some recommended dependencies are enabled by default; [override the package](htt
2409
2438
 
2410
2439
  ## nixos module
2411
2440
 
2412
- for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes) installation of NixOS.
2441
+ for [flake-enabled](https://nixos.wiki/wiki/Flakes) installations of NixOS:
2413
2442
 
2414
2443
  ```nix
2415
2444
  {
@@ -2436,6 +2465,33 @@ for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes)
2436
2465
  }
2437
2466
  ```
2438
2467
 
2468
+ if you don't use a flake in your configuration, you can use other dependency management tools like [npins](https://github.com/andir/npins), [niv](https://github.com/nmattia/niv), or even plain [`fetchTarball`](https://nix.dev/manual/nix/stable/language/builtins#builtins-fetchTarball), like so:
2469
+
2470
+ ```nix
2471
+ { pkgs, ... }:
2472
+
2473
+ let
2474
+ # npins example, adjust for your setup. copyparty should be a path to the downloaded repo
2475
+ # for niv, just replace the npins folder import with the sources.nix file
2476
+ copyparty = (import ./npins).copyparty;
2477
+
2478
+ # or with fetchTarball:
2479
+ copyparty = fetchTarball "https://github.com/9001/copyparty/archive/hovudstraum.tar.gz";
2480
+ in
2481
+
2482
+ {
2483
+ # load the copyparty NixOS module
2484
+ imports = [ "${copyparty}/contrib/nixos/modules/copyparty.nix" ];
2485
+
2486
+ # add the copyparty overlay to expose the package to the module
2487
+ nixpkgs.overlays = [ (import "${copyparty}/contrib/package/nix/overlay.nix") ];
2488
+ # (optional) install the package globally
2489
+ environment.systemPackages = [ pkgs.copyparty ];
2490
+ # configure the copyparty module
2491
+ services.copyparty.enable = true;
2492
+ }
2493
+ ```
2494
+
2439
2495
  copyparty on NixOS is configured via `services.copyparty` options, for example:
2440
2496
  ```nix
2441
2497
  services.copyparty = {
@@ -2622,11 +2678,20 @@ sync folders to/from copyparty
2622
2678
 
2623
2679
  NOTE: full bidirectional sync, like what [nextcloud](https://docs.nextcloud.com/server/latest/user_manual/sv/files/desktop_mobile_sync.html) and [syncthing](https://syncthing.net/) does, will never be supported! Only single-direction sync (server-to-client, or client-to-server) is possible with copyparty
2624
2680
 
2681
+ * if you want bidirectional sync, then copyparty and syncthing *should* be entirely safe to combine; they should be able to collaborate on the same folders without causing any trouble for eachother. Many people do this, and there have been no issues so far. But, if you *do* encounter any problems, please [file a copyparty bug](https://github.com/9001/copyparty/issues/new/choose) and I'll try to help -- just keep in mind I've never used syncthing before :-)
2682
+
2625
2683
  the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
2626
2684
 
2685
+ if you want to sync with `u2c.py` then:
2686
+ * the `e2dsa` option (either globally or volflag) must be enabled on the server for the volumes you're syncing into
2687
+ * ...but DON'T enable global-options `no-hash` or `no-idx` (or volflags `nohash` / `noidx`), or at least make sure they are configured so they do not affect anything you are syncing into
2688
+ * ...and u2c needs the delete-permission, so either `rwd` at minimum, or just `A` which is the same as `rwmd.a`
2689
+ * quick reminder that `a` and `A` are different permissions, and `.` is very useful for sync
2690
+
2627
2691
  alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
2628
2692
 
2629
2693
  * starting from rclone v1.63, rclone is faster than u2c.py on low-latency connections
2694
+ * but this is only true for the initial upload; u2c will be faster for periodic syncing
2630
2695
 
2631
2696
 
2632
2697
  ## mount as drive
@@ -2668,6 +2733,8 @@ there is no iPhone app, but the following shortcuts are almost as good:
2668
2733
  * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
2669
2734
  * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
2670
2735
 
2736
+ if you want to run the copyparty server on your iPhone or iPad, see [install on iOS](#install-on-iOS)
2737
+
2671
2738
 
2672
2739
  # performance
2673
2740
 
@@ -3001,6 +3068,27 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132
3001
3068
  * or if you want to use `vips` for photo-thumbs instead, `pkg install libvips && python -m pip install --user -U wheel && python -m pip install --user -U pyvips && (cd /data/data/com.termux/files/usr/lib/; ln -s libgobject-2.0.so{,.0}; ln -s libvips.so{,.42})`
3002
3069
 
3003
3070
 
3071
+ # install on iOS
3072
+
3073
+ first install one of the following:
3074
+ * [a-Shell mini](https://apps.apple.com/us/app/a-shell-mini/id1543537943) gives you the essential features
3075
+ * [a-Shell](https://apps.apple.com/us/app/a-shell/id1473805438) also enables audio transcoding and better thubmnails
3076
+
3077
+ and then copypaste the following command into `a-Shell`:
3078
+
3079
+ ```sh
3080
+ curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
3081
+ ```
3082
+
3083
+ what this does:
3084
+ * creates a basic [config file](#accounts-and-volumes) named `cpc` which you can edit with `vim cpc`
3085
+ * adds the command `cpp` to launch copyparty with that config file
3086
+
3087
+ known issues:
3088
+ * cannot run in the background; it needs to be on-screen to accept connections / uploads / downloads
3089
+ * the best way to exit copyparty is to swipe away the app
3090
+
3091
+
3004
3092
  # reporting bugs
3005
3093
 
3006
3094
  ideas for context to include, and where to submit them
@@ -112,6 +112,7 @@ made in Norway 🇳🇴
112
112
  * [packages](#packages) - the party might be closer than you think
113
113
  * [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
114
114
  * [fedora package](#fedora-package) - does not exist yet
115
+ * [homebrew formulae](#homebrew-formulae) - `brew install copyparty ffmpeg`
115
116
  * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
116
117
  * [nixos module](#nixos-module)
117
118
  * [browser support](#browser-support) - TLDR: yes
@@ -141,6 +142,7 @@ made in Norway 🇳🇴
141
142
  * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
142
143
  * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
143
144
  * [install on android](#install-on-android)
145
+ * [install on iOS](#install-on-iOS)
144
146
  * [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
145
147
  * [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
146
148
 
@@ -155,6 +157,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
155
157
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
156
158
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
157
159
  * or if you are on android, [install copyparty in termux](#install-on-android)
160
+ * or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS)
158
161
  * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
159
162
  * or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty`
160
163
  * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
@@ -241,7 +244,7 @@ also see [comparison to similar software](./docs/versus.md)
241
244
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
242
245
  * ☑ [event hooks](#event-hooks) / script runner
243
246
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
244
- * ☑ cross-platform (Windows, Linux, Macos, Android, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
247
+ * ☑ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
245
248
  * upload
246
249
  * ☑ basic: plain multipart, ie6 support
247
250
  * ☑ [up2k](#uploading): js, resumable, multithreaded
@@ -264,7 +267,7 @@ also see [comparison to similar software](./docs/versus.md)
264
267
  * ☑ play video files as audio (converted on server)
265
268
  * ☑ create and play [m3u8 playlists](#playlists)
266
269
  * ☑ image gallery with webm player
267
- * ☑ [textfile browser](#textfile-viewer) with syntax hilighting
270
+ * ☑ [textfile browser](#textfile-viewer) with syntax highlighting
268
271
  * ☑ realtime streaming of growing files (logfiles and such)
269
272
  * ☑ [thumbnails](#thumbnails)
270
273
  * ☑ ...of images using Pillow, pyvips, or FFmpeg
@@ -569,6 +572,8 @@ for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mn
569
572
 
570
573
  the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead
571
574
 
575
+ > ℹ️ this also works for single files, because files can also be volumes
576
+
572
577
 
573
578
  ## dotfiles
574
579
 
@@ -831,7 +836,7 @@ the up2k UI is the epitome of polished intuitive experiences:
831
836
  * `[🔎]` switch between upload and [file-search](#file-search) mode
832
837
  * ignore `[🔎]` if you add files by dragging them into the browser
833
838
 
834
- and then theres the tabs below it,
839
+ and then there's the tabs below it,
835
840
  * `[ok]` is the files which completed successfully
836
841
  * `[ng]` is the ones that failed / got rejected (already exists, ...)
837
842
  * `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order
@@ -1063,7 +1068,7 @@ plays almost every audio format there is (if the server has FFmpeg installed fo
1063
1068
 
1064
1069
  the following audio formats are usually always playable, even without FFmpeg: `aac|flac|m4a|mp3|ogg|opus|wav`
1065
1070
 
1066
- some hilights:
1071
+ some highlights:
1067
1072
  * OS integration; control playback from your phone's lockscreen ([windows](https://user-images.githubusercontent.com/241032/233213022-298a98ba-721a-4cf1-a3d4-f62634bc53d5.png) // [iOS](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) // [android](https://user-images.githubusercontent.com/241032/233212311-a7368590-08c7-4f9f-a1af-48ccf3f36fad.png))
1068
1073
  * shows the audio waveform in the seekbar
1069
1074
  * not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
@@ -1294,6 +1299,12 @@ print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/
1294
1299
  * `--qrl lootbox/?pw=hunter2` appends to the url, linking to the `lootbox` folder with password `hunter2`
1295
1300
  * `--qrz 1` forces 1x zoom instead of autoscaling to fit the terminal size
1296
1301
  * 1x may render incorrectly on some terminals/fonts, but 2x should always work
1302
+ * `--qr-pin 1` makes the qr-code stick to the bottom of the console (never scrolls away)
1303
+ * `--qr-file qr.txt:1:2` writes a small qr-code to `qr.txt`
1304
+ * `--qr-file qr.txt:2:2` writes a big qr-code to `qr.txt`
1305
+ * `--qr-file qr.svg:1:2` writes a vector-graphics qr-code to `qr.svg`
1306
+ * `--qr-file qr.png:8:4:333333:ffcc55` writes an 8x-magnified yellow-on-gray `qr.png`
1307
+ * `--qr-file qr.png:8:4::ffffff` writes an 8x-magnified white-on-transparent `qr.png`
1297
1308
 
1298
1309
  it uses the server hostname if [mdns](#mdns) is enabled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain
1299
1310
 
@@ -1318,6 +1329,14 @@ some recommended FTP / FTPS clients; `wark` = example password:
1318
1329
  * `lftp -u k,wark -p 3921 127.0.0.1 -e ls`
1319
1330
  * `lftp -u k,wark -p 3990 127.0.0.1 -e 'set ssl:verify-certificate no; ls'`
1320
1331
 
1332
+ config file example, which restricts FTP to only use ports 3921 and 12000-12099 so all of those ports must be opened in your firewall:
1333
+
1334
+ ```yaml
1335
+ [global]
1336
+ ftp: 3921
1337
+ ftp-pr: 12000-12099
1338
+ ```
1339
+
1321
1340
 
1322
1341
  ## webdav server
1323
1342
 
@@ -1412,6 +1431,7 @@ and some minor issues,
1412
1431
  * win10 onwards does not allow connecting anonymously / without accounts
1413
1432
  * python3 only
1414
1433
  * slow (the builtin webdav support in windows is 5x faster, and rclone-webdav is 30x faster)
1434
+ * those numbers are specifically for copyparty's smb-server (because it sucks); other smb-servers should be similar to webdav
1415
1435
 
1416
1436
  known client bugs:
1417
1437
  * on win7 only, `--smb1` is much faster than smb2 (default) because it keeps rescanning folders on smb2
@@ -2160,7 +2180,7 @@ when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-f
2160
2180
 
2161
2181
  in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)
2162
2182
 
2163
- * if these results are bullshit because my config exampels are bad, please submit corrections!
2183
+ * if these results are bullshit because my config examples are bad, please submit corrections!
2164
2184
 
2165
2185
 
2166
2186
  ## permanent cloudflare tunnel
@@ -2331,6 +2351,15 @@ after installing, start either the system service or the user service and naviga
2331
2351
  does not exist yet; there are rumours that it is being packaged! keep an eye on this space...
2332
2352
 
2333
2353
 
2354
+ ## homebrew formulae
2355
+
2356
+ `brew install copyparty ffmpeg` -- https://formulae.brew.sh/formula/copyparty
2357
+
2358
+ should work on all macs (both intel and apple silicon) and all relevant macos versions
2359
+
2360
+ the homebrew package is maintained by the homebrew team (thanks!)
2361
+
2362
+
2334
2363
  ## nix package
2335
2364
 
2336
2365
  `nix profile install github:9001/copyparty`
@@ -2344,7 +2373,7 @@ some recommended dependencies are enabled by default; [override the package](htt
2344
2373
 
2345
2374
  ## nixos module
2346
2375
 
2347
- for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes) installation of NixOS.
2376
+ for [flake-enabled](https://nixos.wiki/wiki/Flakes) installations of NixOS:
2348
2377
 
2349
2378
  ```nix
2350
2379
  {
@@ -2371,6 +2400,33 @@ for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes)
2371
2400
  }
2372
2401
  ```
2373
2402
 
2403
+ if you don't use a flake in your configuration, you can use other dependency management tools like [npins](https://github.com/andir/npins), [niv](https://github.com/nmattia/niv), or even plain [`fetchTarball`](https://nix.dev/manual/nix/stable/language/builtins#builtins-fetchTarball), like so:
2404
+
2405
+ ```nix
2406
+ { pkgs, ... }:
2407
+
2408
+ let
2409
+ # npins example, adjust for your setup. copyparty should be a path to the downloaded repo
2410
+ # for niv, just replace the npins folder import with the sources.nix file
2411
+ copyparty = (import ./npins).copyparty;
2412
+
2413
+ # or with fetchTarball:
2414
+ copyparty = fetchTarball "https://github.com/9001/copyparty/archive/hovudstraum.tar.gz";
2415
+ in
2416
+
2417
+ {
2418
+ # load the copyparty NixOS module
2419
+ imports = [ "${copyparty}/contrib/nixos/modules/copyparty.nix" ];
2420
+
2421
+ # add the copyparty overlay to expose the package to the module
2422
+ nixpkgs.overlays = [ (import "${copyparty}/contrib/package/nix/overlay.nix") ];
2423
+ # (optional) install the package globally
2424
+ environment.systemPackages = [ pkgs.copyparty ];
2425
+ # configure the copyparty module
2426
+ services.copyparty.enable = true;
2427
+ }
2428
+ ```
2429
+
2374
2430
  copyparty on NixOS is configured via `services.copyparty` options, for example:
2375
2431
  ```nix
2376
2432
  services.copyparty = {
@@ -2557,11 +2613,20 @@ sync folders to/from copyparty
2557
2613
 
2558
2614
  NOTE: full bidirectional sync, like what [nextcloud](https://docs.nextcloud.com/server/latest/user_manual/sv/files/desktop_mobile_sync.html) and [syncthing](https://syncthing.net/) does, will never be supported! Only single-direction sync (server-to-client, or client-to-server) is possible with copyparty
2559
2615
 
2616
+ * if you want bidirectional sync, then copyparty and syncthing *should* be entirely safe to combine; they should be able to collaborate on the same folders without causing any trouble for eachother. Many people do this, and there have been no issues so far. But, if you *do* encounter any problems, please [file a copyparty bug](https://github.com/9001/copyparty/issues/new/choose) and I'll try to help -- just keep in mind I've never used syncthing before :-)
2617
+
2560
2618
  the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
2561
2619
 
2620
+ if you want to sync with `u2c.py` then:
2621
+ * the `e2dsa` option (either globally or volflag) must be enabled on the server for the volumes you're syncing into
2622
+ * ...but DON'T enable global-options `no-hash` or `no-idx` (or volflags `nohash` / `noidx`), or at least make sure they are configured so they do not affect anything you are syncing into
2623
+ * ...and u2c needs the delete-permission, so either `rwd` at minimum, or just `A` which is the same as `rwmd.a`
2624
+ * quick reminder that `a` and `A` are different permissions, and `.` is very useful for sync
2625
+
2562
2626
  alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
2563
2627
 
2564
2628
  * starting from rclone v1.63, rclone is faster than u2c.py on low-latency connections
2629
+ * but this is only true for the initial upload; u2c will be faster for periodic syncing
2565
2630
 
2566
2631
 
2567
2632
  ## mount as drive
@@ -2603,6 +2668,8 @@ there is no iPhone app, but the following shortcuts are almost as good:
2603
2668
  * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
2604
2669
  * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
2605
2670
 
2671
+ if you want to run the copyparty server on your iPhone or iPad, see [install on iOS](#install-on-iOS)
2672
+
2606
2673
 
2607
2674
  # performance
2608
2675
 
@@ -2936,6 +3003,27 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132
2936
3003
  * or if you want to use `vips` for photo-thumbs instead, `pkg install libvips && python -m pip install --user -U wheel && python -m pip install --user -U pyvips && (cd /data/data/com.termux/files/usr/lib/; ln -s libgobject-2.0.so{,.0}; ln -s libvips.so{,.42})`
2937
3004
 
2938
3005
 
3006
+ # install on iOS
3007
+
3008
+ first install one of the following:
3009
+ * [a-Shell mini](https://apps.apple.com/us/app/a-shell-mini/id1543537943) gives you the essential features
3010
+ * [a-Shell](https://apps.apple.com/us/app/a-shell/id1473805438) also enables audio transcoding and better thubmnails
3011
+
3012
+ and then copypaste the following command into `a-Shell`:
3013
+
3014
+ ```sh
3015
+ curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
3016
+ ```
3017
+
3018
+ what this does:
3019
+ * creates a basic [config file](#accounts-and-volumes) named `cpc` which you can edit with `vim cpc`
3020
+ * adds the command `cpp` to launch copyparty with that config file
3021
+
3022
+ known issues:
3023
+ * cannot run in the background; it needs to be on-screen to accept connections / uploads / downloads
3024
+ * the best way to exit copyparty is to swipe away the app
3025
+
3026
+
2939
3027
  # reporting bugs
2940
3028
 
2941
3029
  ideas for context to include, and where to submit them
@@ -190,35 +190,41 @@ def init_E(EE ) :
190
190
  (unicode, "/tmp"),
191
191
  ]
192
192
  errs = []
193
- for chk in [os.listdir, os.mkdir]:
194
- for npath, (pf, pa) in enumerate(paths):
195
- p = ""
196
- try:
197
- p = pf(pa)
198
- # print(chk.__name__, p, pa)
199
- if not p or p.startswith("~"):
200
- continue
201
-
202
- p = os.path.normpath(p)
203
- chk(p) # type: ignore
204
- p = os.path.join(p, "copyparty")
205
- if not os.path.isdir(p):
206
- os.mkdir(p)
207
-
208
- if npath > 1:
209
- t = "Using [%s] for config; filekeys/dirkeys will change on every restart. Consider setting XDG_CONFIG_HOME or giving the unix-user a ~/.config/"
210
- errs.append(t % (p,))
211
- elif errs:
212
- errs.append("Using [%s] instead" % (p,))
213
-
214
- if errs:
215
- warn(". ".join(errs))
216
-
217
- return p # type: ignore
218
- except Exception as ex:
219
- if p and npath < 2:
220
- t = "Unable to store config in [%s] due to %r"
221
- errs.append(t % (p, ex))
193
+ for npath, (pf, pa) in enumerate(paths):
194
+ p = ""
195
+ try:
196
+ p = pf(pa)
197
+ if not p or p.startswith("~"):
198
+ continue
199
+
200
+ p = os.path.normpath(p)
201
+ if os.path.isdir(p) and os.listdir(p):
202
+ mkdir = False
203
+ else:
204
+ mkdir = True
205
+ os.mkdir(p)
206
+
207
+ p = os.path.join(p, "copyparty")
208
+ if not os.path.isdir(p):
209
+ os.mkdir(p)
210
+
211
+ if npath > 1:
212
+ t = "Using %s/copyparty [%s] for config; filekeys/dirkeys will change on every restart. Consider setting XDG_CONFIG_HOME or giving the unix-user a ~/.config/"
213
+ errs.append(t % (pa, p))
214
+ elif mkdir:
215
+ t = "Using %s/copyparty [%s] for config%s (Warning: %s did not exist and was created just now)"
216
+ errs.append(t % (pa, p, " instead" if npath else "", pa))
217
+ elif errs:
218
+ errs.append("Using %s/copyparty [%s] instead" % (pa, p))
219
+
220
+ if errs:
221
+ warn(". ".join(errs))
222
+
223
+ return p # type: ignore
224
+ except Exception as ex:
225
+ if p and npath < 2:
226
+ t = "Unable to store config in %s [%s] due to %r"
227
+ errs.append(t % (pa, p, ex))
222
228
 
223
229
  raise Exception("could not find a writable path for config")
224
230
 
@@ -662,6 +668,42 @@ def get_sects():
662
668
  """
663
669
  ),
664
670
  ],
671
+ [
672
+ "auth-ord",
673
+ "authentication precedence",
674
+ dedent(
675
+ """
676
+ \033[33m--auth-ord\033[0m is a comma-separated list of auth options
677
+ (one or more of the [\033[35moptions\033[0m] below); first one wins
678
+
679
+ [\033[35mpw\033[0m] is conventional login, for example the "\033[36mPW\033[0m" header,
680
+ or the \033[36m?pw=\033[0m[...] URL-suffix, or a valid session cookie
681
+ (see \033[33m--help-auth\033[0m)
682
+
683
+ [\033[35midp\033[0m] is a username provided in the http-request-header
684
+ defined by \033[33m--idp-h-usr\033[0m and/or \033[33m--idp-hm-usr\033[0m, which is
685
+ provided by an authentication middleware such as
686
+ authentik, authelia, tailscale, ... (see \033[33m--help-idp\033[0m)
687
+
688
+ [\033[35midp-h\033[0m] is specifically an \033[33m--idp-h-usr\033[0m header,
689
+ [\033[35midp-hm\033[0m] is specifically an \033[33m--idp-hm-usr\033[0m header;
690
+ [\033[35midp\033[0m] is the same as [\033[35midp-hm,idp-h\033[0m]
691
+
692
+ [\033[35mipu\033[0m] is a mapping from an IP-address to a username,
693
+ auto-authing that client-IP to that account
694
+ (see the description of \033[36m--ipu\033[0m in \033[33m--help\033[0m)
695
+
696
+ NOTE: even if an option (\033[35mpw\033[0m/\033[35mipu\033[0m/...) is not in the list,
697
+ it may still be enabled and can still take effect if
698
+ none of the other alternatives identify the user
699
+
700
+ NOTE: if [\033[35mipu\033[0m] is in the list, it must be FIRST or LAST
701
+
702
+ NOTE: if [\033[35mpw\033[0m] is not in the list, the logout-button
703
+ will be hidden when any idp feature is enabled
704
+ """
705
+ ),
706
+ ],
665
707
  [
666
708
  "flags",
667
709
  "list of volflags",
@@ -767,7 +809,7 @@ def get_sects():
767
809
  \033[36mc0\033[35m show all process output (default)
768
810
  \033[36mc1\033[35m show only stderr
769
811
  \033[36mc2\033[35m show only stdout
770
- \033[36mc3\033[35m mute all process otput
812
+ \033[36mc3\033[35m mute all process output
771
813
  \033[0m
772
814
  examples:
773
815
 
@@ -1106,6 +1148,10 @@ def add_qr(ap, tty):
1106
1148
  ap2.add_argument("--qrp", metavar="CELLS", type=int, default=4, help="padding (spec says 4 or more, but 1 is usually fine)")
1107
1149
  ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)")
1108
1150
  ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr")
1151
+ ap2.add_argument("--qr-wait", metavar="SEC", type=float, default=0, help="wait \033[33mSEC\033[0m before printing the qr-code to the log")
1152
+ ap2.add_argument("--qr-every", metavar="SEC", type=float, default=0, help="print the qr-code every \033[33mSEC\033[0m (try this with/without --qr-pin in case of issues)")
1153
+ ap2.add_argument("--qr-winch", metavar="SEC", type=float, default=0, help="when --qr-pin is enabled, check for terminal size change every \033[33mSEC\033[0m")
1154
+ ap2.add_argument("--qr-file", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m write qr-code to file.\n └─To create txt or svg, \033[33mTXT\033[0m is Filepath:Zoom:Pad, for example [\033[32mqr.txt:1:2\033[0m]\n └─To create png or gif, \033[33mTXT\033[0m is Filepath:Zoom:Pad:Foreground:Background, for example [\033[32mqr.png:8:2:333333:ffcc55\033[0m], or [\033[32mqr.png:8:2::ffcc55\033[0m] for transparent")
1109
1155
 
1110
1156
 
1111
1157
  def add_fs(ap):
@@ -1230,7 +1276,7 @@ def add_auth(ap):
1230
1276
  ses_db = os.path.join(E.cfg, "sessions.db")
1231
1277
  ap2 = ap.add_argument_group("IdP / identity provider / user authentication options")
1232
1278
  ap2.add_argument("--idp-h-usr", metavar="HN", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy")
1233
- ap2.add_argument("--idp-hm-usr", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp")
1279
+ ap2.add_argument("--idp-hm-usr", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mT\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp")
1234
1280
  ap2.add_argument("--idp-h-grp", metavar="HN", type=u, default="", help="assume the request-header \033[33mHN\033[0m contains the groupname of the requesting user; can be referenced in config files for group-based access control")
1235
1281
  ap2.add_argument("--idp-h-key", metavar="HN", type=u, default="", help="optional but recommended safeguard; your reverse-proxy will insert a secret header named \033[33mHN\033[0m into all requests, and the other IdP headers will be ignored if this header is not present")
1236
1282
  ap2.add_argument("--idp-gsep", metavar="RE", type=u, default="|:;+,", help="if there are multiple groups in \033[33m--idp-h-grp\033[0m, they are separated by one of the characters in \033[33mRE\033[0m")
@@ -1238,6 +1284,7 @@ def add_auth(ap):
1238
1284
  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")
1239
1285
  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)")
1240
1286
  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)")
1287
+ ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see --help-auth-ord")
1241
1288
  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")
1242
1289
  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")
1243
1290
  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)")
@@ -1248,6 +1295,10 @@ def add_auth(ap):
1248
1295
  ap2.add_argument("--ipr", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m username \033[33mUSR\033[0m can only connect from an IP matching one or more \033[33mCIDR\033[0m (comma-sep.); example: [\033[32m192.168.123.0/24,172.16.0.0/16=dave]")
1249
1296
  ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS)
1250
1297
  ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS)
1298
+ ap2.add_argument("--ao-idp-before-pw", type=u, default="", help=argparse.SUPPRESS)
1299
+ ap2.add_argument("--ao-h-before-hm", type=u, default="", help=argparse.SUPPRESS)
1300
+ ap2.add_argument("--ao-ipu-wins", type=u, default="", help=argparse.SUPPRESS)
1301
+ ap2.add_argument("--ao-has-pw", type=u, default="", help=argparse.SUPPRESS)
1251
1302
 
1252
1303
 
1253
1304
  def add_chpw(ap):
@@ -1487,6 +1538,7 @@ def add_logging(ap):
1487
1538
  ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
1488
1539
  ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
1489
1540
  ap2.add_argument("--log-badpwd", metavar="N", type=int, default=2, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
1541
+ ap2.add_argument("--log-badxml", action="store_true", help="log any invalid XML received from a client")
1490
1542
  ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
1491
1543
  ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
1492
1544
  ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
@@ -1624,6 +1676,7 @@ def add_db_metadata(ap):
1624
1676
 
1625
1677
  def add_txt(ap):
1626
1678
  ap2 = ap.add_argument_group("textfile options")
1679
+ ap2.add_argument("--md-no-br", action="store_true", help="markdown: disable newline-is-newline; will only render a newline into the html given two trailing spaces or a double-newline (volflag=md_no_br)")
1627
1680
  ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of markdown files; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)")
1628
1681
  ap2.add_argument("--txt-eol", metavar="TYPE", type=u, default="", help="enable EOL conversion when writing documents; supported: CRLF, LF (volflag=txt_eol)")
1629
1682
  ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="the textfile editor will check for serverside changes every \033[33mSEC\033[0m seconds")
@@ -1957,7 +2010,7 @@ def main(argv = None) :
1957
2010
  if not HAVE_IPV6 and al.i == "::":
1958
2011
  al.i = "0.0.0.0"
1959
2012
 
1960
- al.i = al.i.split(",")
2013
+ al.i = [x.strip() for x in al.i.split(",")]
1961
2014
  try:
1962
2015
  if "-" in al.p:
1963
2016
  lo, hi = [int(x) for x in al.p.split("-")]