copyparty 1.19.15__tar.gz → 1.19.17__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 (145) hide show
  1. {copyparty-1.19.15 → copyparty-1.19.17}/PKG-INFO +47 -2
  2. {copyparty-1.19.15 → copyparty-1.19.17}/README.md +46 -1
  3. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/__init__.py +19 -0
  4. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/__main__.py +47 -11
  5. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/__version__.py +2 -2
  6. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/authsrv.py +45 -9
  7. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/bos/bos.py +5 -1
  8. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/cfg.py +20 -0
  9. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/ftpd.py +5 -3
  10. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/httpcli.py +114 -27
  11. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/mdns.py +53 -18
  12. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/mtag.py +18 -4
  13. copyparty-1.19.17/copyparty/qrkode.py +107 -0
  14. copyparty-1.19.17/copyparty/res/COPYING.txt +223 -0
  15. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/smbd.py +1 -1
  16. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/qrcodegen.py +1 -64
  17. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/svchub.py +13 -2
  18. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/tcpsrv.py +6 -7
  19. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/tftpd.py +1 -1
  20. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/th_srv.py +11 -5
  21. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/up2k.py +40 -16
  22. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/util.py +63 -16
  23. copyparty-1.19.17/copyparty/web/baguettebox.js.gz +0 -0
  24. copyparty-1.19.17/copyparty/web/browser.css.gz +0 -0
  25. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/browser.html +3 -1
  26. copyparty-1.19.17/copyparty/web/browser.js.gz +0 -0
  27. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/splash.html +3 -0
  28. copyparty-1.19.17/copyparty/web/splash.js.gz +0 -0
  29. copyparty-1.19.17/copyparty/web/tl/chi.js.gz +0 -0
  30. copyparty-1.19.17/copyparty/web/tl/cze.js.gz +0 -0
  31. copyparty-1.19.17/copyparty/web/tl/deu.js.gz +0 -0
  32. copyparty-1.19.17/copyparty/web/tl/epo.js.gz +0 -0
  33. copyparty-1.19.17/copyparty/web/tl/fin.js.gz +0 -0
  34. copyparty-1.19.17/copyparty/web/tl/fra.js.gz +0 -0
  35. copyparty-1.19.17/copyparty/web/tl/grc.js.gz +0 -0
  36. copyparty-1.19.17/copyparty/web/tl/ita.js.gz +0 -0
  37. copyparty-1.19.17/copyparty/web/tl/kor.js.gz +0 -0
  38. copyparty-1.19.17/copyparty/web/tl/nld.js.gz +0 -0
  39. copyparty-1.19.17/copyparty/web/tl/nno.js.gz +0 -0
  40. copyparty-1.19.17/copyparty/web/tl/nor.js.gz +0 -0
  41. copyparty-1.19.17/copyparty/web/tl/pol.js.gz +0 -0
  42. copyparty-1.19.17/copyparty/web/tl/por.js.gz +0 -0
  43. copyparty-1.19.17/copyparty/web/tl/rus.js.gz +0 -0
  44. copyparty-1.19.17/copyparty/web/tl/spa.js.gz +0 -0
  45. copyparty-1.19.17/copyparty/web/tl/swe.js.gz +0 -0
  46. copyparty-1.19.17/copyparty/web/tl/tur.js.gz +0 -0
  47. copyparty-1.19.17/copyparty/web/tl/ukr.js.gz +0 -0
  48. copyparty-1.19.17/copyparty/web/util.js.gz +0 -0
  49. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/PKG-INFO +47 -2
  50. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/SOURCES.txt +21 -1
  51. {copyparty-1.19.15 → copyparty-1.19.17}/pyproject.toml +2 -0
  52. copyparty-1.19.15/copyparty/res/COPYING.txt +0 -152
  53. copyparty-1.19.15/copyparty/web/baguettebox.js.gz +0 -0
  54. copyparty-1.19.15/copyparty/web/browser.css.gz +0 -0
  55. copyparty-1.19.15/copyparty/web/browser.js.gz +0 -0
  56. copyparty-1.19.15/copyparty/web/splash.js.gz +0 -0
  57. copyparty-1.19.15/copyparty/web/util.js.gz +0 -0
  58. {copyparty-1.19.15 → copyparty-1.19.17}/LICENSE +0 -0
  59. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/bos/__init__.py +0 -0
  60. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/bos/path.py +0 -0
  61. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/broker_mp.py +0 -0
  62. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/broker_mpw.py +0 -0
  63. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/broker_thr.py +0 -0
  64. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/broker_util.py +0 -0
  65. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/cert.py +0 -0
  66. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/dxml.py +0 -0
  67. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/fsutil.py +0 -0
  68. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/httpconn.py +0 -0
  69. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/httpsrv.py +0 -0
  70. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/ico.py +0 -0
  71. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/metrics.py +0 -0
  72. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/multicast.py +0 -0
  73. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/pwhash.py +0 -0
  74. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/res/__init__.py +0 -0
  75. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/res/insecure.pem +0 -0
  76. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/ssdp.py +0 -0
  77. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/star.py +0 -0
  78. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/__init__.py +0 -0
  79. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/__init__.py +0 -0
  80. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/bimap.py +0 -0
  81. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/bit.py +0 -0
  82. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/buffer.py +0 -0
  83. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/dns.py +0 -0
  84. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/label.py +0 -0
  85. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/lex.py +0 -0
  86. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/dnslib/ranges.py +0 -0
  87. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/ifaddr/__init__.py +0 -0
  88. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/ifaddr/_posix.py +0 -0
  89. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/ifaddr/_shared.py +0 -0
  90. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/ifaddr/_win32.py +0 -0
  91. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/stolen/surrogateescape.py +0 -0
  92. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/sutil.py +0 -0
  93. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/szip.py +0 -0
  94. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/th_cli.py +0 -0
  95. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/u2idx.py +0 -0
  96. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/a/__init__.py +0 -0
  97. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/a/partyfuse.py +0 -0
  98. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/a/u2c.py +0 -0
  99. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/a/webdav-cfg.bat +0 -0
  100. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/browser2.html +0 -0
  101. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/cf.html +0 -0
  102. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/dbg-audio.js.gz +0 -0
  103. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/__init__.py +0 -0
  104. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/busy.mp3.gz +0 -0
  105. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/easymde.css.gz +0 -0
  106. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/easymde.js.gz +0 -0
  107. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/fuse.py +0 -0
  108. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/marked.js.gz +0 -0
  109. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/mini-fa.css.gz +0 -0
  110. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/mini-fa.woff +0 -0
  111. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/prism.css.gz +0 -0
  112. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/prism.js.gz +0 -0
  113. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/prismd.css.gz +0 -0
  114. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/scp.woff2 +0 -0
  115. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  116. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  117. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/idp.html +0 -0
  118. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/md.css.gz +0 -0
  119. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/md.html +0 -0
  120. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/md.js.gz +0 -0
  121. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/md2.css.gz +0 -0
  122. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/md2.js.gz +0 -0
  123. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/mde.css.gz +0 -0
  124. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/mde.html +0 -0
  125. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/mde.js.gz +0 -0
  126. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/msg.css.gz +0 -0
  127. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/msg.html +0 -0
  128. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/opds.xml +0 -0
  129. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/rups.css.gz +0 -0
  130. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/rups.html +0 -0
  131. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/rups.js.gz +0 -0
  132. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/shares.css.gz +0 -0
  133. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/shares.html +0 -0
  134. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/shares.js.gz +0 -0
  135. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/splash.css.gz +0 -0
  136. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/svcs.html +0 -0
  137. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/svcs.js.gz +0 -0
  138. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/ui.css.gz +0 -0
  139. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/up2k.js.gz +0 -0
  140. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty/web/w.hash.js.gz +0 -0
  141. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/dependency_links.txt +0 -0
  142. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/entry_points.txt +0 -0
  143. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/requires.txt +0 -0
  144. {copyparty-1.19.15 → copyparty-1.19.17}/copyparty.egg-info/top_level.txt +0 -0
  145. {copyparty-1.19.15 → copyparty-1.19.17}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.19.15
3
+ Version: 1.19.17
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
@@ -203,6 +203,7 @@ made in Norway 🇳🇴
203
203
  * [dependencies](#dependencies) - mandatory deps
204
204
  * [optional dependencies](#optional-dependencies) - install these to enable bonus features
205
205
  * [dependency chickenbits](#dependency-chickenbits) - prevent loading an optional dependency
206
+ * [dependency unvendoring](#dependency-unvendoring) - force use of system modules
206
207
  * [optional gpl stuff](#optional-gpl-stuff)
207
208
  * [sfx](#sfx) - the self-contained "binary" (recommended!)
208
209
  * [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+)
@@ -278,6 +279,7 @@ you may also want these, especially on servers:
278
279
 
279
280
  * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
280
281
  * [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
282
+ * [contrib/podman-systemd/](contrib/podman-systemd/) to run copyparty in a Podman container as a systemd service (see guide inside)
281
283
  * [contrib/openrc/copyparty](contrib/openrc/copyparty) to run copyparty on Alpine / Gentoo
282
284
  * [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
283
285
  * [nixos module](#nixos-module) to run copyparty on NixOS hosts
@@ -586,6 +588,9 @@ examples:
586
588
 
587
589
  if you want to grant access to all users who are logged in, the group `acct` will always contain all known users, so for example `-v /mnt/music:music:r,@acct`
588
590
 
591
+ * to do the opposite, granting access to everyone who is NOT logged in. `*,-@acct` does the trick, for example `-v /srv/welcome:welcome:r,*,-@acct`
592
+ * single users can also be subtracted from a group: `@admins,-james`
593
+
589
594
  anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
590
595
 
591
596
  and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
@@ -794,6 +799,10 @@ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` an
794
799
  * the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
795
800
  * 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
796
801
 
802
+ note:
803
+ * heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) (available in the `iv` docker image, `withFastThumbnails` in nixos)
804
+ * technical trivia: FFmpeg has basic support for tiled heic as of v7.0; need `-show_stream_groups` for correct resolution
805
+
797
806
  config file example:
798
807
 
799
808
  ```yaml
@@ -1062,6 +1071,8 @@ available functions:
1062
1071
  * `$lpad(text, length, pad_char)`
1063
1072
  * `$rpad(text, length, pad_char)`
1064
1073
 
1074
+ two counters are available; `.n.s` is the nth file in the selection, and `.n.d` the nth file in the folder, for example rename-output `file(.n.d).(ext)` gives `file5.bin`, and `beach-$lpad((.n.s),3,0).(ext)` is `beach-017.jpg` and the initial value of each counter can be set in the textboxes underneath the preset dropdown
1075
+
1065
1076
  so,
1066
1077
 
1067
1078
  say you have a file named [`meganeko - Eclipse - 07 Sirius A.mp3`](https://www.youtube.com/watch?v=-dtb0vDPruI) (absolutely fantastic album btw) and the tags are: `Album:Eclipse`, `Artist:meganeko`, `Title:Sirius A`, `tn:7`
@@ -1097,6 +1108,8 @@ url parameters:
1097
1108
 
1098
1109
  * `pw=hunter2` for password auth
1099
1110
  * if you enabled `--usernames` then do `pw=username:password` instead
1111
+ * `nopw` disables embedding the password (if provided) into item-URLs in the feed
1112
+ * `nopw=a` disables mentioning the password anywhere at all in the feed; may break some readers
1100
1113
  * `recursive` to also include subfolders
1101
1114
  * `title=foo` changes the feed title (default: folder name)
1102
1115
  * `fext=mp3,opus` only include mp3 and opus files (default: all)
@@ -1163,6 +1176,7 @@ some highlights:
1163
1176
  * shows the audio waveform in the seekbar
1164
1177
  * not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
1165
1178
  * videos can be played as audio, without wasting bandwidth on the video
1179
+ * adding `?v` to the end of an audio/video/image link will make it open in the mediaplayer
1166
1180
 
1167
1181
  click the `play` link next to an audio file, or copy the link target to [share it](https://a.ocv.me/pub/demo/music/Ubiktune%20-%20SOUNDSHOCK%202%20-%20FM%20FUNK%20TERRROR!!/#af-1fbfba61&t=18) (optionally with a timestamp to start playing from, like that example does)
1168
1182
 
@@ -1547,7 +1561,12 @@ tweaking the ui
1547
1561
  * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Circle,tags/.tn,tags/Artist,tags/Title,href`
1548
1562
  * to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
1549
1563
 
1550
- see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag, or to add your own translation
1564
+ see [./docs/rice](./docs/rice) for more, including:
1565
+ * how to [hide ui-elements](./docs/rice/README.md#hide-ui-elements)
1566
+ * [custom fonts](./docs/rice/README.md#custom-fonts)
1567
+ * [custom loading-spinner](./docs/rice/README.md#boring-loader-spinner)
1568
+ * adding stuff (css/`<meta>`/...) [to the html `<head>` tag](./docs/rice/README.md#head)
1569
+ * [adding your own translation](./docs/rice/README.md#translations)
1551
1570
 
1552
1571
 
1553
1572
  ## opengraph
@@ -2530,6 +2549,10 @@ copyparty on NixOS is configured via `services.copyparty` options, for example:
2530
2549
  ```nix
2531
2550
  services.copyparty = {
2532
2551
  enable = true;
2552
+ # the user to run the service as
2553
+ user = "copyparty";
2554
+ # the group to run the service as
2555
+ group = "copyparty";
2533
2556
  # directly maps to values in the [global] section of the copyparty config.
2534
2557
  # see `copyparty --help` for available options
2535
2558
  settings = {
@@ -2554,6 +2577,12 @@ services.copyparty = {
2554
2577
  k.passwordFile = "/run/keys/copyparty/k_password";
2555
2578
  };
2556
2579
 
2580
+ # create a group
2581
+ groups = {
2582
+ # users "ed" and "k" are part of the group g1
2583
+ g1 = [ "ed" "k" ];
2584
+ };
2585
+
2557
2586
  # create a volume
2558
2587
  volumes = {
2559
2588
  # create a volume at "/" (the webroot), which will
@@ -3043,6 +3072,20 @@ example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
3043
3072
  * python2.7 on windows: `PRTY_NO_FFMPEG` + `PRTY_NO_FFPROBE` saves startup time
3044
3073
 
3045
3074
 
3075
+ ### dependency unvendoring
3076
+
3077
+ force use of system modules instead of the vendored versions:
3078
+
3079
+ | env-var | what it does |
3080
+ | -------------------- | ------------ |
3081
+ | `PRTY_SYS_ALL` | all of the below |
3082
+ | `PRTY_SYS_DNSLIB` | replace [stolen/dnslib](./copyparty/stolen/dnslib) with [upstream](https://pypi.org/project/dnslib/) |
3083
+ | `PRTY_SYS_IFADDR` | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) |
3084
+ | `PRTY_SYS_QRCG` | replace [stolen/qrcodegen.py](./copyparty/stolen/qrcodegen.py) with [upstream](https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py) |
3085
+
3086
+ to debug, run copyparty with `PRTY_MODSPEC=1` to see where it's getting each module from
3087
+
3088
+
3046
3089
  ## optional gpl stuff
3047
3090
 
3048
3091
  some bundled tools have copyleft dependencies, see [./bin/#mtag](bin/#mtag)
@@ -3105,6 +3148,8 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132
3105
3148
 
3106
3149
  * 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})`
3107
3150
 
3151
+ if you are suddenly unable to access storage (permission issues), try forcequitting termux, revoke all of its permissions in android settings, and run the command `termux-setup-storage`
3152
+
3108
3153
 
3109
3154
  # install on iOS
3110
3155
 
@@ -138,6 +138,7 @@ made in Norway 🇳🇴
138
138
  * [dependencies](#dependencies) - mandatory deps
139
139
  * [optional dependencies](#optional-dependencies) - install these to enable bonus features
140
140
  * [dependency chickenbits](#dependency-chickenbits) - prevent loading an optional dependency
141
+ * [dependency unvendoring](#dependency-unvendoring) - force use of system modules
141
142
  * [optional gpl stuff](#optional-gpl-stuff)
142
143
  * [sfx](#sfx) - the self-contained "binary" (recommended!)
143
144
  * [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+)
@@ -213,6 +214,7 @@ you may also want these, especially on servers:
213
214
 
214
215
  * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
215
216
  * [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
217
+ * [contrib/podman-systemd/](contrib/podman-systemd/) to run copyparty in a Podman container as a systemd service (see guide inside)
216
218
  * [contrib/openrc/copyparty](contrib/openrc/copyparty) to run copyparty on Alpine / Gentoo
217
219
  * [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
218
220
  * [nixos module](#nixos-module) to run copyparty on NixOS hosts
@@ -521,6 +523,9 @@ examples:
521
523
 
522
524
  if you want to grant access to all users who are logged in, the group `acct` will always contain all known users, so for example `-v /mnt/music:music:r,@acct`
523
525
 
526
+ * to do the opposite, granting access to everyone who is NOT logged in. `*,-@acct` does the trick, for example `-v /srv/welcome:welcome:r,*,-@acct`
527
+ * single users can also be subtracted from a group: `@admins,-james`
528
+
524
529
  anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
525
530
 
526
531
  and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
@@ -729,6 +734,10 @@ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` an
729
734
  * the supported image formats are [jpg, png, gif, webp, ico](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Image_types)
730
735
  * 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
731
736
 
737
+ note:
738
+ * heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) (available in the `iv` docker image, `withFastThumbnails` in nixos)
739
+ * technical trivia: FFmpeg has basic support for tiled heic as of v7.0; need `-show_stream_groups` for correct resolution
740
+
732
741
  config file example:
733
742
 
734
743
  ```yaml
@@ -997,6 +1006,8 @@ available functions:
997
1006
  * `$lpad(text, length, pad_char)`
998
1007
  * `$rpad(text, length, pad_char)`
999
1008
 
1009
+ two counters are available; `.n.s` is the nth file in the selection, and `.n.d` the nth file in the folder, for example rename-output `file(.n.d).(ext)` gives `file5.bin`, and `beach-$lpad((.n.s),3,0).(ext)` is `beach-017.jpg` and the initial value of each counter can be set in the textboxes underneath the preset dropdown
1010
+
1000
1011
  so,
1001
1012
 
1002
1013
  say you have a file named [`meganeko - Eclipse - 07 Sirius A.mp3`](https://www.youtube.com/watch?v=-dtb0vDPruI) (absolutely fantastic album btw) and the tags are: `Album:Eclipse`, `Artist:meganeko`, `Title:Sirius A`, `tn:7`
@@ -1032,6 +1043,8 @@ url parameters:
1032
1043
 
1033
1044
  * `pw=hunter2` for password auth
1034
1045
  * if you enabled `--usernames` then do `pw=username:password` instead
1046
+ * `nopw` disables embedding the password (if provided) into item-URLs in the feed
1047
+ * `nopw=a` disables mentioning the password anywhere at all in the feed; may break some readers
1035
1048
  * `recursive` to also include subfolders
1036
1049
  * `title=foo` changes the feed title (default: folder name)
1037
1050
  * `fext=mp3,opus` only include mp3 and opus files (default: all)
@@ -1098,6 +1111,7 @@ some highlights:
1098
1111
  * shows the audio waveform in the seekbar
1099
1112
  * not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
1100
1113
  * videos can be played as audio, without wasting bandwidth on the video
1114
+ * adding `?v` to the end of an audio/video/image link will make it open in the mediaplayer
1101
1115
 
1102
1116
  click the `play` link next to an audio file, or copy the link target to [share it](https://a.ocv.me/pub/demo/music/Ubiktune%20-%20SOUNDSHOCK%202%20-%20FM%20FUNK%20TERRROR!!/#af-1fbfba61&t=18) (optionally with a timestamp to start playing from, like that example does)
1103
1117
 
@@ -1482,7 +1496,12 @@ tweaking the ui
1482
1496
  * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Circle,tags/.tn,tags/Artist,tags/Title,href`
1483
1497
  * to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
1484
1498
 
1485
- see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag, or to add your own translation
1499
+ see [./docs/rice](./docs/rice) for more, including:
1500
+ * how to [hide ui-elements](./docs/rice/README.md#hide-ui-elements)
1501
+ * [custom fonts](./docs/rice/README.md#custom-fonts)
1502
+ * [custom loading-spinner](./docs/rice/README.md#boring-loader-spinner)
1503
+ * adding stuff (css/`<meta>`/...) [to the html `<head>` tag](./docs/rice/README.md#head)
1504
+ * [adding your own translation](./docs/rice/README.md#translations)
1486
1505
 
1487
1506
 
1488
1507
  ## opengraph
@@ -2465,6 +2484,10 @@ copyparty on NixOS is configured via `services.copyparty` options, for example:
2465
2484
  ```nix
2466
2485
  services.copyparty = {
2467
2486
  enable = true;
2487
+ # the user to run the service as
2488
+ user = "copyparty";
2489
+ # the group to run the service as
2490
+ group = "copyparty";
2468
2491
  # directly maps to values in the [global] section of the copyparty config.
2469
2492
  # see `copyparty --help` for available options
2470
2493
  settings = {
@@ -2489,6 +2512,12 @@ services.copyparty = {
2489
2512
  k.passwordFile = "/run/keys/copyparty/k_password";
2490
2513
  };
2491
2514
 
2515
+ # create a group
2516
+ groups = {
2517
+ # users "ed" and "k" are part of the group g1
2518
+ g1 = [ "ed" "k" ];
2519
+ };
2520
+
2492
2521
  # create a volume
2493
2522
  volumes = {
2494
2523
  # create a volume at "/" (the webroot), which will
@@ -2978,6 +3007,20 @@ example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
2978
3007
  * python2.7 on windows: `PRTY_NO_FFMPEG` + `PRTY_NO_FFPROBE` saves startup time
2979
3008
 
2980
3009
 
3010
+ ### dependency unvendoring
3011
+
3012
+ force use of system modules instead of the vendored versions:
3013
+
3014
+ | env-var | what it does |
3015
+ | -------------------- | ------------ |
3016
+ | `PRTY_SYS_ALL` | all of the below |
3017
+ | `PRTY_SYS_DNSLIB` | replace [stolen/dnslib](./copyparty/stolen/dnslib) with [upstream](https://pypi.org/project/dnslib/) |
3018
+ | `PRTY_SYS_IFADDR` | replace [stolen/ifaddr](./copyparty/stolen/ifaddr) with [upstream](https://pypi.org/project/ifaddr/) |
3019
+ | `PRTY_SYS_QRCG` | replace [stolen/qrcodegen.py](./copyparty/stolen/qrcodegen.py) with [upstream](https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py) |
3020
+
3021
+ to debug, run copyparty with `PRTY_MODSPEC=1` to see where it's getting each module from
3022
+
3023
+
2981
3024
  ## optional gpl stuff
2982
3025
 
2983
3026
  some bundled tools have copyleft dependencies, see [./bin/#mtag](bin/#mtag)
@@ -3040,6 +3083,8 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132
3040
3083
 
3041
3084
  * 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})`
3042
3085
 
3086
+ if you are suddenly unable to access storage (permission issues), try forcequitting termux, revoke all of its permissions in android settings, and run the command `termux-setup-storage`
3087
+
3043
3088
 
3044
3089
  # install on iOS
3045
3090
 
@@ -97,6 +97,25 @@ web/splash.html
97
97
  web/splash.js
98
98
  web/svcs.html
99
99
  web/svcs.js
100
+ web/tl/chi.js
101
+ web/tl/cze.js
102
+ web/tl/deu.js
103
+ web/tl/epo.js
104
+ web/tl/fin.js
105
+ web/tl/fra.js
106
+ web/tl/grc.js
107
+ web/tl/ita.js
108
+ web/tl/kor.js
109
+ web/tl/nld.js
110
+ web/tl/nno.js
111
+ web/tl/nor.js
112
+ web/tl/pol.js
113
+ web/tl/por.js
114
+ web/tl/rus.js
115
+ web/tl/spa.js
116
+ web/tl/swe.js
117
+ web/tl/tur.js
118
+ web/tl/ukr.js
100
119
  web/ui.css
101
120
  web/up2k.js
102
121
  web/util.js
@@ -641,8 +641,11 @@ def get_sects():
641
641
  if no accounts or volumes are configured,
642
642
  current folder will be read/write for everyone
643
643
 
644
- the group @acct will always have every user with an account
645
- (the name of that group can be changed with --grp-all)
644
+ the group \033[33m@acct\033[0m will always have every user with an account
645
+ (the name of that group can be changed with \033[32m--grp-all\033[0m)
646
+
647
+ to hide a volume from authenticated users, specify \033[33m*,-@acct\033[0m
648
+ to subtract \033[33m@acct\033[0m from \033[33m*\033[0m (can subtract users from groups too)
646
649
 
647
650
  consider the config file for more flexible account/volume management,
648
651
  including dynamic reload at runtime (and being more readable w)
@@ -664,12 +667,12 @@ def get_sects():
664
667
 
665
668
  send the password in the '\033[36mPW\033[0m' http-header:
666
669
  \033[36mPW: \033[35mhunter2\033[0m
667
- or if you have \033[33m--accounts\033[0m enabled,
670
+ or if you have \033[33m--usernames\033[0m enabled,
668
671
  \033[36mPW: \033[35med:hunter2\033[0m
669
672
 
670
673
  send the password in the URL itself:
671
674
  \033[36mhttp://127.0.0.1:3923/\033[35m?pw=hunter2\033[0m
672
- or if you have \033[33m--accounts\033[0m enabled,
675
+ or if you have \033[33m--usernames\033[0m enabled,
673
676
  \033[36mhttp://127.0.0.1:3923/\033[35m?pw=ed:hunter2\033[0m
674
677
 
675
678
  use basic-authentication:
@@ -805,9 +808,11 @@ def get_sects():
805
808
  \033[36mf\033[35m forks the process, doesn't wait for completion
806
809
  \033[36mc\033[35m checks return code, blocks the action if non-zero
807
810
  \033[36mj\033[35m provides json with info as 1st arg instead of filepath
811
+ \033[36ms\033[35m provides input data on stdin (instead of 1st arg)
808
812
  \033[36mwN\033[35m waits N sec after command has been started before continuing
809
813
  \033[36mtN\033[35m sets an N sec timeout before the command is abandoned
810
814
  \033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
815
+ \033[36mI\033[35m import and run as module, not as subprocess
811
816
 
812
817
  \033[36mar\033[35m only run hook if user has read-access
813
818
  \033[36marw\033[35m only run hook if user has read-write-access
@@ -837,6 +842,9 @@ def get_sects():
837
842
  the \033[33m--\033[35m stops notify-send from reading the message as args
838
843
  and the alert will be "hey" followed by the messagetext
839
844
 
845
+ \033[36m--xm s,,tee,-a,log.txt\033[35m appends each msg to log.txt;
846
+ \033[36m--xm s,j,,tee,-a,log.txt\033[35m writes it as json instead
847
+
840
848
  \033[36m--xau zmq:pub:tcp://*:5556\033[35m announces uploads on zeromq;
841
849
  \033[36m--xau t3,zmq:push:tcp://*:5557\033[35m also works, and you can
842
850
  \033[36m--xau t3,j,zmq:req:tcp://localhost:5555\033[35m too for example
@@ -846,7 +854,8 @@ def get_sects():
846
854
  as soon as the volume has been idle for iN seconds (5 by default)
847
855
 
848
856
  \033[36mxiu\033[0m is also unique in that it will pass the metadata to the
849
- executed program on STDIN instead of as argv arguments, and
857
+ executed program on STDIN instead of as argv arguments (so
858
+ just like the \033[36ms\033[0m option does for the other hook types), and
850
859
  it also includes the wark (file-id/hash) as a json property
851
860
 
852
861
  \033[36mxban\033[0m can be used to overrule / cancel a user ban event;
@@ -857,6 +866,12 @@ def get_sects():
857
866
  on new uploads, but with certain limitations. See
858
867
  bin/hooks/reloc* and docs/devnotes.md#hook-effects
859
868
 
869
+ the \033[36mI\033[0m option will override most other options, because
870
+ it entirely hands over control to the hook, which is
871
+ then able to tamper with copyparty's internal memory
872
+ and wreck havoc if it wants to -- but this is worh it
873
+ because it makes the hook 140x faster
874
+
860
875
  except for \033[36mxm\033[0m, only one hook / one action can run at a time,
861
876
  so it's recommended to use the \033[36mf\033[0m flag unless you really need
862
877
  to wait for the hook to finish before continuing (without \033[36mf\033[0m
@@ -1147,11 +1162,14 @@ def add_general(ap, nc, srvname):
1147
1162
  ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
1148
1163
  ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
1149
1164
  ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
1165
+ ap2.add_argument("--name-url", metavar="TXT", type=u, help="URL for server name hyperlink (displayed topleft in browser)")
1166
+ ap2.add_argument("--name-html", type=u, help=argparse.SUPPRESS)
1150
1167
  ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="\033[34mREPEATABLE:\033[0m map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
1151
1168
  ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
1152
1169
  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)")
1153
1170
  ap2.add_argument("--license", action="store_true", help="show licenses and exit")
1154
1171
  ap2.add_argument("--version", action="store_true", help="show versions and exit")
1172
+ ap2.add_argument("--versionb", action="store_true", help="show version and exit")
1155
1173
 
1156
1174
 
1157
1175
  def add_qr(ap, tty):
@@ -1219,6 +1237,7 @@ def add_upload(ap):
1219
1237
  ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
1220
1238
  ap2.add_argument("--reflink", action="store_true", help="enable reflink-based dedup; will fallback on full copies when that is impossible (non-CoW filesystem) (volflag=reflink)")
1221
1239
  ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
1240
+ ap2.add_argument("--no-dupe-m", action="store_true", help="also reject dupes when moving a file into another volume (volflag=nodupem)")
1222
1241
  ap2.add_argument("--no-clone", action="store_true", help="do not use existing data on disk to satisfy dupe uploads; reduces server HDD reads in exchange for much more network load (volflag=noclone)")
1223
1242
  ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
1224
1243
  ap2.add_argument("--snap-wri", metavar="SEC", type=int, default=300, help="write upload state to ./hist/up2k.snap every \033[33mSEC\033[0m seconds; allows resuming incomplete uploads after a server crash")
@@ -1506,6 +1525,7 @@ def add_optouts(ap):
1506
1525
  ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1507
1526
  ap2.add_argument("--no-tail", action="store_true", help="disable streaming a growing files with ?tail (volflag=notail)")
1508
1527
  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)")
1528
+ ap2.add_argument("--no-zls", action="store_true", help="disable browsing the contents of zip/cbz files, does not affect thumbnails")
1509
1529
 
1510
1530
 
1511
1531
  def add_safety(ap):
@@ -1524,6 +1544,7 @@ def add_safety(ap):
1524
1544
  ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore \033[33m--no-robots\033[0m")
1525
1545
  ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything (volflag=norobots)")
1526
1546
  ap2.add_argument("--logout", metavar="H", type=float, default=8086.0, help="logout clients after \033[33mH\033[0m hours of inactivity; [\033[32m0.0028\033[0m]=10sec, [\033[32m0.1\033[0m]=6min, [\033[32m24\033[0m]=day, [\033[32m168\033[0m]=week, [\033[32m720\033[0m]=month, [\033[32m8760\033[0m]=year)")
1547
+ ap2.add_argument("--dont-ban", metavar="TXT", type=u, default="no", help="anyone at this accesslevel or above will not get banned: [\033[32mav\033[0m]=admin-in-volume, [\033[32maa\033[0m]=has-admin-anywhere, [\033[32mrw\033[0m]=read-write, [\033[32mauth\033[0m]=authenticated, [\033[32many\033[0m]=disable-all-bans, [\033[32mno\033[0m]=anyone-can-get-banned")
1527
1548
  ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
1528
1549
  ap2.add_argument("--ban-pwc", metavar="N,W,B", type=u, default="5,60,1440", help="more than \033[33mN\033[0m password-changes in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
1529
1550
  ap2.add_argument("--ban-404", metavar="N,W,B", type=u, default="50,60,1440", help="hitting more than \033[33mN\033[0m 404's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; only affects users who cannot see directory listings because their access is either g/G/h")
@@ -1531,7 +1552,7 @@ def add_safety(ap):
1531
1552
  ap2.add_argument("--ban-422", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m 422's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes (invalid requests, attempted exploits ++)")
1532
1553
  ap2.add_argument("--ban-url", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m sus URL's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; applies only to permissions g/G/h (decent replacement for \033[33m--ban-404\033[0m if that can't be used)")
1533
1554
  ap2.add_argument("--sus-urls", metavar="R", type=u, default=r"\.php$|(^|/)wp-(admin|content|includes)/", help="URLs which are considered sus / eligible for banning; disable with blank or [\033[32mno\033[0m]")
1534
- ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\.ico|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 404-bans; disable with blank or [\033[32mno\033[0m]")
1555
+ ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\..{3}|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 403/404-bans; disable with blank or [\033[32mno\033[0m]")
1535
1556
  ap2.add_argument("--early-ban", action="store_true", help="if a client is banned, reject its connection as soon as possible; not a good idea to enable when proxied behind cloudflare since it could ban your reverse-proxy")
1536
1557
  ap2.add_argument("--cookie-nmax", metavar="N", type=int, default=50, help="reject HTTP-request from client if they send more than N cookies")
1537
1558
  ap2.add_argument("--cookie-cmax", metavar="N", type=int, default=8192, help="reject HTTP-request from client if more than N characters in Cookie header")
@@ -1588,12 +1609,14 @@ def add_admin(ap):
1588
1609
  ap2 = ap.add_argument_group("admin panel options")
1589
1610
  ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
1590
1611
  ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
1591
- ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
1612
+ ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks); same as --stack-who=no")
1592
1613
  ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
1593
1614
  ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
1594
1615
  ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
1595
1616
  ap2.add_argument("--ups-who", metavar="LVL", type=int, default=2, help="who can see recent uploads on the ?ru page? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone (volflag=ups_who)")
1596
1617
  ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
1618
+ ap2.add_argument("--stack-who", metavar="LVL", type=u, default="a", help="who can see the ?stack page (list of threads)? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admins, [\033[32mrw\033[0m]=read+write, [\033[32mall\033[0m]=everyone")
1619
+ ap2.add_argument("--stack-v", action="store_true", help="verbose ?stack")
1597
1620
 
1598
1621
 
1599
1622
  def add_thumbnail(ap):
@@ -1791,6 +1814,15 @@ def add_ui(ap, retry ):
1791
1814
  ap2.add_argument("--lg-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for prologue/epilogue docs (volflag=lg_sba); see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#iframes")
1792
1815
  ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
1793
1816
  ap2.add_argument("--no-sb-lg", action="store_true", help="don't sandbox prologue/epilogue docs (volflags: no_sb_lg | sb_lg); enables non-js support")
1817
+ ap2.add_argument("--ui-nombar", action="store_true", help="hide top-menu in the UI (volflag=ui_nombar)")
1818
+ ap2.add_argument("--ui-noacci", action="store_true", help="hide account-info in the UI (volflag=ui_noacci)")
1819
+ ap2.add_argument("--ui-nosrvi", action="store_true", help="hide server-info in the UI (volflag=ui_nosrvi)")
1820
+ ap2.add_argument("--ui-nonav", action="store_true", help="hide navpane+breadcrumbs (volflag=ui_nonav)")
1821
+ ap2.add_argument("--ui-notree", action="store_true", help="hide navpane in the UI (volflag=ui_nonav)")
1822
+ ap2.add_argument("--ui-nocpla", action="store_true", help="hide cpanel-link in the UI (volflag=ui_nocpla)")
1823
+ ap2.add_argument("--ui-nolbar", action="store_true", help="hide link-bar in the UI (volflag=ui_nolbar)")
1824
+ ap2.add_argument("--ui-noctxb", action="store_true", help="hide context-buttons in the UI (volflag=ui_noctxb)")
1825
+ ap2.add_argument("--ui-norepl", action="store_true", help="hide repl-button in the UI (volflag=ui_norepl)")
1794
1826
  ap2.add_argument("--have-unlistc", action="store_true", help=argparse.SUPPRESS)
1795
1827
 
1796
1828
 
@@ -1918,15 +1950,19 @@ def run_argparse(
1918
1950
 
1919
1951
 
1920
1952
  def main(argv = None) :
1953
+ if argv is None:
1954
+ argv = sys.argv
1955
+
1956
+ if "--versionb" in argv:
1957
+ print(S_VERSION)
1958
+ sys.exit(0)
1959
+
1921
1960
  time.strptime("19970815", "%Y%m%d") # python#7980
1922
1961
  if WINDOWS:
1923
1962
  os.system("rem") # enables colors
1924
1963
 
1925
1964
  init_E(E)
1926
1965
 
1927
- if argv is None:
1928
- argv = sys.argv
1929
-
1930
1966
  f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0;36m\n sqlite {} | jinja {} | pyftpd {} | tftp {}\n\033[0m'
1931
1967
  f = f.format(
1932
1968
  S_VERSION,
@@ -2053,7 +2089,7 @@ def main(argv = None) :
2053
2089
 
2054
2090
  # propagate implications
2055
2091
  for k1, k2 in IMPLICATIONS:
2056
- if getattr(al, k1):
2092
+ if getattr(al, k1, None):
2057
2093
  setattr(al, k2, True)
2058
2094
 
2059
2095
  # propagate unplications
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 19, 15)
3
+ VERSION = (1, 19, 17)
4
4
  CODENAME = "usernames"
5
- BUILD_DT = (2025, 9, 29)
5
+ BUILD_DT = (2025, 10, 17)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -1961,9 +1961,18 @@ class AuthSrv(object):
1961
1961
  axs_key = "u" + perm
1962
1962
  for vp, vol in vfs.all_vols.items():
1963
1963
  zx = getattr(vol.axs, axs_key)
1964
- if "*" in zx:
1964
+ if "*" in zx and "-@acct" not in zx:
1965
1965
  for usr in unames:
1966
1966
  zx.add(usr)
1967
+ for zs in list(zx):
1968
+ if zs.startswith("-"):
1969
+ zx.discard(zs)
1970
+ zs = zs[1:]
1971
+ zx.discard(zs)
1972
+ if zs.startswith("@"):
1973
+ zs = zs[1:]
1974
+ for zs in grps.get(zs) or []:
1975
+ zx.discard(zs)
1967
1976
 
1968
1977
  # aread,... = dict[uname, list[volnames] or []]
1969
1978
  umap = {x: [] for x in unames}
@@ -2503,13 +2512,17 @@ class AuthSrv(object):
2503
2512
 
2504
2513
  ico_url = vol.flags.get("ufavico")
2505
2514
  if ico_url:
2515
+ ico_h = ""
2506
2516
  ico_ext = ico_url.split("?")[0].split(".")[-1].lower()
2507
2517
  if ico_ext in FAVICON_MIMES:
2508
2518
  zs = '<link rel="icon" type="%s" href="%s">\n'
2509
- head_s += zs % (FAVICON_MIMES[ico_ext], ico_url)
2519
+ ico_h = zs % (FAVICON_MIMES[ico_ext], ico_url)
2510
2520
  elif ico_ext == "ico":
2511
2521
  zs = '<link rel="shortcut icon" href="%s">\n'
2512
- head_s += zs % (ico_url,)
2522
+ ico_h = zs % (ico_url,)
2523
+ if ico_h:
2524
+ vol.flags["ufavico_h"] = ico_h
2525
+ head_s += ico_h
2513
2526
 
2514
2527
  if head_s:
2515
2528
  vol.flags["html_head_s"] = head_s
@@ -2573,6 +2586,15 @@ class AuthSrv(object):
2573
2586
  for x in drop:
2574
2587
  vol.flags.pop(x)
2575
2588
 
2589
+ zi = vol.flags.get("lifetime") or 0
2590
+ zi2 = time.time() // (86400 * 365)
2591
+ zi3 = zi2 * 86400 * 365
2592
+ if zi < 0 or zi > zi3:
2593
+ t = "the lifetime of volume [/%s] (%d) exceeds max value (%d years; %d)"
2594
+ t = t % (vol.vpath, zi, zi2, zi3)
2595
+ self.log(t, 1)
2596
+ raise Exception(t)
2597
+
2576
2598
  # verify tags mentioned by -mt[mp] are used by -mte
2577
2599
  local_mtp = {}
2578
2600
  local_only_mtp = {}
@@ -2739,9 +2761,13 @@ class AuthSrv(object):
2739
2761
  ["uadmin", "uadmin"],
2740
2762
  ]:
2741
2763
  u = list(sorted(getattr(zv.axs, attr)))
2742
- u = ["*"] if "*" in u else u
2743
- u = ", ".join("\033[35meverybody\033[0m" if x == "*" else x for x in u)
2744
- u = u if u else "\033[36m--none--\033[0m"
2764
+ if u == ["*"] and acct:
2765
+ u = ["\033[35monly-anonymous\033[0m"]
2766
+ elif "*" in u:
2767
+ u = ["\033[35meverybody\033[0m"]
2768
+ if not u:
2769
+ u = ["\033[36m--none--\033[0m"]
2770
+ u = ", ".join(u)
2745
2771
  t += "\n| {}: {}".format(txt, u)
2746
2772
 
2747
2773
  if "e2d" in zv.flags:
@@ -2853,8 +2879,6 @@ class AuthSrv(object):
2853
2879
 
2854
2880
  if have_reflink:
2855
2881
  t = "WARNING: Reflink-based dedup was requested, but %s. This will not work; files will be full copies instead."
2856
- if sys.version_info < (3, 14):
2857
- self.log(t % "your python version is not new enough", 1)
2858
2882
  if not sys.platform.startswith("linux"):
2859
2883
  self.log(t % "your OS is not Linux", 1)
2860
2884
 
@@ -2931,6 +2955,11 @@ class AuthSrv(object):
2931
2955
  shn.shr_src = (s_vfs, s_rem)
2932
2956
  shn.realpath = s_vfs.canonical(s_rem)
2933
2957
 
2958
+ o_vn, _ = shn._get_share_src("")
2959
+ shn.flags = o_vn.flags.copy()
2960
+ shn.dbpath = o_vn.dbpath
2961
+ shn.histpath = o_vn.histpath
2962
+
2934
2963
  # root.all_aps doesn't include any shares, so make a copy where the
2935
2964
  # share appears in all abspaths it can provide (for example for chk_ap)
2936
2965
  ap = shn.realpath
@@ -2994,6 +3023,8 @@ class AuthSrv(object):
2994
3023
  "unlist": vf.get("unlist") or "",
2995
3024
  "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
2996
3025
  }
3026
+ if "ufavico_h" in vf:
3027
+ vn.js_ls["ufavico"] = vf["ufavico_h"]
2997
3028
  js_htm = {
2998
3029
  "SPINNER": self.args.spinner,
2999
3030
  "s_name": self.args.bname,
@@ -3005,6 +3036,7 @@ class AuthSrv(object):
3005
3036
  "have_shr": self.args.shr,
3006
3037
  "shr_who": vf["shr_who"],
3007
3038
  "have_zip": not self.args.no_zip,
3039
+ "have_zls": not self.args.no_zls,
3008
3040
  "have_mv": not self.args.no_mv,
3009
3041
  "have_del": not self.args.no_del,
3010
3042
  "have_unpost": int(self.args.unpost),
@@ -3029,7 +3061,7 @@ class AuthSrv(object):
3029
3061
  "dvol": self.args.au_vol,
3030
3062
  "idxh": int(self.args.ih),
3031
3063
  "dutc": not self.args.localtime,
3032
- "dfszf": self.args.ui_filesz,
3064
+ "dfszf": self.args.ui_filesz.strip("-"),
3033
3065
  "themes": self.args.themes,
3034
3066
  "turbolvl": self.args.turbo,
3035
3067
  "nosubtle": self.args.nosubtle,
@@ -3041,6 +3073,10 @@ class AuthSrv(object):
3041
3073
  "lifetime": vn.js_ls["lifetime"],
3042
3074
  "u2sort": self.args.u2sort,
3043
3075
  }
3076
+ zs = "ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi"
3077
+ for zs in zs.split():
3078
+ if vf.get(zs):
3079
+ js_htm[zs] = 1
3044
3080
  vn.js_htm = json_hesc(json.dumps(js_htm))
3045
3081
 
3046
3082
  vols = list(vfs.all_nodes.values())
@@ -99,7 +99,11 @@ def utime(
99
99
 
100
100
 
101
101
  def utime_c(
102
- log , p , ts , follow_symlinks = True, throw = False
102
+ log ,
103
+ p ,
104
+ ts ,
105
+ follow_symlinks = True,
106
+ throw = False,
103
107
  ) :
104
108
  clamp = 0
105
109
  ov = ts