copyparty 1.16.4__tar.gz → 1.16.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {copyparty-1.16.4 → copyparty-1.16.6}/PKG-INFO +20 -3
  2. {copyparty-1.16.4 → copyparty-1.16.6}/README.md +19 -2
  3. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/__init__.py +3 -0
  4. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/__main__.py +6 -3
  5. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/__version__.py +2 -2
  6. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/authsrv.py +31 -18
  7. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/fsutil.py +4 -4
  8. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/httpcli.py +220 -79
  9. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/httpsrv.py +6 -5
  10. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/mtag.py +4 -4
  11. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/smbd.py +1 -1
  12. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/sutil.py +1 -1
  13. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/tcpsrv.py +6 -6
  14. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/tftpd.py +1 -1
  15. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/th_cli.py +3 -3
  16. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/th_srv.py +6 -6
  17. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/u2idx.py +3 -3
  18. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/up2k.py +91 -86
  19. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/util.py +13 -13
  20. copyparty-1.16.6/copyparty/web/deps/marked.js.gz +0 -0
  21. copyparty-1.16.6/copyparty/web/rups.css.gz +0 -0
  22. copyparty-1.16.6/copyparty/web/rups.html +67 -0
  23. copyparty-1.16.6/copyparty/web/rups.js.gz +0 -0
  24. copyparty-1.16.6/copyparty/web/shares.css.gz +0 -0
  25. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/shares.html +2 -0
  26. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/splash.html +1 -0
  27. copyparty-1.16.6/copyparty/web/splash.js.gz +0 -0
  28. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/svcs.html +0 -2
  29. copyparty-1.16.6/copyparty/web/up2k.js.gz +0 -0
  30. copyparty-1.16.6/copyparty/web/util.js.gz +0 -0
  31. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/PKG-INFO +20 -3
  32. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/SOURCES.txt +3 -0
  33. copyparty-1.16.4/copyparty/web/deps/marked.js.gz +0 -0
  34. copyparty-1.16.4/copyparty/web/shares.css.gz +0 -0
  35. copyparty-1.16.4/copyparty/web/splash.js.gz +0 -0
  36. copyparty-1.16.4/copyparty/web/up2k.js.gz +0 -0
  37. copyparty-1.16.4/copyparty/web/util.js.gz +0 -0
  38. {copyparty-1.16.4 → copyparty-1.16.6}/LICENSE +0 -0
  39. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/bos/__init__.py +0 -0
  40. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/bos/bos.py +0 -0
  41. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/bos/path.py +0 -0
  42. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/broker_mp.py +0 -0
  43. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/broker_mpw.py +0 -0
  44. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/broker_thr.py +0 -0
  45. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/broker_util.py +0 -0
  46. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/cert.py +0 -0
  47. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/cfg.py +0 -0
  48. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/dxml.py +0 -0
  49. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/ftpd.py +0 -0
  50. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/httpconn.py +0 -0
  51. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/ico.py +0 -0
  52. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/mdns.py +0 -0
  53. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/metrics.py +0 -0
  54. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/multicast.py +0 -0
  55. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/pwhash.py +0 -0
  56. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/res/COPYING.txt +0 -0
  57. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/res/__init__.py +0 -0
  58. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/res/insecure.pem +0 -0
  59. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/ssdp.py +0 -0
  60. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/star.py +0 -0
  61. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/__init__.py +0 -0
  62. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/__init__.py +0 -0
  63. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/bimap.py +0 -0
  64. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/bit.py +0 -0
  65. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/buffer.py +0 -0
  66. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/dns.py +0 -0
  67. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/label.py +0 -0
  68. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/lex.py +0 -0
  69. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/dnslib/ranges.py +0 -0
  70. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/ifaddr/__init__.py +0 -0
  71. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/ifaddr/_posix.py +0 -0
  72. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/ifaddr/_shared.py +0 -0
  73. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/ifaddr/_win32.py +0 -0
  74. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/qrcodegen.py +0 -0
  75. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/stolen/surrogateescape.py +0 -0
  76. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/svchub.py +0 -0
  77. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/szip.py +0 -0
  78. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/a/__init__.py +0 -0
  79. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/a/partyfuse.py +0 -0
  80. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/a/u2c.py +0 -0
  81. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/a/webdav-cfg.bat +0 -0
  82. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/baguettebox.js.gz +0 -0
  83. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/browser.css.gz +0 -0
  84. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/browser.html +0 -0
  85. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/browser.js.gz +0 -0
  86. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/browser2.html +0 -0
  87. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/cf.html +0 -0
  88. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dbg-audio.js.gz +0 -0
  89. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dd/2.png +0 -0
  90. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dd/3.png +0 -0
  91. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dd/4.png +0 -0
  92. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dd/5.png +0 -0
  93. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/dd/__init__.py +0 -0
  94. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/__init__.py +0 -0
  95. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/busy.mp3.gz +0 -0
  96. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/easymde.css.gz +0 -0
  97. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/easymde.js.gz +0 -0
  98. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/fuse.py +0 -0
  99. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/mini-fa.css.gz +0 -0
  100. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/mini-fa.woff +0 -0
  101. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/prism.css.gz +0 -0
  102. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/prism.js.gz +0 -0
  103. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/prismd.css.gz +0 -0
  104. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/scp.woff2 +0 -0
  105. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  106. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  107. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/md.css.gz +0 -0
  108. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/md.html +0 -0
  109. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/md.js.gz +0 -0
  110. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/md2.css.gz +0 -0
  111. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/md2.js.gz +0 -0
  112. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/mde.css.gz +0 -0
  113. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/mde.html +0 -0
  114. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/mde.js.gz +0 -0
  115. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/msg.css.gz +0 -0
  116. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/msg.html +0 -0
  117. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/shares.js.gz +0 -0
  118. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/splash.css.gz +0 -0
  119. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/svcs.js.gz +0 -0
  120. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/ui.css.gz +0 -0
  121. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty/web/w.hash.js.gz +0 -0
  122. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/dependency_links.txt +0 -0
  123. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/entry_points.txt +0 -0
  124. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/requires.txt +0 -0
  125. {copyparty-1.16.4 → copyparty-1.16.6}/copyparty.egg-info/top_level.txt +0 -0
  126. {copyparty-1.16.4 → copyparty-1.16.6}/pyproject.toml +0 -0
  127. {copyparty-1.16.4 → copyparty-1.16.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.16.4
3
+ Version: 1.16.6
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -103,6 +103,7 @@ turn almost any device into a file server with resumable uploads/downloads using
103
103
  * [shares](#shares) - share a file or folder by creating a temporary link
104
104
  * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
105
105
  * [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
106
+ * [recent uploads](#recent-uploads) - list all recent uploads
106
107
  * [media player](#media-player) - plays almost every audio format there is
107
108
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
108
109
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
@@ -394,6 +395,9 @@ same order here too
394
395
 
395
396
  * [Chrome issue 1352210](https://bugs.chromium.org/p/chromium/issues/detail?id=1352210) -- plaintext http may be faster at filehashing than https (but also extremely CPU-intensive)
396
397
 
398
+ * [Chrome issue 383568268](https://issues.chromium.org/issues/383568268) -- filereaders in webworkers can OOM / crash the browser-tab
399
+ * copyparty has a workaround which seems to work well enough
400
+
397
401
  * [Firefox issue 1790500](https://bugzilla.mozilla.org/show_bug.cgi?id=1790500) -- entire browser can crash after uploading ~4000 small files
398
402
 
399
403
  * Android: music playback randomly stops due to [battery usage settings](#fix-unreliable-playback-on-android)
@@ -769,7 +773,7 @@ files go into `[ok]` if they exist (and you get a link to where it is), otherwis
769
773
 
770
774
  ### unpost
771
775
 
772
- undo/delete accidental uploads
776
+ undo/delete accidental uploads using the `[🧯]` tab in the UI
773
777
 
774
778
  ![copyparty-unpost-fs8](https://user-images.githubusercontent.com/241032/129635368-3afa6634-c20f-418c-90dc-ec411f3b3897.png)
775
779
 
@@ -928,6 +932,17 @@ url parameters:
928
932
  * uppercase = reverse-sort; `M` = oldest file first
929
933
 
930
934
 
935
+ ## recent uploads
936
+
937
+ list all recent uploads by clicking "show recent uploads" in the controlpanel
938
+
939
+ will show uploader IP and upload-time if the visitor has the admin permission
940
+
941
+ * global-option `--ups-when` makes upload-time visible to all users, and not just admins
942
+
943
+ note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them
944
+
945
+
931
946
  ## media player
932
947
 
933
948
  plays almost every audio format there is (if the server has FFmpeg installed for on-demand transcoding)
@@ -1543,7 +1558,9 @@ replace copyparty passwords with oauth and such
1543
1558
 
1544
1559
  you can disable the built-in password-based login system, and instead replace it with a separate piece of software (an identity provider) which will then handle authenticating / authorizing of users; this makes it possible to login with passkeys / fido2 / webauthn / yubikey / ldap / active directory / oauth / many other single-sign-on contraptions
1545
1560
 
1546
- a popular choice is [Authelia](https://www.authelia.com/) (config-file based), another one is [authentik](https://goauthentik.io/) (GUI-based, more complex)
1561
+ * the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
1562
+
1563
+ some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
1547
1564
 
1548
1565
  there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)
1549
1566
 
@@ -48,6 +48,7 @@ turn almost any device into a file server with resumable uploads/downloads using
48
48
  * [shares](#shares) - share a file or folder by creating a temporary link
49
49
  * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
50
50
  * [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
51
+ * [recent uploads](#recent-uploads) - list all recent uploads
51
52
  * [media player](#media-player) - plays almost every audio format there is
52
53
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
53
54
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
@@ -339,6 +340,9 @@ same order here too
339
340
 
340
341
  * [Chrome issue 1352210](https://bugs.chromium.org/p/chromium/issues/detail?id=1352210) -- plaintext http may be faster at filehashing than https (but also extremely CPU-intensive)
341
342
 
343
+ * [Chrome issue 383568268](https://issues.chromium.org/issues/383568268) -- filereaders in webworkers can OOM / crash the browser-tab
344
+ * copyparty has a workaround which seems to work well enough
345
+
342
346
  * [Firefox issue 1790500](https://bugzilla.mozilla.org/show_bug.cgi?id=1790500) -- entire browser can crash after uploading ~4000 small files
343
347
 
344
348
  * Android: music playback randomly stops due to [battery usage settings](#fix-unreliable-playback-on-android)
@@ -714,7 +718,7 @@ files go into `[ok]` if they exist (and you get a link to where it is), otherwis
714
718
 
715
719
  ### unpost
716
720
 
717
- undo/delete accidental uploads
721
+ undo/delete accidental uploads using the `[🧯]` tab in the UI
718
722
 
719
723
  ![copyparty-unpost-fs8](https://user-images.githubusercontent.com/241032/129635368-3afa6634-c20f-418c-90dc-ec411f3b3897.png)
720
724
 
@@ -873,6 +877,17 @@ url parameters:
873
877
  * uppercase = reverse-sort; `M` = oldest file first
874
878
 
875
879
 
880
+ ## recent uploads
881
+
882
+ list all recent uploads by clicking "show recent uploads" in the controlpanel
883
+
884
+ will show uploader IP and upload-time if the visitor has the admin permission
885
+
886
+ * global-option `--ups-when` makes upload-time visible to all users, and not just admins
887
+
888
+ note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them
889
+
890
+
876
891
  ## media player
877
892
 
878
893
  plays almost every audio format there is (if the server has FFmpeg installed for on-demand transcoding)
@@ -1488,7 +1503,9 @@ replace copyparty passwords with oauth and such
1488
1503
 
1489
1504
  you can disable the built-in password-based login system, and instead replace it with a separate piece of software (an identity provider) which will then handle authenticating / authorizing of users; this makes it possible to login with passkeys / fido2 / webauthn / yubikey / ldap / active directory / oauth / many other single-sign-on contraptions
1490
1505
 
1491
- a popular choice is [Authelia](https://www.authelia.com/) (config-file based), another one is [authentik](https://goauthentik.io/) (GUI-based, more complex)
1506
+ * the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
1507
+
1508
+ some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
1492
1509
 
1493
1510
  there is a [docker-compose example](./docs/examples/docker/idp-authelia-traefik) which is hopefully a good starting point (alternatively see [./docs/idp.md](./docs/idp.md) if you're the DIY type)
1494
1511
 
@@ -88,6 +88,9 @@ web/mde.html
88
88
  web/mde.js
89
89
  web/msg.css
90
90
  web/msg.html
91
+ web/rups.css
92
+ web/rups.html
93
+ web/rups.js
91
94
  web/shares.css
92
95
  web/shares.html
93
96
  web/shares.js
@@ -1075,7 +1075,7 @@ def add_cert(ap, cert_path):
1075
1075
  def add_auth(ap):
1076
1076
  ses_db = os.path.join(E.cfg, "sessions.db")
1077
1077
  ap2 = ap.add_argument_group('IdP / identity provider / user authentication options')
1078
- ap2.add_argument("--idp-h-usr", metavar="HN", type=u, default="", help="bypass the copyparty authentication checks and assume the request-header \033[33mHN\033[0m contains the username of the requesting user (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")
1078
+ 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")
1079
1079
  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")
1080
1080
  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")
1081
1081
  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")
@@ -1242,7 +1242,6 @@ def add_optouts(ap):
1242
1242
  ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
1243
1243
  ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
1244
1244
  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")
1245
- ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
1246
1245
  ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1247
1246
  ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
1248
1247
 
@@ -1318,7 +1317,10 @@ def add_admin(ap):
1318
1317
  ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)")
1319
1318
  ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)")
1320
1319
  ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)")
1320
+ ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
1321
+ ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
1321
1322
  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")
1323
+ ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
1322
1324
 
1323
1325
 
1324
1326
  def add_thumbnail(ap):
@@ -1497,6 +1499,7 @@ def add_debug(ap):
1497
1499
  ap2.add_argument("--bf-nc", metavar="NUM", type=int, default=200, help="bak-flips: stop if there's more than \033[33mNUM\033[0m files at \033[33m--kf-dir\033[0m already; default: 6.3 GiB max (200*32M)")
1498
1500
  ap2.add_argument("--bf-dir", metavar="PATH", type=u, default="bf", help="bak-flips: store corrupted chunks at \033[33mPATH\033[0m; default: folder named 'bf' wherever copyparty was started")
1499
1501
  ap2.add_argument("--bf-log", metavar="PATH", type=u, default="", help="bak-flips: log corruption info to a textfile at \033[33mPATH\033[0m")
1502
+ ap2.add_argument("--no-cfg-cmt-warn", action="store_true", help=argparse.SUPPRESS)
1500
1503
 
1501
1504
 
1502
1505
  # fmt: on
@@ -1729,7 +1732,7 @@ def main(argv = None) :
1729
1732
  except:
1730
1733
  lprint("\nfailed to disable quick-edit-mode:\n" + min_ex() + "\n")
1731
1734
 
1732
- if al.ansi:
1735
+ if not al.ansi:
1733
1736
  al.wintitle = ""
1734
1737
 
1735
1738
  # propagate implications
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 16, 4)
3
+ VERSION = (1, 16, 6)
4
4
  CODENAME = "COPYparty"
5
- BUILD_DT = (2024, 12, 7)
5
+ BUILD_DT = (2024, 12, 19)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -205,7 +205,7 @@ class Lim(object):
205
205
 
206
206
  df, du, err = get_df(abspath, True)
207
207
  if err:
208
- t = "failed to read disk space usage for [%s]: %s"
208
+ t = "failed to read disk space usage for %r: %s"
209
209
  self.log(t % (abspath, err), 3)
210
210
  self.dfv = 0xAAAAAAAAA # 42.6 GiB
211
211
  else:
@@ -519,7 +519,7 @@ class VFS(object):
519
519
  """returns [vfsnode,fs_remainder] if user has the requested permissions"""
520
520
  if relchk(vpath):
521
521
  if self.log:
522
- self.log("vfs", "invalid relpath [{}]".format(vpath))
522
+ self.log("vfs", "invalid relpath %r @%s" % (vpath, uname))
523
523
  raise Pebkac(422)
524
524
 
525
525
  cvpath = undot(vpath)
@@ -536,11 +536,11 @@ class VFS(object):
536
536
  if req and uname not in d and uname != LEELOO_DALLAS:
537
537
  if vpath != cvpath and vpath != "." and self.log:
538
538
  ap = vn.canonical(rem)
539
- t = "{} has no {} in [{}] => [{}] => [{}]"
540
- self.log("vfs", t.format(uname, msg, vpath, cvpath, ap), 6)
539
+ t = "%s has no %s in %r => %r => %r"
540
+ self.log("vfs", t % (uname, msg, vpath, cvpath, ap), 6)
541
541
 
542
- t = 'you don\'t have %s-access in "/%s" or below "/%s"'
543
- raise Pebkac(err, t % (msg, cvpath, vn.vpath))
542
+ t = "you don't have %s-access in %r or below %r"
543
+ raise Pebkac(err, t % (msg, "/" + cvpath, "/" + vn.vpath))
544
544
 
545
545
  return vn, rem
546
546
 
@@ -686,8 +686,8 @@ class VFS(object):
686
686
  and fsroot in seen
687
687
  ):
688
688
  if self.log:
689
- t = "bailing from symlink loop,\n prev: {}\n curr: {}\n from: {}/{}"
690
- self.log("vfs.walk", t.format(seen[-1], fsroot, self.vpath, rem), 3)
689
+ t = "bailing from symlink loop,\n prev: %r\n curr: %r\n from: %r / %r"
690
+ self.log("vfs.walk", t % (seen[-1], fsroot, self.vpath, rem), 3)
691
691
  return
692
692
 
693
693
  if "xdev" in self.flags or "xvol" in self.flags:
@@ -699,7 +699,7 @@ class VFS(object):
699
699
  rm1.append(le)
700
700
  _ = [vfs_ls.remove(x) for x in rm1] # type: ignore
701
701
 
702
- dots_ok = wantdots and uname in dbv.axs.udot
702
+ dots_ok = wantdots and (wantdots == 2 or uname in dbv.axs.udot)
703
703
  if not dots_ok:
704
704
  vfs_ls = [x for x in vfs_ls if "/." not in "/" + x[0]]
705
705
 
@@ -753,7 +753,7 @@ class VFS(object):
753
753
  # if single folder: the folder itself is the top-level item
754
754
  folder = "" if flt or not wrap else (vpath.split("/")[-1].lstrip(".") or "top")
755
755
 
756
- g = self.walk(folder, vrem, [], uname, [[True, False]], True, scandir, False)
756
+ g = self.walk(folder, vrem, [], uname, [[True, False]], 1, scandir, False)
757
757
  for _, _, vpath, apath, files, rd, vd in g:
758
758
  if flt:
759
759
  files = [x for x in files if x[0] in flt]
@@ -811,8 +811,8 @@ class VFS(object):
811
811
 
812
812
  if vdev != st.st_dev:
813
813
  if self.log:
814
- t = "xdev: {}[{}] => {}[{}]"
815
- self.log("vfs", t.format(vdev, self.realpath, st.st_dev, ap), 3)
814
+ t = "xdev: %s[%r] => %s[%r]"
815
+ self.log("vfs", t % (vdev, self.realpath, st.st_dev, ap), 3)
816
816
 
817
817
  return None
818
818
 
@@ -822,7 +822,7 @@ class VFS(object):
822
822
  return vn
823
823
 
824
824
  if self.log:
825
- self.log("vfs", "xvol: [{}]".format(ap), 3)
825
+ self.log("vfs", "xvol: %r" % (ap,), 3)
826
826
 
827
827
  return None
828
828
 
@@ -907,7 +907,7 @@ class AuthSrv(object):
907
907
 
908
908
  self.idp_accs[uname] = gnames
909
909
 
910
- t = "reinitializing due to new user from IdP: [%s:%s]"
910
+ t = "reinitializing due to new user from IdP: [%r:%r]"
911
911
  self.log(t % (uname, gnames), 3)
912
912
 
913
913
  if not broker:
@@ -1561,7 +1561,7 @@ class AuthSrv(object):
1561
1561
  continue
1562
1562
 
1563
1563
  if self.args.shr_v:
1564
- t = "loading %s share [%s] by [%s] => [%s]"
1564
+ t = "loading %s share %r by %r => %r"
1565
1565
  self.log(t % (s_pr, s_k, s_un, s_vp))
1566
1566
 
1567
1567
  if s_pw:
@@ -1758,7 +1758,7 @@ class AuthSrv(object):
1758
1758
  use = True
1759
1759
  try:
1760
1760
  _ = float(zs)
1761
- zs = "%sg" % (zs)
1761
+ zs = "%sg" % (zs,)
1762
1762
  except:
1763
1763
  pass
1764
1764
  lim.dfl = unhumanize(zs)
@@ -2530,7 +2530,7 @@ class AuthSrv(object):
2530
2530
  return
2531
2531
 
2532
2532
  elif self.args.chpw_v == 2:
2533
- t = "chpw: %d changed" % (len(uok))
2533
+ t = "chpw: %d changed" % (len(uok),)
2534
2534
  if urst:
2535
2535
  t += ", \033[0munchanged:\033[35m %s" % (", ".join(list(urst)))
2536
2536
 
@@ -2688,7 +2688,7 @@ class AuthSrv(object):
2688
2688
  [],
2689
2689
  u,
2690
2690
  [[True, False]],
2691
- True,
2691
+ 1,
2692
2692
  not self.args.no_scandir,
2693
2693
  False,
2694
2694
  False,
@@ -3009,6 +3009,19 @@ def expand_config_file(
3009
3009
 
3010
3010
  ret.append("#\033[36m closed{}\033[0m".format(ipath))
3011
3011
 
3012
+ zsl = []
3013
+ for ln in ret:
3014
+ zs = ln.split(" #")[0]
3015
+ if " #" in zs and zs.split("#")[0].strip():
3016
+ zsl.append(ln)
3017
+ if zsl and "no-cfg-cmt-warn" not in "\n".join(ret):
3018
+ t = "\033[33mWARNING: there is less than two spaces before the # in the following config lines, so instead of assuming that this is a comment, the whole line will become part of the config value:\n\n>>> %s\n\nif you are familiar with this and would like to mute this warning, specify the global-option no-cfg-cmt-warn\n\033[0m"
3019
+ t = t % ("\n>>> ".join(zsl),)
3020
+ if log:
3021
+ log(t)
3022
+ else:
3023
+ print(t, file=sys.stderr)
3024
+
3012
3025
 
3013
3026
  def upgrade_cfg_fmt(
3014
3027
  log , args , orig , cfg_fp
@@ -36,14 +36,14 @@ class Fstab(object):
36
36
  self.cache = {}
37
37
 
38
38
  fs = "ext4"
39
- msg = "failed to determine filesystem at [{}]; assuming {}\n{}"
39
+ msg = "failed to determine filesystem at %r; assuming %s\n%s"
40
40
 
41
41
  if ANYWIN:
42
42
  fs = "vfat"
43
43
  try:
44
44
  path = self._winpath(path)
45
45
  except:
46
- self.log(msg.format(path, fs, min_ex()), 3)
46
+ self.log(msg % (path, fs, min_ex()), 3)
47
47
  return fs
48
48
 
49
49
  path = undot(path)
@@ -55,11 +55,11 @@ class Fstab(object):
55
55
  try:
56
56
  fs = self.get_w32(path) if ANYWIN else self.get_unix(path)
57
57
  except:
58
- self.log(msg.format(path, fs, min_ex()), 3)
58
+ self.log(msg % (path, fs, min_ex()), 3)
59
59
 
60
60
  fs = fs.lower()
61
61
  self.cache[path] = fs
62
- self.log("found {} at {}".format(fs, path))
62
+ self.log("found %s at %r" % (fs, path))
63
63
  return fs
64
64
 
65
65
  def _winpath(self, path ) :