copyparty 1.8.0__tar.gz → 1.8.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. {copyparty-1.8.0 → copyparty-1.8.1}/PKG-INFO +23 -1
  2. {copyparty-1.8.0 → copyparty-1.8.1}/README.md +22 -0
  3. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/__main__.py +53 -1
  4. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/__version__.py +2 -2
  5. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/authsrv.py +3 -3
  6. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/cfg.py +4 -0
  7. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/httpcli.py +50 -5
  8. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/ico.py +14 -3
  9. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/util.py +28 -0
  10. copyparty-1.8.1/copyparty/web/browser.css.gz +0 -0
  11. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/PKG-INFO +23 -1
  12. copyparty-1.8.0/copyparty/web/browser.css.gz +0 -0
  13. {copyparty-1.8.0 → copyparty-1.8.1}/LICENSE +0 -0
  14. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/__init__.py +0 -0
  15. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/bos/__init__.py +0 -0
  16. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/bos/bos.py +0 -0
  17. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/bos/path.py +0 -0
  18. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/broker_mp.py +0 -0
  19. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/broker_mpw.py +0 -0
  20. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/broker_thr.py +0 -0
  21. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/broker_util.py +0 -0
  22. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/cert.py +0 -0
  23. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/dxml.py +0 -0
  24. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/fsutil.py +0 -0
  25. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/ftpd.py +0 -0
  26. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/httpconn.py +0 -0
  27. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/httpsrv.py +0 -0
  28. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/mdns.py +0 -0
  29. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/mtag.py +0 -0
  30. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/multicast.py +0 -0
  31. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/pwhash.py +0 -0
  32. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/res/COPYING.txt +0 -0
  33. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/res/__init__.py +0 -0
  34. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/res/insecure.pem +0 -0
  35. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/smbd.py +0 -0
  36. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/ssdp.py +0 -0
  37. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/star.py +0 -0
  38. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/__init__.py +0 -0
  39. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/__init__.py +0 -0
  40. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/bimap.py +0 -0
  41. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/bit.py +0 -0
  42. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/buffer.py +0 -0
  43. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/dns.py +0 -0
  44. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/label.py +0 -0
  45. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/lex.py +0 -0
  46. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/dnslib/ranges.py +0 -0
  47. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/ifaddr/__init__.py +0 -0
  48. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/ifaddr/_posix.py +0 -0
  49. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/ifaddr/_shared.py +0 -0
  50. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/ifaddr/_win32.py +0 -0
  51. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/qrcodegen.py +0 -0
  52. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/stolen/surrogateescape.py +0 -0
  53. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/sutil.py +0 -0
  54. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/svchub.py +0 -0
  55. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/szip.py +0 -0
  56. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/tcpsrv.py +0 -0
  57. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/th_cli.py +0 -0
  58. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/th_srv.py +0 -0
  59. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/u2idx.py +0 -0
  60. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/up2k.py +0 -0
  61. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/a/__init__.py +0 -0
  62. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/a/partyfuse.py +0 -0
  63. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/a/u2c.py +0 -0
  64. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/a/webdav-cfg.bat +0 -0
  65. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/baguettebox.js.gz +0 -0
  66. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/browser.html +0 -0
  67. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/browser.js.gz +0 -0
  68. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/browser2.html +0 -0
  69. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/cf.html +0 -0
  70. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dbg-audio.js.gz +0 -0
  71. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dd/2.png +0 -0
  72. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dd/3.png +0 -0
  73. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dd/4.png +0 -0
  74. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dd/5.png +0 -0
  75. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/dd/__init__.py +0 -0
  76. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/__init__.py +0 -0
  77. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/easymde.css.gz +0 -0
  78. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/easymde.js.gz +0 -0
  79. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/marked.js.gz +0 -0
  80. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/mini-fa.css.gz +0 -0
  81. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/mini-fa.woff +0 -0
  82. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/prism.css.gz +0 -0
  83. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/prism.js.gz +0 -0
  84. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/prismd.css.gz +0 -0
  85. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/scp.woff2 +0 -0
  86. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  87. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  88. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/md.css.gz +0 -0
  89. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/md.html +0 -0
  90. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/md.js.gz +0 -0
  91. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/md2.css.gz +0 -0
  92. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/md2.js.gz +0 -0
  93. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/mde.css.gz +0 -0
  94. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/mde.html +0 -0
  95. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/mde.js.gz +0 -0
  96. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/msg.css.gz +0 -0
  97. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/msg.html +0 -0
  98. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/splash.css.gz +0 -0
  99. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/splash.html +0 -0
  100. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/splash.js.gz +0 -0
  101. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/svcs.html +0 -0
  102. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/svcs.js.gz +0 -0
  103. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/ui.css.gz +0 -0
  104. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/up2k.js.gz +0 -0
  105. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/util.js.gz +0 -0
  106. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty/web/w.hash.js.gz +0 -0
  107. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/SOURCES.txt +0 -0
  108. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/dependency_links.txt +0 -0
  109. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/entry_points.txt +0 -0
  110. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/requires.txt +0 -0
  111. {copyparty-1.8.0 → copyparty-1.8.1}/copyparty.egg-info/top_level.txt +0 -0
  112. {copyparty-1.8.0 → copyparty-1.8.1}/pyproject.toml +0 -0
  113. {copyparty-1.8.0 → copyparty-1.8.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.8.0
3
+ Version: 1.8.1
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -109,12 +109,14 @@ turn almost any device into a file server with resumable uploads/downloads using
109
109
  * [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
110
110
  * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
111
111
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
112
+ * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
112
113
  * [hiding from google](#hiding-from-google) - tell search engines you dont wanna be indexed
113
114
  * [themes](#themes)
114
115
  * [complete examples](#complete-examples)
115
116
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
116
117
  * [packages](#packages) - the party might be closer than you think
117
118
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
119
+ * [fedora package](#fedora-package) - now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/)
118
120
  * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
119
121
  * [nixos module](#nixos-module)
120
122
  * [browser support](#browser-support) - TLDR: yes
@@ -1169,6 +1171,13 @@ note that this is way more complicated than the new [event hooks](#event-hooks)
1169
1171
  note that it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1`
1170
1172
 
1171
1173
 
1174
+ ## handlers
1175
+
1176
+ redefine behavior with plugins ([examples](./bin/handlers/))
1177
+
1178
+ replace 404 and 403 errors with something completely different (that's it for now)
1179
+
1180
+
1172
1181
  ## hiding from google
1173
1182
 
1174
1183
  tell search engines you dont wanna be indexed, either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:
@@ -1271,6 +1280,19 @@ the party might be closer than you think
1271
1280
  now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
1272
1281
 
1273
1282
 
1283
+ ## fedora package
1284
+
1285
+ now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/) , maintained autonomously -- [track record](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/package/python-copyparty/) seems OK
1286
+
1287
+ ```bash
1288
+ dnf copr enable @copr/PyPI
1289
+ dnf install python3-copyparty # just a minimal install, or...
1290
+ dnf install python3-{copyparty,pillow,argon2-cffi,pyftpdlib,pyOpenSSL} ffmpeg-free # with recommended deps
1291
+ ```
1292
+
1293
+ this *may* also work on RHEL but [I'm not paying IBM to verify that](https://www.jeffgeerling.com/blog/2023/dear-red-hat-are-you-dumb)
1294
+
1295
+
1274
1296
  ## nix package
1275
1297
 
1276
1298
  `nix profile install github:9001/copyparty`
@@ -66,12 +66,14 @@ turn almost any device into a file server with resumable uploads/downloads using
66
66
  * [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
67
67
  * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
68
68
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
69
+ * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
69
70
  * [hiding from google](#hiding-from-google) - tell search engines you dont wanna be indexed
70
71
  * [themes](#themes)
71
72
  * [complete examples](#complete-examples)
72
73
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
73
74
  * [packages](#packages) - the party might be closer than you think
74
75
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
76
+ * [fedora package](#fedora-package) - now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/)
75
77
  * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
76
78
  * [nixos module](#nixos-module)
77
79
  * [browser support](#browser-support) - TLDR: yes
@@ -1126,6 +1128,13 @@ note that this is way more complicated than the new [event hooks](#event-hooks)
1126
1128
  note that it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1`
1127
1129
 
1128
1130
 
1131
+ ## handlers
1132
+
1133
+ redefine behavior with plugins ([examples](./bin/handlers/))
1134
+
1135
+ replace 404 and 403 errors with something completely different (that's it for now)
1136
+
1137
+
1129
1138
  ## hiding from google
1130
1139
 
1131
1140
  tell search engines you dont wanna be indexed, either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:
@@ -1228,6 +1237,19 @@ the party might be closer than you think
1228
1237
  now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
1229
1238
 
1230
1239
 
1240
+ ## fedora package
1241
+
1242
+ now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/) , maintained autonomously -- [track record](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/package/python-copyparty/) seems OK
1243
+
1244
+ ```bash
1245
+ dnf copr enable @copr/PyPI
1246
+ dnf install python3-copyparty # just a minimal install, or...
1247
+ dnf install python3-{copyparty,pillow,argon2-cffi,pyftpdlib,pyOpenSSL} ffmpeg-free # with recommended deps
1248
+ ```
1249
+
1250
+ this *may* also work on RHEL but [I'm not paying IBM to verify that](https://www.jeffgeerling.com/blog/2023/dear-red-hat-are-you-dumb)
1251
+
1252
+
1231
1253
  ## nix package
1232
1254
 
1233
1255
  `nix profile install github:9001/copyparty`
@@ -522,6 +522,50 @@ def get_sects():
522
522
  ).rstrip()
523
523
  + build_flags_desc(),
524
524
  ],
525
+ [
526
+ "handlers",
527
+ "use plugins to handle certain events",
528
+ dedent(
529
+ """
530
+ usually copyparty returns a \033[33m404\033[0m if a file does not exist, and
531
+ \033[33m403\033[0m if a user tries to access a file they don't have access to
532
+
533
+ you can load a plugin which will be invoked right before this
534
+ happens, and the plugin can choose to override this behavior
535
+
536
+ load the plugin using --args or volflags; for example \033[36m
537
+ --on404 ~/partyhandlers/not404.py
538
+ -v .::r:c,on404=~/partyhandlers/not404.py
539
+ \033[0m
540
+ the file must define the function \033[35mmain(cli,vn,rem)\033[0m:
541
+ \033[35mcli\033[0m: the copyparty HttpCli instance
542
+ \033[35mvn\033[0m: the VFS which overlaps with the requested URL
543
+ \033[35mrem\033[0m: the remainder of the URL below the VFS mountpoint
544
+
545
+ `main` must return a string; one of the following:
546
+
547
+ > \033[32m"true"\033[0m: the plugin has responded to the request,
548
+ and the TCP connection should be kept open
549
+
550
+ > \033[32m"false"\033[0m: the plugin has responded to the request,
551
+ and the TCP connection should be terminated
552
+
553
+ > \033[32m"retry"\033[0m: the plugin has done something to resolve the 404
554
+ situation, and copyparty should reattempt reading the file.
555
+ if it still fails, a regular 404 will be returned
556
+
557
+ > \033[32m"allow"\033[0m: should ignore the insufficient permissions
558
+ and let the client continue anyways
559
+
560
+ > \033[32m""\033[0m: the plugin has not handled the request;
561
+ try the next plugin or return the usual 404 or 403
562
+
563
+ \033[1;35mPS!\033[0m the folder that contains the python file should ideally
564
+ not contain many other python files, and especially nothing
565
+ with filenames that overlap with modules used by copyparty
566
+ """
567
+ ),
568
+ ],
525
569
  [
526
570
  "hooks",
527
571
  "execute commands before/after various events",
@@ -852,6 +896,13 @@ def add_smb(ap):
852
896
  ap2.add_argument("--smbvvv", action="store_true", help="verbosest")
853
897
 
854
898
 
899
+ def add_handlers(ap):
900
+ ap2 = ap.add_argument_group('handlers (see --help-handlers)')
901
+ ap2.add_argument("--on404", metavar="PY", type=u, action="append", help="handle 404s by executing PY file")
902
+ ap2.add_argument("--on403", metavar="PY", type=u, action="append", help="handle 403s by executing PY file")
903
+ ap2.add_argument("--hot-handlers", action="store_true", help="reload handlers on each request -- expensive but convenient when hacking on stuff")
904
+
905
+
855
906
  def add_hooks(ap):
856
907
  ap2 = ap.add_argument_group('event hooks (see --help-hooks)')
857
908
  ap2.add_argument("--xbu", metavar="CMD", type=u, action="append", help="execute CMD before a file upload starts")
@@ -1107,6 +1158,7 @@ def run_argparse(
1107
1158
  add_optouts(ap)
1108
1159
  add_shutdown(ap)
1109
1160
  add_yolo(ap)
1161
+ add_handlers(ap)
1110
1162
  add_hooks(ap)
1111
1163
  add_ui(ap, retry)
1112
1164
  add_admin(ap)
@@ -1254,7 +1306,7 @@ def main(argv = None) :
1254
1306
  elif not al.no_ansi:
1255
1307
  al.ansi = VT100
1256
1308
 
1257
- if WINDOWS and not al.keep_qem:
1309
+ if WINDOWS and not al.keep_qem and not al.ah_cli:
1258
1310
  try:
1259
1311
  disable_quickedit()
1260
1312
  except:
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 8, 0)
3
+ VERSION = (1, 8, 1)
4
4
  CODENAME = "argon"
5
- BUILD_DT = (2023, 6, 26)
5
+ BUILD_DT = (2023, 7, 7)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -1040,7 +1040,7 @@ class AuthSrv(object):
1040
1040
  flags[name] = True
1041
1041
  return
1042
1042
 
1043
- if name not in "mtp xbu xau xiu xbr xar xbd xad xm".split():
1043
+ if name not in "mtp xbu xau xiu xbr xar xbd xad xm on404 on403".split():
1044
1044
  if value is True:
1045
1045
  t = "└─add volflag [{}] = {} ({})"
1046
1046
  else:
@@ -1439,7 +1439,7 @@ class AuthSrv(object):
1439
1439
 
1440
1440
  # append additive args from argv to volflags
1441
1441
  hooks = "xbu xau xiu xbr xar xbd xad xm".split()
1442
- for name in ["mtp"] + hooks:
1442
+ for name in "mtp on404 on403".split() + hooks:
1443
1443
  self._read_volflag(vol.flags, name, getattr(self.args, name), True)
1444
1444
 
1445
1445
  for hn in hooks:
@@ -1828,7 +1828,7 @@ class AuthSrv(object):
1828
1828
  ]
1829
1829
 
1830
1830
  csv = set("i p".split())
1831
- lst = set("c ihead mtm mtp xad xar xau xiu xbd xbr xbu xm".split())
1831
+ lst = set("c ihead mtm mtp xad xar xau xiu xbd xbr xbu xm on404 on403".split())
1832
1832
  askip = set("a v c vc cgen theme".split())
1833
1833
 
1834
1834
  # keymap from argv to vflag
@@ -125,6 +125,10 @@ flagcats = {
125
125
  "dathumb": "disables audio thumbnails (spectrograms)",
126
126
  "dithumb": "disables image thumbnails",
127
127
  },
128
+ "handlers\n(better explained in --help-handlers)": {
129
+ "on404=PY": "handle 404s by executing PY file",
130
+ "on403=PY": "handle 403s by executing PY file",
131
+ },
128
132
  "event hooks\n(better explained in --help-hooks)": {
129
133
  "xbu=CMD": "execute CMD before a file upload starts",
130
134
  "xau=CMD": "execute CMD after a file upload finishes",
@@ -58,6 +58,7 @@ from .util import (
58
58
  html_escape,
59
59
  humansize,
60
60
  ipnorm,
61
+ loadpy,
61
62
  min_ex,
62
63
  quotep,
63
64
  rand_name,
@@ -763,11 +764,27 @@ class HttpCli(object):
763
764
  return True
764
765
 
765
766
  if not self.can_read and not self.can_write and not self.can_get:
766
- if self.vpath:
767
- self.log("inaccessible: [{}]".format(self.vpath))
768
- return self.tx_404(True)
767
+ t = "@{} has no access to [{}]"
768
+ self.log(t.format(self.uname, self.vpath))
769
+
770
+ if self.avn and "on403" in self.avn.flags:
771
+ vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
772
+ ret = self.on40x(vn.flags["on403"], vn, rem)
773
+ if ret == "true":
774
+ return True
775
+ elif ret == "false":
776
+ return False
777
+ elif ret == "allow":
778
+ self.log("plugin override; access permitted")
779
+ self.can_read = self.can_write = self.can_move = True
780
+ self.can_delete = self.can_get = self.can_upget = True
781
+ else:
782
+ return self.tx_404(True)
783
+ else:
784
+ if self.vpath:
785
+ return self.tx_404(True)
769
786
 
770
- self.uparam["h"] = ""
787
+ self.uparam["h"] = ""
771
788
 
772
789
  if "tree" in self.uparam:
773
790
  return self.tx_tree()
@@ -3052,6 +3069,20 @@ class HttpCli(object):
3052
3069
  self.reply(html.encode("utf-8"), status=rc)
3053
3070
  return True
3054
3071
 
3072
+ def on40x(self, mods , vn , rem ) :
3073
+ for mpath in mods:
3074
+ try:
3075
+ mod = loadpy(mpath, self.args.hot_handlers)
3076
+ except Exception as ex:
3077
+ self.log("import failed: {!r}".format(ex))
3078
+ continue
3079
+
3080
+ ret = mod.main(self, vn, rem)
3081
+ if ret:
3082
+ return ret.lower()
3083
+
3084
+ return "" # unhandled / fallthrough
3085
+
3055
3086
  def scanvol(self) :
3056
3087
  if not self.can_read or not self.can_write:
3057
3088
  raise Pebkac(403, "not allowed for user " + self.uname)
@@ -3369,7 +3400,21 @@ class HttpCli(object):
3369
3400
  try:
3370
3401
  st = bos.stat(abspath)
3371
3402
  except:
3372
- return self.tx_404()
3403
+ if "on404" not in vn.flags:
3404
+ return self.tx_404()
3405
+
3406
+ ret = self.on40x(vn.flags["on404"], vn, rem)
3407
+ if ret == "true":
3408
+ return True
3409
+ elif ret == "false":
3410
+ return False
3411
+ elif ret == "retry":
3412
+ try:
3413
+ st = bos.stat(abspath)
3414
+ except:
3415
+ return self.tx_404()
3416
+ else:
3417
+ return self.tx_404()
3373
3418
 
3374
3419
  if rem.startswith(".hist/up2k.") or (
3375
3420
  rem.endswith("/dir.txt") and rem.startswith(".hist/th/")
@@ -35,7 +35,7 @@ class Ico(object):
35
35
  h = int(100 / (float(sw) / float(sh)))
36
36
  w = 100
37
37
 
38
- if chrome and as_thumb:
38
+ if chrome:
39
39
  # cannot handle more than ~2000 unique SVGs
40
40
  if HAVE_PIL:
41
41
  # svg: 3s, cache: 6s, this: 8s
@@ -45,8 +45,19 @@ class Ico(object):
45
45
  w = 64
46
46
  img = Image.new("RGB", (w, h), "#" + c[:6])
47
47
  pb = ImageDraw.Draw(img)
48
- tw, th = pb.textsize(ext)
49
- pb.text(((w - tw) // 2, (h - th) // 2), ext, fill="#" + c[6:])
48
+ try:
49
+ _, _, tw, th = pb.textbbox((0, 0), ext)
50
+ except:
51
+ tw, th = pb.textsize(ext)
52
+
53
+ tw += len(ext)
54
+ cw = tw // len(ext)
55
+ x = ((w - tw) // 2) - (cw * 2) // 3
56
+ fill = "#" + c[6:]
57
+ for ch in ext:
58
+ pb.text((x, (h - th) // 2), " %s " % (ch,), fill=fill)
59
+ x += cw
60
+
50
61
  img = img.resize((w * 3, h * 3), Image.NEAREST)
51
62
 
52
63
  buf = BytesIO()
@@ -2711,6 +2711,34 @@ def runhook(
2711
2711
  return True
2712
2712
 
2713
2713
 
2714
+ def loadpy(ap , hot ) :
2715
+ """
2716
+ a nice can of worms capable of causing all sorts of bugs
2717
+ depending on what other inconveniently named files happen
2718
+ to be in the same folder
2719
+ """
2720
+ if ap.startswith("~"):
2721
+ ap = os.path.expanduser(ap)
2722
+
2723
+ mdir, mfile = os.path.split(absreal(ap))
2724
+ mname = mfile.rsplit(".", 1)[0]
2725
+ sys.path.insert(0, mdir)
2726
+
2727
+ if PY2:
2728
+ mod = __import__(mname)
2729
+ if hot:
2730
+ reload(mod)
2731
+ else:
2732
+ import importlib
2733
+
2734
+ mod = importlib.import_module(mname)
2735
+ if hot:
2736
+ importlib.reload(mod)
2737
+
2738
+ sys.path.remove(mdir)
2739
+ return mod
2740
+
2741
+
2714
2742
  def gzip_orig_sz(fn ) :
2715
2743
  with open(fsenc(fn), "rb") as f:
2716
2744
  f.seek(-4, 2)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.8.0
3
+ Version: 1.8.1
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -109,12 +109,14 @@ turn almost any device into a file server with resumable uploads/downloads using
109
109
  * [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
110
110
  * [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
111
111
  * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
112
+ * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
112
113
  * [hiding from google](#hiding-from-google) - tell search engines you dont wanna be indexed
113
114
  * [themes](#themes)
114
115
  * [complete examples](#complete-examples)
115
116
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
116
117
  * [packages](#packages) - the party might be closer than you think
117
118
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
119
+ * [fedora package](#fedora-package) - now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/)
118
120
  * [nix package](#nix-package) - `nix profile install github:9001/copyparty`
119
121
  * [nixos module](#nixos-module)
120
122
  * [browser support](#browser-support) - TLDR: yes
@@ -1169,6 +1171,13 @@ note that this is way more complicated than the new [event hooks](#event-hooks)
1169
1171
  note that it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1`
1170
1172
 
1171
1173
 
1174
+ ## handlers
1175
+
1176
+ redefine behavior with plugins ([examples](./bin/handlers/))
1177
+
1178
+ replace 404 and 403 errors with something completely different (that's it for now)
1179
+
1180
+
1172
1181
  ## hiding from google
1173
1182
 
1174
1183
  tell search engines you dont wanna be indexed, either using the good old [robots.txt](https://www.robotstxt.org/robotstxt.html) or through copyparty settings:
@@ -1271,6 +1280,19 @@ the party might be closer than you think
1271
1280
  now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
1272
1281
 
1273
1282
 
1283
+ ## fedora package
1284
+
1285
+ now [available on copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/) , maintained autonomously -- [track record](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/package/python-copyparty/) seems OK
1286
+
1287
+ ```bash
1288
+ dnf copr enable @copr/PyPI
1289
+ dnf install python3-copyparty # just a minimal install, or...
1290
+ dnf install python3-{copyparty,pillow,argon2-cffi,pyftpdlib,pyOpenSSL} ffmpeg-free # with recommended deps
1291
+ ```
1292
+
1293
+ this *may* also work on RHEL but [I'm not paying IBM to verify that](https://www.jeffgeerling.com/blog/2023/dear-red-hat-are-you-dumb)
1294
+
1295
+
1274
1296
  ## nix package
1275
1297
 
1276
1298
  `nix profile install github:9001/copyparty`
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes