copyparty 1.17.2__tar.gz → 1.18.1__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 (129) hide show
  1. {copyparty-1.17.2 → copyparty-1.18.1}/PKG-INFO +32 -7
  2. {copyparty-1.17.2 → copyparty-1.18.1}/README.md +31 -6
  3. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/__init__.py +1 -0
  4. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/__main__.py +18 -0
  5. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/__version__.py +3 -3
  6. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/authsrv.py +93 -17
  7. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/cfg.py +14 -0
  8. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/httpcli.py +214 -13
  9. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/httpconn.py +3 -0
  10. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/httpsrv.py +16 -0
  11. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/svchub.py +100 -25
  12. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/th_srv.py +4 -0
  13. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/util.py +21 -5
  14. copyparty-1.18.1/copyparty/web/browser.css.gz +0 -0
  15. copyparty-1.18.1/copyparty/web/browser.js.gz +0 -0
  16. copyparty-1.18.1/copyparty/web/deps/marked.js.gz +0 -0
  17. copyparty-1.18.1/copyparty/web/idp.html +55 -0
  18. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/splash.html +4 -0
  19. copyparty-1.18.1/copyparty/web/splash.js.gz +0 -0
  20. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/ui.css.gz +0 -0
  21. copyparty-1.18.1/copyparty/web/up2k.js.gz +0 -0
  22. copyparty-1.18.1/copyparty/web/util.js.gz +0 -0
  23. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/PKG-INFO +32 -7
  24. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/SOURCES.txt +1 -0
  25. copyparty-1.17.2/copyparty/web/browser.css.gz +0 -0
  26. copyparty-1.17.2/copyparty/web/browser.js.gz +0 -0
  27. copyparty-1.17.2/copyparty/web/deps/marked.js.gz +0 -0
  28. copyparty-1.17.2/copyparty/web/splash.js.gz +0 -0
  29. copyparty-1.17.2/copyparty/web/up2k.js.gz +0 -0
  30. copyparty-1.17.2/copyparty/web/util.js.gz +0 -0
  31. {copyparty-1.17.2 → copyparty-1.18.1}/LICENSE +0 -0
  32. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/bos/__init__.py +0 -0
  33. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/bos/bos.py +0 -0
  34. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/bos/path.py +0 -0
  35. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/broker_mp.py +0 -0
  36. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/broker_mpw.py +0 -0
  37. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/broker_thr.py +0 -0
  38. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/broker_util.py +0 -0
  39. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/cert.py +0 -0
  40. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/dxml.py +0 -0
  41. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/fsutil.py +0 -0
  42. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/ftpd.py +0 -0
  43. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/ico.py +0 -0
  44. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/mdns.py +0 -0
  45. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/metrics.py +0 -0
  46. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/mtag.py +0 -0
  47. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/multicast.py +0 -0
  48. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/pwhash.py +0 -0
  49. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/res/COPYING.txt +0 -0
  50. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/res/__init__.py +0 -0
  51. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/res/insecure.pem +0 -0
  52. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/smbd.py +0 -0
  53. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/ssdp.py +0 -0
  54. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/star.py +0 -0
  55. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/__init__.py +0 -0
  56. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/__init__.py +0 -0
  57. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/bimap.py +0 -0
  58. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/bit.py +0 -0
  59. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/buffer.py +0 -0
  60. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/dns.py +0 -0
  61. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/label.py +0 -0
  62. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/lex.py +0 -0
  63. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/dnslib/ranges.py +0 -0
  64. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/ifaddr/__init__.py +0 -0
  65. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/ifaddr/_posix.py +0 -0
  66. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/ifaddr/_shared.py +0 -0
  67. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/ifaddr/_win32.py +0 -0
  68. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/qrcodegen.py +0 -0
  69. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/stolen/surrogateescape.py +0 -0
  70. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/sutil.py +0 -0
  71. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/szip.py +0 -0
  72. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/tcpsrv.py +0 -0
  73. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/tftpd.py +0 -0
  74. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/th_cli.py +0 -0
  75. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/u2idx.py +0 -0
  76. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/up2k.py +0 -0
  77. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/a/__init__.py +0 -0
  78. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/a/partyfuse.py +0 -0
  79. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/a/u2c.py +0 -0
  80. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/a/webdav-cfg.bat +0 -0
  81. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/baguettebox.js.gz +0 -0
  82. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/browser.html +0 -0
  83. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/browser2.html +0 -0
  84. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/cf.html +0 -0
  85. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dbg-audio.js.gz +0 -0
  86. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dd/2.png +0 -0
  87. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dd/3.png +0 -0
  88. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dd/4.png +0 -0
  89. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dd/5.png +0 -0
  90. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/dd/__init__.py +0 -0
  91. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/__init__.py +0 -0
  92. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/busy.mp3.gz +0 -0
  93. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/easymde.css.gz +0 -0
  94. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/easymde.js.gz +0 -0
  95. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/fuse.py +0 -0
  96. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/mini-fa.css.gz +0 -0
  97. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/mini-fa.woff +0 -0
  98. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/prism.css.gz +0 -0
  99. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/prism.js.gz +0 -0
  100. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/prismd.css.gz +0 -0
  101. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/scp.woff2 +0 -0
  102. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  103. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  104. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/md.css.gz +0 -0
  105. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/md.html +0 -0
  106. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/md.js.gz +0 -0
  107. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/md2.css.gz +0 -0
  108. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/md2.js.gz +0 -0
  109. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/mde.css.gz +0 -0
  110. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/mde.html +0 -0
  111. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/mde.js.gz +0 -0
  112. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/msg.css.gz +0 -0
  113. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/msg.html +0 -0
  114. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/rups.css.gz +0 -0
  115. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/rups.html +0 -0
  116. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/rups.js.gz +0 -0
  117. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/shares.css.gz +0 -0
  118. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/shares.html +0 -0
  119. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/shares.js.gz +0 -0
  120. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/splash.css.gz +0 -0
  121. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/svcs.html +0 -0
  122. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/svcs.js.gz +0 -0
  123. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty/web/w.hash.js.gz +0 -0
  124. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/dependency_links.txt +0 -0
  125. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/entry_points.txt +0 -0
  126. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/requires.txt +0 -0
  127. {copyparty-1.17.2 → copyparty-1.18.1}/copyparty.egg-info/top_level.txt +0 -0
  128. {copyparty-1.17.2 → copyparty-1.18.1}/pyproject.toml +0 -0
  129. {copyparty-1.17.2 → copyparty-1.18.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.17.2
3
+ Version: 1.18.1
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
@@ -66,7 +66,7 @@ turn almost any device into a file server with resumable uploads/downloads using
66
66
  * 🔌 protocols: [http](#the-browser) // [webdav](#webdav-server) // [ftp](#ftp-server) // [tftp](#tftp-server) // [smb/cifs](#smb-server)
67
67
  * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
68
68
 
69
- 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
69
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running on a nuc in my basement
70
70
 
71
71
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
72
72
 
@@ -114,6 +114,7 @@ made in Norway 🇳🇴
114
114
  * [creating a playlist](#creating-a-playlist) - with a standalone mediaplayer or copyparty
115
115
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
116
116
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
117
+ * [textfile viewer](#textfile-viewer) - with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/))
117
118
  * [markdown viewer](#markdown-viewer) - and there are *two* editors
118
119
  * [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
119
120
  * [other tricks](#other-tricks)
@@ -315,7 +316,8 @@ also see [comparison to similar software](./docs/versus.md)
315
316
  * ☑ play video files as audio (converted on server)
316
317
  * ☑ create and play [m3u8 playlists](#playlists)
317
318
  * ☑ image gallery with webm player
318
- * ☑ textfile browser with syntax hilighting
319
+ * ☑ [textfile browser](#textfile-viewer) with syntax hilighting
320
+ * ☑ realtime streaming of growing files (logfiles and such)
319
321
  * ☑ [thumbnails](#thumbnails)
320
322
  * ☑ ...of images using Pillow, pyvips, or FFmpeg
321
323
  * ☑ ...of videos using FFmpeg
@@ -619,6 +621,8 @@ a client can request to see dotfiles in directory listings if global option `-ed
619
621
 
620
622
  dotfiles do not appear in search results unless one of the above is true, **and** the global option / volflag `dotsrch` is set
621
623
 
624
+ > even if user has permission to see dotfiles, they are default-hidden unless `--see-dots` is set, and/or user has enabled the `dotfiles` option in the settings tab
625
+
622
626
  config file example, where the same permission to see dotfiles is given in two different ways just for reference:
623
627
 
624
628
  ```yaml
@@ -755,7 +759,10 @@ enabling `multiselect` lets you click files to select them, and then shift-click
755
759
  * `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
756
760
  * the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`
757
761
 
758
- to show `/icons/exe.png` as the thumbnail for all .exe files, `--ext-th=exe=/icons/exe.png` (optionally as a volflag)
762
+ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` and `.elf` files respectively, do this: `--ext-th=exe=/icons/exe.png --ext-th=elf=/icons/elf.gif`
763
+ * optionally as separate volflags for each mapping; see config file example below
764
+ * the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
765
+ * be careful with svg; chrome will crash if you have too many unique svg files showing on the same page (the limit is 250 or so) -- showing the same handful of svg files thousands of times is ok however
759
766
 
760
767
  config file example:
761
768
 
@@ -772,6 +779,7 @@ config file example:
772
779
  dthumb # disable ALL thumbnails and audio transcoding
773
780
  dvthumb # only disable video thumbnails
774
781
  ext-th: exe=/ico/exe.png # /ico/exe.png is the thumbnail of *.exe
782
+ ext-th: elf=/ico/elf.gif # ...and /ico/elf.gif is used for *.elf
775
783
  th-covers: folder.png,folder.jpg,cover.png,cover.jpg # the default
776
784
  ```
777
785
 
@@ -1179,6 +1187,18 @@ not available on iPhones / iPads because AudioContext currently breaks backgroun
1179
1187
  due to phone / app settings, android phones may randomly stop playing music when the power saver kicks in, especially at the end of an album -- you can fix it by [disabling power saving](https://user-images.githubusercontent.com/241032/235262123-c328cca9-3930-4948-bd18-3949b9fd3fcf.png) in the [app settings](https://user-images.githubusercontent.com/241032/235262121-2ffc51ae-7821-4310-a322-c3b7a507890c.png) of the browser you use for music streaming (preferably a dedicated one)
1180
1188
 
1181
1189
 
1190
+ ## textfile viewer
1191
+
1192
+ with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/)) , and terminal colors work too
1193
+
1194
+ click `-txt-` next to a textfile to open the viewer, which has the following toolbar buttons:
1195
+
1196
+ * `✏️ edit` opens the textfile editor
1197
+ * `📡 follow` starts monitoring the file for changes, streaming new lines in realtime
1198
+ * similar to `tail -f`
1199
+ * [link directly](https://a.ocv.me/pub/demo/logtail/?doc=lipsum.txt&tail) to a file with tailing enabled by adding `&tail` to the textviewer URL
1200
+
1201
+
1182
1202
  ## markdown viewer
1183
1203
 
1184
1204
  and there are *two* editors
@@ -1539,7 +1559,6 @@ the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`,
1539
1559
  note:
1540
1560
  * upload-times can be displayed in the file listing by enabling the `.up_at` metadata key, either globally with `-e2d -mte +.up_at` or per-volume with volflags `e2d,mte=+.up_at` (will have a ~17% performance impact on directory listings)
1541
1561
  * `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
1542
- * the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
1543
1562
 
1544
1563
  config file example (these options are recommended btw):
1545
1564
 
@@ -2477,6 +2496,9 @@ interact with copyparty using non-browser clients
2477
2496
  * and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
2478
2497
  * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
2479
2498
 
2499
+ * [Custom Uploader](https://f-droid.org/en/packages/com.nyx.custom_uploader/) (an Android app) as an alternative to copyparty's own [PartyUP!](#android-app)
2500
+ * works if you set UploadURL to `https://your.com/foo/?want=url&pw=hunter2` and FormDataName `f`
2501
+
2480
2502
  * contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
2481
2503
 
2482
2504
  * [igloo irc](https://iglooirc.com/): Method: `post` Host: `https://you.com/up/?want=url&pw=hunter2` Multipart: `yes` File parameter: `f`
@@ -2763,7 +2785,7 @@ enable [thumbnails](#thumbnails) of...
2763
2785
  * **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
2764
2786
  * **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
2765
2787
  * **HEIF pictures:** `pyvips` or `ffmpeg` or `pyheif-pillow-opener` (requires Linux or a C compiler)
2766
- * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin`
2788
+ * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
2767
2789
  * **JPEG XL pictures:** `pyvips` or `ffmpeg`
2768
2790
 
2769
2791
  enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
@@ -2790,10 +2812,11 @@ set any of the following environment variables to disable its associated optiona
2790
2812
  | `PRTY_NO_CFSSL` | never attempt to generate self-signed certificates using [cfssl](https://github.com/cloudflare/cfssl) |
2791
2813
  | `PRTY_NO_FFMPEG` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips |
2792
2814
  | `PRTY_NO_FFPROBE` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
2815
+ | `PRTY_NO_MAGIC` | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
2793
2816
  | `PRTY_NO_MUTAGEN` | do not use [mutagen](https://pypi.org/project/mutagen/) for reading metadata from media files; will fallback to ffprobe |
2794
2817
  | `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
2795
2818
  | `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
2796
- | `PRTY_NO_PIL_AVIF` | disable 3rd-party Pillow plugin for [AVIF support](https://pypi.org/project/pillow-avif-plugin/) |
2819
+ | `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
2797
2820
  | `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pyheif-pillow-opener/) |
2798
2821
  | `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
2799
2822
  | `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
@@ -2890,5 +2913,7 @@ if there's a wall of base64 in the log (thread stacks) then please include that,
2890
2913
 
2891
2914
  for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
2892
2915
 
2916
+ specifically you may want to [build the sfx](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#just-the-sfx) or [build from scratch](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#build-from-scratch)
2917
+
2893
2918
  see [./docs/TODO.md](./docs/TODO.md) for planned features / fixes / changes
2894
2919
 
@@ -8,7 +8,7 @@ turn almost any device into a file server with resumable uploads/downloads using
8
8
  * 🔌 protocols: [http](#the-browser) // [webdav](#webdav-server) // [ftp](#ftp-server) // [tftp](#tftp-server) // [smb/cifs](#smb-server)
9
9
  * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
10
10
 
11
- 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
11
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running on a nuc in my basement
12
12
 
13
13
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
14
14
 
@@ -56,6 +56,7 @@ made in Norway 🇳🇴
56
56
  * [creating a playlist](#creating-a-playlist) - with a standalone mediaplayer or copyparty
57
57
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
58
58
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
59
+ * [textfile viewer](#textfile-viewer) - with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/))
59
60
  * [markdown viewer](#markdown-viewer) - and there are *two* editors
60
61
  * [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
61
62
  * [other tricks](#other-tricks)
@@ -257,7 +258,8 @@ also see [comparison to similar software](./docs/versus.md)
257
258
  * ☑ play video files as audio (converted on server)
258
259
  * ☑ create and play [m3u8 playlists](#playlists)
259
260
  * ☑ image gallery with webm player
260
- * ☑ textfile browser with syntax hilighting
261
+ * ☑ [textfile browser](#textfile-viewer) with syntax hilighting
262
+ * ☑ realtime streaming of growing files (logfiles and such)
261
263
  * ☑ [thumbnails](#thumbnails)
262
264
  * ☑ ...of images using Pillow, pyvips, or FFmpeg
263
265
  * ☑ ...of videos using FFmpeg
@@ -561,6 +563,8 @@ a client can request to see dotfiles in directory listings if global option `-ed
561
563
 
562
564
  dotfiles do not appear in search results unless one of the above is true, **and** the global option / volflag `dotsrch` is set
563
565
 
566
+ > even if user has permission to see dotfiles, they are default-hidden unless `--see-dots` is set, and/or user has enabled the `dotfiles` option in the settings tab
567
+
564
568
  config file example, where the same permission to see dotfiles is given in two different ways just for reference:
565
569
 
566
570
  ```yaml
@@ -697,7 +701,10 @@ enabling `multiselect` lets you click files to select them, and then shift-click
697
701
  * `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
698
702
  * the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`
699
703
 
700
- to show `/icons/exe.png` as the thumbnail for all .exe files, `--ext-th=exe=/icons/exe.png` (optionally as a volflag)
704
+ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` and `.elf` files respectively, do this: `--ext-th=exe=/icons/exe.png --ext-th=elf=/icons/elf.gif`
705
+ * optionally as separate volflags for each mapping; see config file example below
706
+ * the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
707
+ * be careful with svg; chrome will crash if you have too many unique svg files showing on the same page (the limit is 250 or so) -- showing the same handful of svg files thousands of times is ok however
701
708
 
702
709
  config file example:
703
710
 
@@ -714,6 +721,7 @@ config file example:
714
721
  dthumb # disable ALL thumbnails and audio transcoding
715
722
  dvthumb # only disable video thumbnails
716
723
  ext-th: exe=/ico/exe.png # /ico/exe.png is the thumbnail of *.exe
724
+ ext-th: elf=/ico/elf.gif # ...and /ico/elf.gif is used for *.elf
717
725
  th-covers: folder.png,folder.jpg,cover.png,cover.jpg # the default
718
726
  ```
719
727
 
@@ -1121,6 +1129,18 @@ not available on iPhones / iPads because AudioContext currently breaks backgroun
1121
1129
  due to phone / app settings, android phones may randomly stop playing music when the power saver kicks in, especially at the end of an album -- you can fix it by [disabling power saving](https://user-images.githubusercontent.com/241032/235262123-c328cca9-3930-4948-bd18-3949b9fd3fcf.png) in the [app settings](https://user-images.githubusercontent.com/241032/235262121-2ffc51ae-7821-4310-a322-c3b7a507890c.png) of the browser you use for music streaming (preferably a dedicated one)
1122
1130
 
1123
1131
 
1132
+ ## textfile viewer
1133
+
1134
+ with realtime streaming of logfiles and such ([demo](https://a.ocv.me/pub/demo/logtail/)) , and terminal colors work too
1135
+
1136
+ click `-txt-` next to a textfile to open the viewer, which has the following toolbar buttons:
1137
+
1138
+ * `✏️ edit` opens the textfile editor
1139
+ * `📡 follow` starts monitoring the file for changes, streaming new lines in realtime
1140
+ * similar to `tail -f`
1141
+ * [link directly](https://a.ocv.me/pub/demo/logtail/?doc=lipsum.txt&tail) to a file with tailing enabled by adding `&tail` to the textviewer URL
1142
+
1143
+
1124
1144
  ## markdown viewer
1125
1145
 
1126
1146
  and there are *two* editors
@@ -1481,7 +1501,6 @@ the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`,
1481
1501
  note:
1482
1502
  * upload-times can be displayed in the file listing by enabling the `.up_at` metadata key, either globally with `-e2d -mte +.up_at` or per-volume with volflags `e2d,mte=+.up_at` (will have a ~17% performance impact on directory listings)
1483
1503
  * `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
1484
- * the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
1485
1504
 
1486
1505
  config file example (these options are recommended btw):
1487
1506
 
@@ -2419,6 +2438,9 @@ interact with copyparty using non-browser clients
2419
2438
  * and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
2420
2439
  * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
2421
2440
 
2441
+ * [Custom Uploader](https://f-droid.org/en/packages/com.nyx.custom_uploader/) (an Android app) as an alternative to copyparty's own [PartyUP!](#android-app)
2442
+ * works if you set UploadURL to `https://your.com/foo/?want=url&pw=hunter2` and FormDataName `f`
2443
+
2422
2444
  * contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
2423
2445
 
2424
2446
  * [igloo irc](https://iglooirc.com/): Method: `post` Host: `https://you.com/up/?want=url&pw=hunter2` Multipart: `yes` File parameter: `f`
@@ -2705,7 +2727,7 @@ enable [thumbnails](#thumbnails) of...
2705
2727
  * **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
2706
2728
  * **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
2707
2729
  * **HEIF pictures:** `pyvips` or `ffmpeg` or `pyheif-pillow-opener` (requires Linux or a C compiler)
2708
- * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin`
2730
+ * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
2709
2731
  * **JPEG XL pictures:** `pyvips` or `ffmpeg`
2710
2732
 
2711
2733
  enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
@@ -2732,10 +2754,11 @@ set any of the following environment variables to disable its associated optiona
2732
2754
  | `PRTY_NO_CFSSL` | never attempt to generate self-signed certificates using [cfssl](https://github.com/cloudflare/cfssl) |
2733
2755
  | `PRTY_NO_FFMPEG` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips |
2734
2756
  | `PRTY_NO_FFPROBE` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
2757
+ | `PRTY_NO_MAGIC` | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
2735
2758
  | `PRTY_NO_MUTAGEN` | do not use [mutagen](https://pypi.org/project/mutagen/) for reading metadata from media files; will fallback to ffprobe |
2736
2759
  | `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
2737
2760
  | `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
2738
- | `PRTY_NO_PIL_AVIF` | disable 3rd-party Pillow plugin for [AVIF support](https://pypi.org/project/pillow-avif-plugin/) |
2761
+ | `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
2739
2762
  | `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pyheif-pillow-opener/) |
2740
2763
  | `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
2741
2764
  | `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
@@ -2832,5 +2855,7 @@ if there's a wall of base64 in the log (thread stacks) then please include that,
2832
2855
 
2833
2856
  for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
2834
2857
 
2858
+ specifically you may want to [build the sfx](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#just-the-sfx) or [build from scratch](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#build-from-scratch)
2859
+
2835
2860
  see [./docs/TODO.md](./docs/TODO.md) for planned features / fixes / changes
2836
2861
 
@@ -77,6 +77,7 @@ web/deps/prismd.css
77
77
  web/deps/scp.woff2
78
78
  web/deps/sha512.ac.js
79
79
  web/deps/sha512.hw.js
80
+ web/idp.html
80
81
  web/iiam.gif
81
82
  web/md.css
82
83
  web/md.html
@@ -956,6 +956,7 @@ def add_general(ap, nc, srvname):
956
956
  ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
957
957
  ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
958
958
  ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
959
+ ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
959
960
  ap2.add_argument("--license", action="store_true", help="show licenses and exit")
960
961
  ap2.add_argument("--version", action="store_true", help="show versions and exit")
961
962
 
@@ -1084,12 +1085,16 @@ def add_cert(ap, cert_path):
1084
1085
 
1085
1086
 
1086
1087
  def add_auth(ap):
1088
+ idp_db = os.path.join(E.cfg, "idp.db")
1087
1089
  ses_db = os.path.join(E.cfg, "sessions.db")
1088
1090
  ap2 = ap.add_argument_group('IdP / identity provider / user authentication options')
1089
1091
  ap2.add_argument("--idp-h-usr", metavar="HN", type=u, default="", help="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")
1090
1092
  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")
1091
1093
  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")
1092
1094
  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")
1095
+ 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)")
1096
+ 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")
1097
+ 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)")
1093
1098
  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")
1094
1099
  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")
1095
1100
  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)")
@@ -1262,6 +1267,7 @@ def add_optouts(ap):
1262
1267
  ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
1263
1268
  ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
1264
1269
  ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1270
+ ap2.add_argument("--no-tail", action="store_true", help="disable streaming a growing files with ?tail (volflag=notail)")
1265
1271
  ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader-IP into the database; will also disable unpost, you may want \033[32m--forget-ip\033[0m instead (volflag=no_db_ip)")
1266
1272
 
1267
1273
 
@@ -1391,6 +1397,16 @@ def add_transcoding(ap):
1391
1397
  ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
1392
1398
 
1393
1399
 
1400
+ def add_tail(ap):
1401
+ ap2 = ap.add_argument_group('tailing options (realtime streaming of a growing file)')
1402
+ ap2.add_argument("--tail-who", metavar="LVL", type=int, default=2, help="who can tail? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=authenticated-with-read-access, [\033[32m3\033[0m]=everyone-with-read-access (volflag=tail_who)")
1403
+ ap2.add_argument("--tail-cmax", metavar="N", type=int, default=64, help="do not allow starting a new tail if more than \033[33mN\033[0m active downloads")
1404
+ ap2.add_argument("--tail-tmax", metavar="SEC", type=float, default=0, help="terminate connection after \033[33mSEC\033[0m seconds; [\033[32m0\033[0m]=never (volflag=tail_tmax)")
1405
+ ap2.add_argument("--tail-rate", metavar="SEC", type=float, default=0.2, help="check for new data every \033[33mSEC\033[0m seconds (volflag=tail_rate)")
1406
+ ap2.add_argument("--tail-ka", metavar="SEC", type=float, default=3.0, help="send a zerobyte if connection is idle for \033[33mSEC\033[0m seconds to prevent disconnect")
1407
+ ap2.add_argument("--tail-fd", metavar="SEC", type=float, default=1.0, help="check if file was replaced (new fd) if idle for \033[33mSEC\033[0m seconds (volflag=tail_fd)")
1408
+
1409
+
1394
1410
  def add_rss(ap):
1395
1411
  ap2 = ap.add_argument_group('RSS options')
1396
1412
  ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental) (volflag=rss)")
@@ -1486,6 +1502,7 @@ def add_ui(ap, retry):
1486
1502
  ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)")
1487
1503
  ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
1488
1504
  ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
1505
+ ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
1489
1506
  ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1490
1507
  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")
1491
1508
  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)")
@@ -1595,6 +1612,7 @@ def run_argparse(
1595
1612
  add_hooks(ap)
1596
1613
  add_stats(ap)
1597
1614
  add_txt(ap)
1615
+ add_tail(ap)
1598
1616
  add_og(ap)
1599
1617
  add_ui(ap, retry)
1600
1618
  add_admin(ap)
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 17, 2)
4
- CODENAME = "mixtape.m3u"
5
- BUILD_DT = (2025, 5, 27)
3
+ VERSION = (1, 18, 1)
4
+ CODENAME = "logtail"
5
+ BUILD_DT = (2025, 7, 7)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -21,6 +21,7 @@ from .util import (
21
21
  DEF_MTE,
22
22
  DEF_MTH,
23
23
  EXTS,
24
+ HAVE_SQLITE3,
24
25
  IMPLICATIONS,
25
26
  MIMES,
26
27
  SQLITE_VER,
@@ -32,6 +33,7 @@ from .util import (
32
33
  afsenc,
33
34
  get_df,
34
35
  humansize,
36
+ min_ex,
35
37
  odfusion,
36
38
  read_utf8,
37
39
  relchk,
@@ -44,6 +46,9 @@ from .util import (
44
46
  vsplit,
45
47
  )
46
48
 
49
+ if HAVE_SQLITE3:
50
+ import sqlite3
51
+
47
52
  if TYPE_CHECKING:
48
53
  from .broker_mp import BrokerMp
49
54
  from .broker_thr import BrokerThr
@@ -65,7 +70,9 @@ SSEELOG = " ({})".format(SEE_LOG)
65
70
  BAD_CFG = "invalid config; {}".format(SEE_LOG)
66
71
  SBADCFG = " ({})".format(BAD_CFG)
67
72
 
68
- PTN_U_GRP = re.compile(r"\$\{u%([+-])([^}]+)\}")
73
+ PTN_U_GRP = re.compile(r"\$\{u(%[+-][^}]+)\}")
74
+ PTN_G_GRP = re.compile(r"\$\{g(%[+-][^}]+)\}")
75
+ PTN_SIGIL = re.compile(r"(\${[ug][}%])")
69
76
 
70
77
 
71
78
  class CfgEx(Exception):
@@ -926,6 +933,10 @@ class AuthSrv(object):
926
933
  return False
927
934
 
928
935
  self.idp_accs[uname] = gnames
936
+ try:
937
+ self._update_idp_db(uname, gname)
938
+ except:
939
+ self.log("failed to update the --idp-db:\n%s" % (min_ex(),), 3)
929
940
 
930
941
  t = "reinitializing due to new user from IdP: [%r:%r]"
931
942
  self.log(t % (uname, gnames), 3)
@@ -938,6 +949,21 @@ class AuthSrv(object):
938
949
  broker.ask("reload", False, True).get()
939
950
  return True
940
951
 
952
+ def _update_idp_db(self, uname , gname ) :
953
+ if not self.args.idp_store:
954
+ return
955
+
956
+
957
+ db = sqlite3.connect(self.args.idp_db)
958
+ cur = db.cursor()
959
+
960
+ cur.execute("delete from us where un = ?", (uname,))
961
+ cur.execute("insert into us values (?,?)", (uname, gname))
962
+
963
+ db.commit()
964
+ cur.close()
965
+ db.close()
966
+
941
967
  def _map_volume_idp(
942
968
  self,
943
969
  src ,
@@ -958,15 +984,27 @@ class AuthSrv(object):
958
984
  un_gn = [("", "")]
959
985
 
960
986
  for un, gn in un_gn:
961
- m = PTN_U_GRP.search(dst0)
962
- if m:
963
- req, gnc = m.groups()
964
- hit = gnc in (un_gns.get(un) or [])
965
- if req == "+":
966
- if not hit:
967
- continue
968
- elif hit:
987
+ rejected = False
988
+ for ptn in [PTN_U_GRP, PTN_G_GRP]:
989
+ m = ptn.search(dst0)
990
+ if not m:
969
991
  continue
992
+ zs = m.group(1)
993
+ zs = zs.replace(",%+", "\n%+")
994
+ zs = zs.replace(",%-", "\n%-")
995
+ for rule in zs.split("\n"):
996
+ gnc = rule[2:]
997
+ if ptn == PTN_U_GRP:
998
+ # is user member of group?
999
+ hit = gnc in (un_gns.get(un) or [])
1000
+ else:
1001
+ # is it this specific group?
1002
+ hit = gn == gnc
1003
+
1004
+ if rule.startswith("%+") != hit:
1005
+ rejected = True
1006
+ if rejected:
1007
+ continue
970
1008
 
971
1009
  # if ap/vp has a user/group placeholder, make sure to keep
972
1010
  # track so the same user/group is mapped when setting perms;
@@ -981,6 +1019,8 @@ class AuthSrv(object):
981
1019
 
982
1020
  src = src1.replace("${g}", gn or "\n")
983
1021
  dst = dst1.replace("${g}", gn or "\n")
1022
+ src = PTN_G_GRP.sub(gn or "\n", src)
1023
+ dst = PTN_G_GRP.sub(gn or "\n", dst)
984
1024
  if src == src1 and dst == dst1:
985
1025
  gn = ""
986
1026
 
@@ -1072,6 +1112,7 @@ class AuthSrv(object):
1072
1112
  * any non-zero value from IdP group header
1073
1113
  * otherwise take --grps / [groups]
1074
1114
  """
1115
+ self.load_idp_db(bool(self.idp_accs))
1075
1116
  ret = {un: gns[:] for un, gns in self.idp_accs.items()}
1076
1117
  ret.update({zs: [""] for zs in acct if zs not in ret})
1077
1118
  for gn, uns in grps.items():
@@ -1632,7 +1673,6 @@ class AuthSrv(object):
1632
1673
  shr = enshare[1:-1]
1633
1674
  shrs = enshare[1:]
1634
1675
  if enshare:
1635
- import sqlite3
1636
1676
 
1637
1677
  zsd = {"d2d": True, "tcolor": self.args.tcolor}
1638
1678
  shv = VFS(self.log_func, "", shr, shr, AXS(), zsd)
@@ -1855,7 +1895,7 @@ class AuthSrv(object):
1855
1895
  is_shr = shr and zv.vpath.split("/")[0] == shr
1856
1896
  if histp and not is_shr and histp in rhisttab:
1857
1897
  zv2 = rhisttab[histp]
1858
- t = "invalid config; multiple volumes share the same histpath (database+thumbnails location):\n histpath: %s\n volume 1: /%s [%s]\n volume 2: %s [%s]"
1898
+ t = "invalid config; multiple volumes share the same histpath (database+thumbnails location):\n histpath: %s\n volume 1: /%s [%s]\n volume 2: /%s [%s]"
1859
1899
  t = t % (histp, zv2.vpath, zv2.realpath, zv.vpath, zv.realpath)
1860
1900
  self.log(t, 1)
1861
1901
  raise Exception(t)
@@ -1869,7 +1909,7 @@ class AuthSrv(object):
1869
1909
  is_shr = shr and zv.vpath.split("/")[0] == shr
1870
1910
  if dbp and not is_shr and dbp in rdbpaths:
1871
1911
  zv2 = rdbpaths[dbp]
1872
- t = "invalid config; multiple volumes share the same dbpath (database location):\n dbpath: %s\n volume 1: /%s [%s]\n volume 2: %s [%s]"
1912
+ t = "invalid config; multiple volumes share the same dbpath (database location):\n dbpath: %s\n volume 1: /%s [%s]\n volume 2: /%s [%s]"
1873
1913
  t = t % (dbp, zv2.vpath, zv2.realpath, zv.vpath, zv.realpath)
1874
1914
  self.log(t, 1)
1875
1915
  raise Exception(t)
@@ -2052,12 +2092,13 @@ class AuthSrv(object):
2052
2092
  if vf not in vol.flags:
2053
2093
  vol.flags[vf] = getattr(self.args, ga)
2054
2094
 
2055
- zs = "forget_ip nrand u2abort u2ow ups_who zip_who"
2095
+ zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who"
2056
2096
  for k in zs.split():
2057
2097
  if k in vol.flags:
2058
2098
  vol.flags[k] = int(vol.flags[k])
2059
2099
 
2060
- for k in ("convt",):
2100
+ zs = "convt tail_fd tail_rate tail_tmax"
2101
+ for k in zs.split():
2061
2102
  if k in vol.flags:
2062
2103
  vol.flags[k] = float(vol.flags[k])
2063
2104
 
@@ -2386,7 +2427,7 @@ class AuthSrv(object):
2386
2427
  idp_vn, _ = vfs.get(idp_vp, "*", False, False)
2387
2428
  idp_vp0 = idp_vn.vpath0
2388
2429
 
2389
- sigils = set(re.findall(r"(\${[ug][}%])", idp_vp0))
2430
+ sigils = set(PTN_SIGIL.findall(idp_vp0))
2390
2431
  if len(sigils) > 1:
2391
2432
  t = '\nWARNING: IdP-volume "/%s" created by "/%s" has multiple IdP placeholders: %s'
2392
2433
  self.idp_warn.append(t % (idp_vp, idp_vp0, list(sigils)))
@@ -2556,6 +2597,7 @@ class AuthSrv(object):
2556
2597
  "txt_ext": self.args.textfiles.replace(",", " "),
2557
2598
  "def_hcols": list(vf.get("mth") or []),
2558
2599
  "unlist0": vf.get("unlist") or "",
2600
+ "see_dots": self.args.see_dots,
2559
2601
  "dgrid": "grid" in vf,
2560
2602
  "dgsel": "gsel" in vf,
2561
2603
  "dnsort": "nsort" in vf,
@@ -2595,6 +2637,42 @@ class AuthSrv(object):
2595
2637
  zs = str(vol.flags.get("tcolor") or self.args.tcolor)
2596
2638
  vol.flags["tcolor"] = zs.lstrip("#")
2597
2639
 
2640
+ def load_idp_db(self, quiet=False) :
2641
+ # mutex me
2642
+ level = self.args.idp_store
2643
+ if level < 2 or not self.args.idp_h_usr:
2644
+ return
2645
+
2646
+
2647
+ db = sqlite3.connect(self.args.idp_db)
2648
+ cur = db.cursor()
2649
+ from_cache = cur.execute("select un, gs from us").fetchall()
2650
+ cur.close()
2651
+ db.close()
2652
+
2653
+ self.idp_accs.clear()
2654
+ self.idp_usr_gh.clear()
2655
+
2656
+ gsep = self.args.idp_gsep
2657
+ n = []
2658
+ for uname, gname in from_cache:
2659
+ if level < 3:
2660
+ if uname in self.idp_accs:
2661
+ continue
2662
+ gname = ""
2663
+ gnames = [x.strip() for x in gsep.split(gname)]
2664
+ gnames.sort()
2665
+
2666
+ # self.idp_usr_gh[uname] = gname
2667
+ self.idp_accs[uname] = gnames
2668
+ n.append(uname)
2669
+
2670
+ if n and not quiet:
2671
+ t = ", ".join(n[:9])
2672
+ if len(n) > 9:
2673
+ t += "..."
2674
+ self.log("found %d IdP users in db (%s)" % (len(n), t))
2675
+
2598
2676
  def load_sessions(self, quiet=False) :
2599
2677
  # mutex me
2600
2678
  if self.args.no_ses:
@@ -2602,7 +2680,6 @@ class AuthSrv(object):
2602
2680
  self.sesa = {}
2603
2681
  return
2604
2682
 
2605
- import sqlite3
2606
2683
 
2607
2684
  ases = {}
2608
2685
  blen = (self.args.ses_len // 4) * 4 # 3 bytes in 4 chars
@@ -2649,7 +2726,6 @@ class AuthSrv(object):
2649
2726
  if self.args.no_ses:
2650
2727
  return
2651
2728
 
2652
- import sqlite3
2653
2729
 
2654
2730
  db = sqlite3.connect(self.args.ses_db)
2655
2731
  cur = db.cursor()
@@ -22,6 +22,7 @@ def vf_bmap() :
22
22
  "no_forget": "noforget",
23
23
  "no_pipe": "nopipe",
24
24
  "no_robots": "norobots",
25
+ "no_tail": "notail",
25
26
  "no_thumb": "dthumb",
26
27
  "no_vthumb": "dvthumb",
27
28
  "no_athumb": "dathumb",
@@ -51,6 +52,7 @@ def vf_bmap() :
51
52
  "og_no_head",
52
53
  "og_s_title",
53
54
  "rand",
55
+ "rmagic",
54
56
  "rss",
55
57
  "wo_up_readme",
56
58
  "xdev",
@@ -101,6 +103,10 @@ def vf_vmap() :
101
103
  "mv_retry",
102
104
  "rm_retry",
103
105
  "sort",
106
+ "tail_fd",
107
+ "tail_rate",
108
+ "tail_tmax",
109
+ "tail_who",
104
110
  "tcolor",
105
111
  "unlist",
106
112
  "u2abort",
@@ -304,6 +310,13 @@ flagcats = {
304
310
  "exp_md": "placeholders to expand in markdown files; see --help",
305
311
  "exp_lg": "placeholders to expand in prologue/epilogue; see --help",
306
312
  },
313
+ "tailing": {
314
+ "notail": "disable ?tail (download a growing file continuously)",
315
+ "tail_fd=1": "check if file was replaced (new fd) every 1 sec",
316
+ "tail_rate=0.2": "check for new data every 0.2 sec",
317
+ "tail_tmax=30": "kill connection after 30 sec",
318
+ "tail_who=2": "restrict ?tail access (1=admins,2=authed,3=everyone)",
319
+ },
307
320
  "others": {
308
321
  "dots": "allow all users with read-access to\nenable the option to show dotfiles in listings",
309
322
  "fk=8": 'generates per-file accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes',
@@ -312,6 +325,7 @@ flagcats = {
312
325
  "dks": "per-directory accesskeys allow browsing into subdirs",
313
326
  "dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so',
314
327
  "rss": "allow '?rss' URL suffix (experimental)",
328
+ "rmagic": "expensive analysis for mimetype accuracy",
315
329
  "ups_who=2": "restrict viewing the list of recent uploads",
316
330
  "zip_who=2": "restrict access to download-as-zip/tar",
317
331
  "zipmaxn=9k": "reject download-as-zip if more than 9000 files",