copyparty 1.15.6__tar.gz → 1.15.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {copyparty-1.15.6 → copyparty-1.15.8}/PKG-INFO +22 -3
  2. {copyparty-1.15.6 → copyparty-1.15.8}/README.md +21 -2
  3. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/__main__.py +3 -1
  4. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/__version__.py +2 -2
  5. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/authsrv.py +1 -0
  6. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/ftpd.py +4 -0
  7. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/httpcli.py +81 -10
  8. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/httpconn.py +2 -0
  9. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/httpsrv.py +8 -0
  10. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/svchub.py +6 -0
  11. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/up2k.py +56 -32
  12. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/util.py +39 -6
  13. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/a/u2c.py +128 -42
  14. copyparty-1.15.8/copyparty/web/baguettebox.js.gz +0 -0
  15. copyparty-1.15.8/copyparty/web/browser.js.gz +0 -0
  16. copyparty-1.15.8/copyparty/web/ui.css.gz +0 -0
  17. copyparty-1.15.8/copyparty/web/up2k.js.gz +0 -0
  18. copyparty-1.15.8/copyparty/web/util.js.gz +0 -0
  19. copyparty-1.15.8/copyparty/web/w.hash.js.gz +0 -0
  20. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/PKG-INFO +22 -3
  21. copyparty-1.15.6/copyparty/web/baguettebox.js.gz +0 -0
  22. copyparty-1.15.6/copyparty/web/browser.js.gz +0 -0
  23. copyparty-1.15.6/copyparty/web/ui.css.gz +0 -0
  24. copyparty-1.15.6/copyparty/web/up2k.js.gz +0 -0
  25. copyparty-1.15.6/copyparty/web/util.js.gz +0 -0
  26. copyparty-1.15.6/copyparty/web/w.hash.js.gz +0 -0
  27. {copyparty-1.15.6 → copyparty-1.15.8}/LICENSE +0 -0
  28. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/__init__.py +0 -0
  29. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/bos/__init__.py +0 -0
  30. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/bos/bos.py +0 -0
  31. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/bos/path.py +0 -0
  32. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/broker_mp.py +0 -0
  33. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/broker_mpw.py +0 -0
  34. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/broker_thr.py +0 -0
  35. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/broker_util.py +0 -0
  36. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/cert.py +0 -0
  37. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/cfg.py +0 -0
  38. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/dxml.py +0 -0
  39. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/fsutil.py +0 -0
  40. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/ico.py +0 -0
  41. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/mdns.py +0 -0
  42. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/metrics.py +0 -0
  43. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/mtag.py +0 -0
  44. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/multicast.py +0 -0
  45. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/pwhash.py +0 -0
  46. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/res/COPYING.txt +0 -0
  47. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/res/__init__.py +0 -0
  48. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/res/insecure.pem +0 -0
  49. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/smbd.py +0 -0
  50. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/ssdp.py +0 -0
  51. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/star.py +0 -0
  52. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/__init__.py +0 -0
  53. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/__init__.py +0 -0
  54. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/bimap.py +0 -0
  55. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/bit.py +0 -0
  56. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/buffer.py +0 -0
  57. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/dns.py +0 -0
  58. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/label.py +0 -0
  59. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/lex.py +0 -0
  60. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/dnslib/ranges.py +0 -0
  61. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/ifaddr/__init__.py +0 -0
  62. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/ifaddr/_posix.py +0 -0
  63. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/ifaddr/_shared.py +0 -0
  64. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/ifaddr/_win32.py +0 -0
  65. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/qrcodegen.py +0 -0
  66. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/stolen/surrogateescape.py +0 -0
  67. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/sutil.py +0 -0
  68. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/szip.py +0 -0
  69. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/tcpsrv.py +0 -0
  70. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/tftpd.py +0 -0
  71. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/th_cli.py +0 -0
  72. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/th_srv.py +0 -0
  73. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/u2idx.py +0 -0
  74. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/a/__init__.py +0 -0
  75. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/a/partyfuse.py +0 -0
  76. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/a/webdav-cfg.bat +0 -0
  77. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/browser.css.gz +0 -0
  78. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/browser.html +0 -0
  79. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/browser2.html +0 -0
  80. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/cf.html +0 -0
  81. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dbg-audio.js.gz +0 -0
  82. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dd/2.png +0 -0
  83. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dd/3.png +0 -0
  84. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dd/4.png +0 -0
  85. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dd/5.png +0 -0
  86. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/dd/__init__.py +0 -0
  87. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/__init__.py +0 -0
  88. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/busy.mp3.gz +0 -0
  89. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/easymde.css.gz +0 -0
  90. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/easymde.js.gz +0 -0
  91. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/fuse.py +0 -0
  92. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/marked.js.gz +0 -0
  93. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/mini-fa.css.gz +0 -0
  94. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/mini-fa.woff +0 -0
  95. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/prism.css.gz +0 -0
  96. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/prism.js.gz +0 -0
  97. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/prismd.css.gz +0 -0
  98. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/scp.woff2 +0 -0
  99. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  100. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  101. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/md.css.gz +0 -0
  102. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/md.html +0 -0
  103. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/md.js.gz +0 -0
  104. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/md2.css.gz +0 -0
  105. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/md2.js.gz +0 -0
  106. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/mde.css.gz +0 -0
  107. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/mde.html +0 -0
  108. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/mde.js.gz +0 -0
  109. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/msg.css.gz +0 -0
  110. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/msg.html +0 -0
  111. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/shares.css.gz +0 -0
  112. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/shares.html +0 -0
  113. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/shares.js.gz +0 -0
  114. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/splash.css.gz +0 -0
  115. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/splash.html +0 -0
  116. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/splash.js.gz +0 -0
  117. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/svcs.html +0 -0
  118. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty/web/svcs.js.gz +0 -0
  119. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/SOURCES.txt +0 -0
  120. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/dependency_links.txt +0 -0
  121. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/entry_points.txt +0 -0
  122. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/requires.txt +0 -0
  123. {copyparty-1.15.6 → copyparty-1.15.8}/copyparty.egg-info/top_level.txt +0 -0
  124. {copyparty-1.15.6 → copyparty-1.15.8}/pyproject.toml +0 -0
  125. {copyparty-1.15.6 → copyparty-1.15.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.15.6
3
+ Version: 1.15.8
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
@@ -134,6 +134,7 @@ turn almost any device into a file server with resumable uploads/downloads using
134
134
  * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
135
135
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
136
136
  * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
137
+ * [ip auth](#ip-auth) - autologin based on IP range (CIDR)
137
138
  * [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
138
139
  * [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
139
140
  * [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
@@ -272,7 +273,7 @@ also see [comparison to similar software](./docs/versus.md)
272
273
  * upload
273
274
  * ☑ basic: plain multipart, ie6 support
274
275
  * ☑ [up2k](#uploading): js, resumable, multithreaded
275
- * **no filesize limit!** ...unless you use Cloudflare, then it's 383.9 GiB
276
+ * **no filesize limit!** even on Cloudflare
276
277
  * ☑ stash: simple PUT filedropper
277
278
  * ☑ filename randomizer
278
279
  * ☑ write-only folders
@@ -707,7 +708,7 @@ up2k has several advantages:
707
708
  * uploads resume if you reboot your browser or pc, just upload the same files again
708
709
  * server detects any corruption; the client reuploads affected chunks
709
710
  * the client doesn't upload anything that already exists on the server
710
- * no filesize limit unless imposed by a proxy, for example Cloudflare, which blocks uploads over 383.9 GiB
711
+ * no filesize limit, even when a proxy limits the request size (for example Cloudflare)
711
712
  * much higher speeds than ftp/scp/tarpipe on some internet connections (mainly american ones) thanks to parallel connections
712
713
  * the last-modified timestamp of the file is preserved
713
714
 
@@ -743,6 +744,8 @@ note that since up2k has to read each file twice, `[🎈] bup` can *theoreticall
743
744
 
744
745
  if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button
745
746
 
747
+ if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option `--u2sz` (the default is 96 MiB to support Cloudflare)
748
+
746
749
 
747
750
  ### file-search
748
751
 
@@ -1486,6 +1489,22 @@ redefine behavior with plugins ([examples](./bin/handlers/))
1486
1489
  replace 404 and 403 errors with something completely different (that's it for now)
1487
1490
 
1488
1491
 
1492
+ ## ip auth
1493
+
1494
+ autologin based on IP range (CIDR) , using the global-option `--ipu`
1495
+
1496
+ for example, if everyone with an IP that starts with `192.168.123` should automatically log in as the user `spartacus`, then you can either specify `--ipu=192.168.123.0/24=spartacus` as a commandline option, or put this in a config file:
1497
+
1498
+ ```yaml
1499
+ [global]
1500
+ ipu: 192.168.123.0/24=spartacus
1501
+ ```
1502
+
1503
+ repeat the option to map additional subnets
1504
+
1505
+ **be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`
1506
+
1507
+
1489
1508
  ## identity providers
1490
1509
 
1491
1510
  replace copyparty passwords with oauth and such
@@ -80,6 +80,7 @@ turn almost any device into a file server with resumable uploads/downloads using
80
80
  * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
81
81
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
82
82
  * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
83
+ * [ip auth](#ip-auth) - autologin based on IP range (CIDR)
83
84
  * [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
84
85
  * [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
85
86
  * [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
@@ -218,7 +219,7 @@ also see [comparison to similar software](./docs/versus.md)
218
219
  * upload
219
220
  * ☑ basic: plain multipart, ie6 support
220
221
  * ☑ [up2k](#uploading): js, resumable, multithreaded
221
- * **no filesize limit!** ...unless you use Cloudflare, then it's 383.9 GiB
222
+ * **no filesize limit!** even on Cloudflare
222
223
  * ☑ stash: simple PUT filedropper
223
224
  * ☑ filename randomizer
224
225
  * ☑ write-only folders
@@ -653,7 +654,7 @@ up2k has several advantages:
653
654
  * uploads resume if you reboot your browser or pc, just upload the same files again
654
655
  * server detects any corruption; the client reuploads affected chunks
655
656
  * the client doesn't upload anything that already exists on the server
656
- * no filesize limit unless imposed by a proxy, for example Cloudflare, which blocks uploads over 383.9 GiB
657
+ * no filesize limit, even when a proxy limits the request size (for example Cloudflare)
657
658
  * much higher speeds than ftp/scp/tarpipe on some internet connections (mainly american ones) thanks to parallel connections
658
659
  * the last-modified timestamp of the file is preserved
659
660
 
@@ -689,6 +690,8 @@ note that since up2k has to read each file twice, `[🎈] bup` can *theoreticall
689
690
 
690
691
  if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button
691
692
 
693
+ if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option `--u2sz` (the default is 96 MiB to support Cloudflare)
694
+
692
695
 
693
696
  ### file-search
694
697
 
@@ -1432,6 +1435,22 @@ redefine behavior with plugins ([examples](./bin/handlers/))
1432
1435
  replace 404 and 403 errors with something completely different (that's it for now)
1433
1436
 
1434
1437
 
1438
+ ## ip auth
1439
+
1440
+ autologin based on IP range (CIDR) , using the global-option `--ipu`
1441
+
1442
+ for example, if everyone with an IP that starts with `192.168.123` should automatically log in as the user `spartacus`, then you can either specify `--ipu=192.168.123.0/24=spartacus` as a commandline option, or put this in a config file:
1443
+
1444
+ ```yaml
1445
+ [global]
1446
+ ipu: 192.168.123.0/24=spartacus
1447
+ ```
1448
+
1449
+ repeat the option to map additional subnets
1450
+
1451
+ **be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`
1452
+
1453
+
1435
1454
  ## identity providers
1436
1455
 
1437
1456
  replace copyparty passwords with oauth and such
@@ -1009,7 +1009,7 @@ def add_upload(ap):
1009
1009
  ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="windows-only: minimum size of incoming uploads through up2k before they are made into sparse files")
1010
1010
  ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
1011
1011
  ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good for low-latency (same-country) connections, 4-8 for android clients, 16 for cross-atlantic (max=64)")
1012
- ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for this size. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
1012
+ ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
1013
1013
  ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
1014
1014
  ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory")
1015
1015
 
@@ -1079,6 +1079,7 @@ def add_auth(ap):
1079
1079
  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)")
1080
1080
  ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
1081
1081
  ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
1082
+ ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
1082
1083
 
1083
1084
 
1084
1085
  def add_chpw(ap):
@@ -1469,6 +1470,7 @@ def add_debug(ap):
1469
1470
  ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
1470
1471
  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)")
1471
1472
  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")
1473
+ ap2.add_argument("--bf-log", metavar="PATH", type=u, default="", help="bak-flips: log corruption info to a textfile at \033[33mPATH\033[0m")
1472
1474
 
1473
1475
 
1474
1476
  # fmt: on
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 6)
3
+ VERSION = (1, 15, 8)
4
4
  CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 12)
5
+ BUILD_DT = (2024, 10, 16)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -59,6 +59,7 @@ if PY2:
59
59
  LEELOO_DALLAS = "leeloo_dallas"
60
60
 
61
61
  SEE_LOG = "see log for details"
62
+ SEESLOG = " (see serverlog for details)"
62
63
  SSEELOG = " ({})".format(SEE_LOG)
63
64
  BAD_CFG = "invalid config; {}".format(SEE_LOG)
64
65
  SBADCFG = " ({})".format(BAD_CFG)
@@ -72,6 +72,7 @@ class FtpAuth(DummyAuthorizer):
72
72
  else:
73
73
  raise AuthenticationFailed("banned")
74
74
 
75
+ args = self.hub.args
75
76
  asrv = self.hub.asrv
76
77
  uname = "*"
77
78
  if username != "anonymous":
@@ -82,6 +83,9 @@ class FtpAuth(DummyAuthorizer):
82
83
  uname = zs
83
84
  break
84
85
 
86
+ if args.ipu and uname == "*":
87
+ uname = args.ipu_iu[args.ipu_nm.map(ip)]
88
+
85
89
  if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
86
90
  g = self.hub.gpwd
87
91
  if g.lim:
@@ -584,6 +584,9 @@ class HttpCli(object):
584
584
  or "*"
585
585
  )
586
586
 
587
+ if self.args.ipu and self.uname == "*":
588
+ self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
589
+
587
590
  self.rvol = self.asrv.vfs.aread[self.uname]
588
591
  self.wvol = self.asrv.vfs.awrite[self.uname]
589
592
  self.avol = self.asrv.vfs.aadmin[self.uname]
@@ -1871,7 +1874,7 @@ class HttpCli(object):
1871
1874
  f, fn = ren_open(fn, *open_a, **params)
1872
1875
  try:
1873
1876
  path = os.path.join(fdir, fn)
1874
- post_sz, sha_hex, sha_b64 = hashcopy(reader, f, self.args.s_wr_slp)
1877
+ post_sz, sha_hex, sha_b64 = hashcopy(reader, f, None, 0, self.args.s_wr_slp)
1875
1878
  finally:
1876
1879
  f.close()
1877
1880
 
@@ -2023,13 +2026,32 @@ class HttpCli(object):
2023
2026
  return True
2024
2027
 
2025
2028
  def bakflip(
2026
- self, f , ofs , sz , sha , flags
2029
+ self,
2030
+ f ,
2031
+ ap ,
2032
+ ofs ,
2033
+ sz ,
2034
+ good_sha ,
2035
+ bad_sha ,
2036
+ flags ,
2027
2037
  ) :
2038
+ now = time.time()
2039
+ t = "bad-chunk: %.3f %s %s %d %s %s %s"
2040
+ t = t % (now, bad_sha, good_sha, ofs, self.ip, self.uname, ap)
2041
+ self.log(t, 5)
2042
+
2043
+ if self.args.bf_log:
2044
+ try:
2045
+ with open(self.args.bf_log, "ab+") as f2:
2046
+ f2.write((t + "\n").encode("utf-8", "replace"))
2047
+ except Exception as ex:
2048
+ self.log("append %s failed: %r" % (self.args.bf_log, ex))
2049
+
2028
2050
  if not self.args.bak_flips or self.args.nw:
2029
2051
  return
2030
2052
 
2031
2053
  sdir = self.args.bf_dir
2032
- fp = os.path.join(sdir, sha)
2054
+ fp = os.path.join(sdir, bad_sha)
2033
2055
  if bos.path.exists(fp):
2034
2056
  return self.log("no bakflip; have it", 6)
2035
2057
 
@@ -2315,7 +2337,7 @@ class HttpCli(object):
2315
2337
  broker = self.conn.hsrv.broker
2316
2338
  x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
2317
2339
  response = x.get()
2318
- chashes, chunksize, cstarts, path, lastmod, sprs = response
2340
+ chashes, chunksize, cstarts, path, lastmod, fsize, sprs = response
2319
2341
  maxsize = chunksize * len(chashes)
2320
2342
  cstart0 = cstarts[0]
2321
2343
  locked = chashes # remaining chunks to be received in this request
@@ -2323,6 +2345,50 @@ class HttpCli(object):
2323
2345
  num_left = -1 # num chunks left according to most recent up2k release
2324
2346
  treport = time.time() # ratelimit up2k reporting to reduce overhead
2325
2347
 
2348
+ if "x-up2k-subc" in self.headers:
2349
+ sc_ofs = int(self.headers["x-up2k-subc"])
2350
+ chash = chashes[0]
2351
+
2352
+ u2sc = self.conn.hsrv.u2sc
2353
+ try:
2354
+ sc_pofs, hasher = u2sc[chash]
2355
+ if not sc_ofs:
2356
+ t = "client restarted the chunk; forgetting subchunk offset %d"
2357
+ self.log(t % (sc_pofs,))
2358
+ raise Exception()
2359
+ except:
2360
+ sc_pofs = 0
2361
+ hasher = hashlib.sha512()
2362
+
2363
+ et = "subchunk protocol error; resetting chunk "
2364
+ if sc_pofs != sc_ofs:
2365
+ u2sc.pop(chash, None)
2366
+ t = "%s[%s]: the expected resume-point was %d, not %d"
2367
+ raise Pebkac(400, t % (et, chash, sc_pofs, sc_ofs))
2368
+ if len(cstarts) > 1:
2369
+ u2sc.pop(chash, None)
2370
+ t = "%s[%s]: only a single subchunk can be uploaded in one request; you are sending %d chunks"
2371
+ raise Pebkac(400, t % (et, chash, len(cstarts)))
2372
+ csize = min(chunksize, fsize - cstart0[0])
2373
+ cstart0[0] += sc_ofs # also sets cstarts[0][0]
2374
+ sc_next_ofs = sc_ofs + postsize
2375
+ if sc_next_ofs > csize:
2376
+ u2sc.pop(chash, None)
2377
+ t = "%s[%s]: subchunk offset (%d) plus postsize (%d) exceeds chunksize (%d)"
2378
+ raise Pebkac(400, t % (et, chash, sc_ofs, postsize, csize))
2379
+ else:
2380
+ final_subchunk = sc_next_ofs == csize
2381
+ t = "subchunk %s %d:%d/%d %s"
2382
+ zs = "END" if final_subchunk else ""
2383
+ self.log(t % (chash[:15], sc_ofs, sc_next_ofs, csize, zs), 6)
2384
+ if final_subchunk:
2385
+ u2sc.pop(chash, None)
2386
+ else:
2387
+ u2sc[chash] = (sc_next_ofs, hasher)
2388
+ else:
2389
+ hasher = None
2390
+ final_subchunk = True
2391
+
2326
2392
  try:
2327
2393
  if self.args.nw:
2328
2394
  path = os.devnull
@@ -2353,11 +2419,15 @@ class HttpCli(object):
2353
2419
  reader = read_socket(
2354
2420
  self.sr, self.args.s_rd_sz, min(remains, chunksize)
2355
2421
  )
2356
- post_sz, _, sha_b64 = hashcopy(reader, f, self.args.s_wr_slp)
2422
+ post_sz, _, sha_b64 = hashcopy(
2423
+ reader, f, hasher, 0, self.args.s_wr_slp
2424
+ )
2357
2425
 
2358
- if sha_b64 != chash:
2426
+ if sha_b64 != chash and final_subchunk:
2359
2427
  try:
2360
- self.bakflip(f, cstart[0], post_sz, sha_b64, vfs.flags)
2428
+ self.bakflip(
2429
+ f, path, cstart[0], post_sz, chash, sha_b64, vfs.flags
2430
+ )
2361
2431
  except:
2362
2432
  self.log("bakflip failed: " + min_ex())
2363
2433
 
@@ -2385,7 +2455,8 @@ class HttpCli(object):
2385
2455
 
2386
2456
  # be quick to keep the tcp winsize scale;
2387
2457
  # if we can't confirm rn then that's fine
2388
- written.append(chash)
2458
+ if final_subchunk:
2459
+ written.append(chash)
2389
2460
  now = time.time()
2390
2461
  if now - treport < 1:
2391
2462
  continue
@@ -2773,7 +2844,7 @@ class HttpCli(object):
2773
2844
  tabspath = os.path.join(fdir, tnam)
2774
2845
  self.log("writing to {}".format(tabspath))
2775
2846
  sz, sha_hex, sha_b64 = hashcopy(
2776
- p_data, f, self.args.s_wr_slp, max_sz
2847
+ p_data, f, None, max_sz, self.args.s_wr_slp
2777
2848
  )
2778
2849
  if sz == 0:
2779
2850
  raise Pebkac(400, "empty files in post")
@@ -3103,7 +3174,7 @@ class HttpCli(object):
3103
3174
  wunlink(self.log, fp, vfs.flags)
3104
3175
 
3105
3176
  with open(fsenc(fp), "wb", self.args.iobuf) as f:
3106
- sz, sha512, _ = hashcopy(p_data, f, self.args.s_wr_slp)
3177
+ sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
3107
3178
 
3108
3179
  if lim:
3109
3180
  lim.nup(self.ip)
@@ -56,6 +56,8 @@ class HttpConn(object):
56
56
  self.asrv = hsrv.asrv # mypy404
57
57
  self.u2fh = hsrv.u2fh # mypy404
58
58
  self.pipes = hsrv.pipes # mypy404
59
+ self.ipu_iu = hsrv.ipu_iu
60
+ self.ipu_nm = hsrv.ipu_nm
59
61
  self.ipa_nm = hsrv.ipa_nm
60
62
  self.xff_nm = hsrv.xff_nm
61
63
  self.xff_lan = hsrv.xff_lan # type: ignore
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
+ import hashlib
4
5
  import math
5
6
  import os
6
7
  import re
@@ -69,6 +70,7 @@ from .util import (
69
70
  build_netmap,
70
71
  has_resource,
71
72
  ipnorm,
73
+ load_ipu,
72
74
  load_resource,
73
75
  min_ex,
74
76
  shut_socket,
@@ -140,6 +142,7 @@ class HttpSrv(object):
140
142
  self.t_periodic = None
141
143
 
142
144
  self.u2fh = FHC()
145
+ self.u2sc = {}
143
146
  self.pipes = CachedDict(0.2)
144
147
  self.metrics = Metrics(self)
145
148
  self.nreq = 0
@@ -171,6 +174,11 @@ class HttpSrv(object):
171
174
  self.j2 = {x: env.get_template(x + ".html") for x in jn}
172
175
  self.prism = has_resource(self.E, "web/deps/prism.js.gz")
173
176
 
177
+ if self.args.ipu:
178
+ self.ipu_iu, self.ipu_nm = load_ipu(self.log, self.args.ipu)
179
+ else:
180
+ self.ipu_iu = self.ipu_nm = None
181
+
174
182
  self.ipa_nm = build_netmap(self.args.ipa)
175
183
  self.xff_nm = build_netmap(self.args.xff_src)
176
184
  self.xff_lan = build_netmap("lan")
@@ -54,6 +54,7 @@ from .util import (
54
54
  alltrace,
55
55
  ansi_re,
56
56
  build_netmap,
57
+ load_ipu,
57
58
  min_ex,
58
59
  mp,
59
60
  odfusion,
@@ -215,6 +216,11 @@ class SvcHub(object):
215
216
  noch.update([x for x in zsl if x])
216
217
  args.chpw_no = noch
217
218
 
219
+ if args.ipu:
220
+ iu, nm = load_ipu(self.log, args.ipu)
221
+ setattr(args, "ipu_iu", iu)
222
+ setattr(args, "ipu_nm", nm)
223
+
218
224
  if not self.args.no_ses:
219
225
  self.setup_session_db()
220
226