copyparty 1.6.8__tar.gz → 1.6.10__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 (116) hide show
  1. {copyparty-1.6.8/copyparty.egg-info → copyparty-1.6.10}/PKG-INFO +63 -23
  2. {copyparty-1.6.8 → copyparty-1.6.10}/README.md +62 -22
  3. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/__main__.py +2 -1
  4. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/__version__.py +2 -2
  5. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/ftpd.py +1 -0
  6. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/httpcli.py +55 -23
  7. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/th_srv.py +33 -1
  8. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/u2idx.py +8 -7
  9. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/up2k.py +5 -2
  10. copyparty-1.6.10/copyparty/web/browser.css.gz +0 -0
  11. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/browser.html +1 -0
  12. copyparty-1.6.10/copyparty/web/browser.js.gz +0 -0
  13. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/svcs.html +3 -3
  14. copyparty-1.6.10/copyparty/web/util.js.gz +0 -0
  15. {copyparty-1.6.8 → copyparty-1.6.10/copyparty.egg-info}/PKG-INFO +63 -23
  16. copyparty-1.6.8/copyparty/web/browser.css.gz +0 -0
  17. copyparty-1.6.8/copyparty/web/browser.js.gz +0 -0
  18. copyparty-1.6.8/copyparty/web/util.js.gz +0 -0
  19. {copyparty-1.6.8 → copyparty-1.6.10}/LICENSE +0 -0
  20. {copyparty-1.6.8 → copyparty-1.6.10}/MANIFEST.in +0 -0
  21. {copyparty-1.6.8 → copyparty-1.6.10}/bin/partyfuse.py +0 -0
  22. {copyparty-1.6.8 → copyparty-1.6.10}/bin/up2k.py +0 -0
  23. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/__init__.py +0 -0
  24. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/authsrv.py +0 -0
  25. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/bos/__init__.py +0 -0
  26. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/bos/bos.py +0 -0
  27. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/bos/path.py +0 -0
  28. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/broker_mp.py +0 -0
  29. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/broker_mpw.py +0 -0
  30. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/broker_thr.py +0 -0
  31. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/broker_util.py +0 -0
  32. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/cfg.py +0 -0
  33. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/dxml.py +0 -0
  34. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/fsutil.py +0 -0
  35. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/httpconn.py +0 -0
  36. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/httpsrv.py +0 -0
  37. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/ico.py +0 -0
  38. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/mdns.py +0 -0
  39. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/mtag.py +0 -0
  40. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/multicast.py +0 -0
  41. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/res/COPYING.txt +0 -0
  42. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/res/insecure.pem +0 -0
  43. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/smbd.py +0 -0
  44. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/ssdp.py +0 -0
  45. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/star.py +0 -0
  46. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/__init__.py +0 -0
  47. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/__init__.py +0 -0
  48. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/bimap.py +0 -0
  49. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/bit.py +0 -0
  50. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/buffer.py +0 -0
  51. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/dns.py +0 -0
  52. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/label.py +0 -0
  53. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/lex.py +0 -0
  54. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/dnslib/ranges.py +0 -0
  55. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/ifaddr/__init__.py +0 -0
  56. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/ifaddr/_posix.py +0 -0
  57. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/ifaddr/_shared.py +0 -0
  58. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/ifaddr/_win32.py +0 -0
  59. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/qrcodegen.py +0 -0
  60. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/stolen/surrogateescape.py +0 -0
  61. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/sutil.py +0 -0
  62. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/svchub.py +0 -0
  63. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/szip.py +0 -0
  64. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/tcpsrv.py +0 -0
  65. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/th_cli.py +0 -0
  66. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/util.py +0 -0
  67. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/a/__init__.py +0 -0
  68. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/a/partyfuse.py +0 -0
  69. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/a/up2k.py +0 -0
  70. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/a/webdav-cfg.bat +0 -0
  71. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/baguettebox.js.gz +0 -0
  72. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/browser2.html +0 -0
  73. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/cf.html +0 -0
  74. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/copyparty.gif +0 -0
  75. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dbg-audio.js.gz +0 -0
  76. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dd/2.png +0 -0
  77. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dd/3.png +0 -0
  78. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dd/4.png +0 -0
  79. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dd/5.png +0 -0
  80. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/dd/__init__.py +0 -0
  81. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/__init__.py +0 -0
  82. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/easymde.css.gz +0 -0
  83. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/easymde.js.gz +0 -0
  84. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/marked.js.gz +0 -0
  85. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/mini-fa.css.gz +0 -0
  86. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/mini-fa.woff +0 -0
  87. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/prism.css.gz +0 -0
  88. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/prism.js.gz +0 -0
  89. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/prismd.css.gz +0 -0
  90. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/scp.woff2 +0 -0
  91. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  92. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  93. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/md.css.gz +0 -0
  94. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/md.html +0 -0
  95. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/md.js.gz +0 -0
  96. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/md2.css.gz +0 -0
  97. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/md2.js.gz +0 -0
  98. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/mde.css.gz +0 -0
  99. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/mde.html +0 -0
  100. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/mde.js.gz +0 -0
  101. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/msg.css.gz +0 -0
  102. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/msg.html +0 -0
  103. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/splash.css.gz +0 -0
  104. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/splash.html +0 -0
  105. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/splash.js.gz +0 -0
  106. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/svcs.js.gz +0 -0
  107. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/ui.css.gz +0 -0
  108. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/up2k.js.gz +0 -0
  109. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty/web/w.hash.js.gz +0 -0
  110. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty.egg-info/SOURCES.txt +0 -0
  111. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty.egg-info/dependency_links.txt +0 -0
  112. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty.egg-info/entry_points.txt +0 -0
  113. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty.egg-info/requires.txt +0 -0
  114. {copyparty-1.6.8 → copyparty-1.6.10}/copyparty.egg-info/top_level.txt +0 -0
  115. {copyparty-1.6.8 → copyparty-1.6.10}/setup.cfg +0 -0
  116. {copyparty-1.6.8 → copyparty-1.6.10}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.6.8
3
+ Version: 1.6.10
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
  Home-page: https://github.com/9001/copyparty
6
6
  Author: ed
@@ -40,30 +40,17 @@ License-File: LICENSE
40
40
 
41
41
  # 💾🎉 copyparty
42
42
 
43
- * portable file sharing hub (py2/py3) [(on PyPI)](https://pypi.org/project/copyparty/)
44
- * MIT-Licensed, 2019-05-26, ed @ irc.rizon.net
43
+ turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
45
44
 
45
+ * server only needs Python (2 or 3), all dependencies optional
46
+ * 🔌 protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
47
+ * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
46
48
 
47
- ## summary
48
-
49
- turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
50
-
51
- * server only needs Python (`2.7` or `3.3+`), all dependencies optional
52
- * browse/upload with [IE4](#browser-support) / netscape4.0 on win3.11 (heh)
53
- * protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
54
-
55
- try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
49
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
56
50
 
57
51
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
58
52
 
59
53
 
60
- ## get the app
61
-
62
- <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
63
-
64
- (the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet)
65
-
66
-
67
54
  ## readme toc
68
55
 
69
56
  * top
@@ -122,12 +109,16 @@ try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running fro
122
109
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
123
110
  * [browser support](#browser-support) - TLDR: yes
124
111
  * [client examples](#client-examples) - interact with copyparty using non-browser clients
112
+ * [folder sync](#folder-sync) - sync folders to/from copyparty
125
113
  * [mount as drive](#mount-as-drive) - a remote copyparty server as a local filesystem
114
+ * [android app](#android-app) - upload to copyparty with one tap
115
+ * [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
126
116
  * [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
127
117
  * [client-side](#client-side) - when uploading files
128
118
  * [security](#security) - some notes on hardening
129
119
  * [gotchas](#gotchas) - behavior that might be unexpected
130
120
  * [cors](#cors) - cross-site request config
121
+ * [https](#https) - both HTTP and HTTPS are accepted
131
122
  * [recovering from crashes](#recovering-from-crashes)
132
123
  * [client crashes](#client-crashes)
133
124
  * [frefox wsod](#frefox-wsod) - firefox 87 can crash during uploads
@@ -148,6 +139,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
148
139
 
149
140
  * or install through pypi (python3 only): `python3 -m pip install --user -U copyparty`
150
141
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
142
+ * or if you are on android, [install copyparty in termux](#install-on-android)
151
143
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
152
144
  * docker has all deps built-in, so skip this step:
153
145
 
@@ -205,11 +197,15 @@ firewall-cmd --reload
205
197
  * ☑ [smb/cifs server](#smb-server)
206
198
  * ☑ [qr-code](#qr-code) for quick access
207
199
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
200
+ * ☑ [event hooks](#event-hooks) / script runner
201
+ * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
208
202
  * upload
209
203
  * ☑ basic: plain multipart, ie6 support
210
204
  * ☑ [up2k](#uploading): js, resumable, multithreaded
211
205
  * unaffected by cloudflare's max-upload-size (100 MiB)
212
206
  * ☑ stash: simple PUT filedropper
207
+ * ☑ filename randomizer
208
+ * ☑ write-only folders
213
209
  * ☑ [unpost](#unpost): undo/delete accidental uploads
214
210
  * ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
215
211
  * ☑ symlink/discard existing files (content-matching)
@@ -233,10 +229,15 @@ firewall-cmd --reload
233
229
  * ☑ [locate files by contents](#file-search)
234
230
  * ☑ search by name/path/date/size
235
231
  * ☑ [search by ID3-tags etc.](#searching)
232
+ * client support
233
+ * ☑ [folder sync](#folder-sync)
234
+ * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
236
235
  * markdown
237
236
  * ☑ [viewer](#markdown-viewer)
238
237
  * ☑ editor (sure why not)
239
238
 
239
+ PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
240
+
240
241
 
241
242
  ## testimonials
242
243
 
@@ -308,7 +309,7 @@ server notes:
308
309
 
309
310
  * iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
310
311
  * *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
311
- * "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
312
+ * "future" because `AudioContext` can't maintain a stable playback speed in the current iOS version (15.7), maybe one day...
312
313
 
313
314
  * Windows: folders cannot be accessed if the name ends with `.`
314
315
  * python or windows bug
@@ -1132,13 +1133,15 @@ see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where
1132
1133
 
1133
1134
  ## reverse-proxy
1134
1135
 
1135
- running copyparty next to other websites hosted on an existing webserver such as nginx or apache
1136
+ running copyparty next to other websites hosted on an existing webserver such as nginx, caddy, or apache
1136
1137
 
1137
1138
  you can either:
1138
1139
  * give copyparty its own domain or subdomain (recommended)
1139
1140
  * or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
1140
1141
  * if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
1141
1142
 
1143
+ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which could be a nice speed boost
1144
+
1142
1145
  example webserver configs:
1143
1146
 
1144
1147
  * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
@@ -1216,7 +1219,7 @@ interact with copyparty using non-browser clients
1216
1219
  * `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
1217
1220
 
1218
1221
  * python: [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
1219
- * file uploads, file-search, folder sync, autoresume of aborted/broken uploads
1222
+ * file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
1220
1223
  * can be downloaded from copyparty: controlpanel -> connect -> [up2k.py](http://127.0.0.1:3923/.cpr/a/up2k.py)
1221
1224
  * see [./bin/README.md#up2kpy](bin/README.md#up2kpy)
1222
1225
 
@@ -1237,6 +1240,15 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
1237
1240
  NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
1238
1241
 
1239
1242
 
1243
+ ## folder sync
1244
+
1245
+ sync folders to/from copyparty
1246
+
1247
+ the commandline uploader [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
1248
+
1249
+ alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty for instance), although syncing to copyparty is about 5x slower than up2k.py if you have many small files in particular
1250
+
1251
+
1240
1252
  ## mount as drive
1241
1253
 
1242
1254
  a remote copyparty server as a local filesystem; go to the control-panel and click `connect` to see a list of commands to do that
@@ -1255,6 +1267,27 @@ alternatively, some alternatives roughly sorted by speed (unreproducible benchma
1255
1267
  most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead
1256
1268
 
1257
1269
 
1270
+ # android app
1271
+
1272
+ upload to copyparty with one tap
1273
+
1274
+ <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
1275
+
1276
+ the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet
1277
+
1278
+ if you want to run the copyparty server on your android device, see [install on android](#install-on-android)
1279
+
1280
+
1281
+ # iOS shortcuts
1282
+
1283
+ there is no iPhone app, but the following shortcuts are almost as good:
1284
+
1285
+ * [upload to copyparty](https://www.icloud.com/shortcuts/41e98dd985cb4d3bb433222bc1e9e770) ([offline](https://github.com/9001/copyparty/raw/hovudstraum/contrib/ios/upload-to-copyparty.shortcut)) ([png](https://user-images.githubusercontent.com/241032/226118053-78623554-b0ed-482e-98e4-6d57ada58ea4.png)) based on the [original](https://www.icloud.com/shortcuts/ab415d5b4de3467b9ce6f151b439a5d7) by [Daedren](https://github.com/Daedren) (thx!)
1286
+ * can strip exif, upload files, pics, vids, links, clipboard
1287
+ * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
1288
+ * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
1289
+
1290
+
1258
1291
  # performance
1259
1292
 
1260
1293
  defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
@@ -1350,6 +1383,13 @@ by default, except for `GET` and `HEAD` operations, all requests must either:
1350
1383
  cors can be configured with `--acao` and `--acam`, or the protections entirely disabled with `--allow-csrf`
1351
1384
 
1352
1385
 
1386
+ ## https
1387
+
1388
+ both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)
1389
+
1390
+ copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
1391
+
1392
+
1353
1393
  # recovering from crashes
1354
1394
 
1355
1395
  ## client crashes
@@ -1417,7 +1457,7 @@ these are standalone programs and will never be imported / evaluated by copypart
1417
1457
 
1418
1458
  the self-contained "binary" [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
1419
1459
 
1420
- you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](#./docs/devnotes.md#sfx-repack)
1460
+ you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
1421
1461
 
1422
1462
 
1423
1463
  ## copyparty.exe
@@ -1,29 +1,16 @@
1
1
  # 💾🎉 copyparty
2
2
 
3
- * portable file sharing hub (py2/py3) [(on PyPI)](https://pypi.org/project/copyparty/)
4
- * MIT-Licensed, 2019-05-26, ed @ irc.rizon.net
3
+ turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
5
4
 
5
+ * server only needs Python (2 or 3), all dependencies optional
6
+ * 🔌 protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
7
+ * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
6
8
 
7
- ## summary
8
-
9
- turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
10
-
11
- * server only needs Python (`2.7` or `3.3+`), all dependencies optional
12
- * browse/upload with [IE4](#browser-support) / netscape4.0 on win3.11 (heh)
13
- * protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
14
-
15
- try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
9
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
16
10
 
17
11
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
18
12
 
19
13
 
20
- ## get the app
21
-
22
- <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
23
-
24
- (the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet)
25
-
26
-
27
14
  ## readme toc
28
15
 
29
16
  * top
@@ -82,12 +69,16 @@ try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running fro
82
69
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
83
70
  * [browser support](#browser-support) - TLDR: yes
84
71
  * [client examples](#client-examples) - interact with copyparty using non-browser clients
72
+ * [folder sync](#folder-sync) - sync folders to/from copyparty
85
73
  * [mount as drive](#mount-as-drive) - a remote copyparty server as a local filesystem
74
+ * [android app](#android-app) - upload to copyparty with one tap
75
+ * [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
86
76
  * [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
87
77
  * [client-side](#client-side) - when uploading files
88
78
  * [security](#security) - some notes on hardening
89
79
  * [gotchas](#gotchas) - behavior that might be unexpected
90
80
  * [cors](#cors) - cross-site request config
81
+ * [https](#https) - both HTTP and HTTPS are accepted
91
82
  * [recovering from crashes](#recovering-from-crashes)
92
83
  * [client crashes](#client-crashes)
93
84
  * [frefox wsod](#frefox-wsod) - firefox 87 can crash during uploads
@@ -108,6 +99,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
108
99
 
109
100
  * or install through pypi (python3 only): `python3 -m pip install --user -U copyparty`
110
101
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
102
+ * or if you are on android, [install copyparty in termux](#install-on-android)
111
103
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
112
104
  * docker has all deps built-in, so skip this step:
113
105
 
@@ -165,11 +157,15 @@ firewall-cmd --reload
165
157
  * ☑ [smb/cifs server](#smb-server)
166
158
  * ☑ [qr-code](#qr-code) for quick access
167
159
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
160
+ * ☑ [event hooks](#event-hooks) / script runner
161
+ * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
168
162
  * upload
169
163
  * ☑ basic: plain multipart, ie6 support
170
164
  * ☑ [up2k](#uploading): js, resumable, multithreaded
171
165
  * unaffected by cloudflare's max-upload-size (100 MiB)
172
166
  * ☑ stash: simple PUT filedropper
167
+ * ☑ filename randomizer
168
+ * ☑ write-only folders
173
169
  * ☑ [unpost](#unpost): undo/delete accidental uploads
174
170
  * ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
175
171
  * ☑ symlink/discard existing files (content-matching)
@@ -193,10 +189,15 @@ firewall-cmd --reload
193
189
  * ☑ [locate files by contents](#file-search)
194
190
  * ☑ search by name/path/date/size
195
191
  * ☑ [search by ID3-tags etc.](#searching)
192
+ * client support
193
+ * ☑ [folder sync](#folder-sync)
194
+ * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
196
195
  * markdown
197
196
  * ☑ [viewer](#markdown-viewer)
198
197
  * ☑ editor (sure why not)
199
198
 
199
+ PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
200
+
200
201
 
201
202
  ## testimonials
202
203
 
@@ -268,7 +269,7 @@ server notes:
268
269
 
269
270
  * iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
270
271
  * *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
271
- * "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
272
+ * "future" because `AudioContext` can't maintain a stable playback speed in the current iOS version (15.7), maybe one day...
272
273
 
273
274
  * Windows: folders cannot be accessed if the name ends with `.`
274
275
  * python or windows bug
@@ -1092,13 +1093,15 @@ see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where
1092
1093
 
1093
1094
  ## reverse-proxy
1094
1095
 
1095
- running copyparty next to other websites hosted on an existing webserver such as nginx or apache
1096
+ running copyparty next to other websites hosted on an existing webserver such as nginx, caddy, or apache
1096
1097
 
1097
1098
  you can either:
1098
1099
  * give copyparty its own domain or subdomain (recommended)
1099
1100
  * or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
1100
1101
  * if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
1101
1102
 
1103
+ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which could be a nice speed boost
1104
+
1102
1105
  example webserver configs:
1103
1106
 
1104
1107
  * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
@@ -1176,7 +1179,7 @@ interact with copyparty using non-browser clients
1176
1179
  * `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
1177
1180
 
1178
1181
  * python: [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
1179
- * file uploads, file-search, folder sync, autoresume of aborted/broken uploads
1182
+ * file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
1180
1183
  * can be downloaded from copyparty: controlpanel -> connect -> [up2k.py](http://127.0.0.1:3923/.cpr/a/up2k.py)
1181
1184
  * see [./bin/README.md#up2kpy](bin/README.md#up2kpy)
1182
1185
 
@@ -1197,6 +1200,15 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
1197
1200
  NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
1198
1201
 
1199
1202
 
1203
+ ## folder sync
1204
+
1205
+ sync folders to/from copyparty
1206
+
1207
+ the commandline uploader [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
1208
+
1209
+ alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty for instance), although syncing to copyparty is about 5x slower than up2k.py if you have many small files in particular
1210
+
1211
+
1200
1212
  ## mount as drive
1201
1213
 
1202
1214
  a remote copyparty server as a local filesystem; go to the control-panel and click `connect` to see a list of commands to do that
@@ -1215,6 +1227,27 @@ alternatively, some alternatives roughly sorted by speed (unreproducible benchma
1215
1227
  most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead
1216
1228
 
1217
1229
 
1230
+ # android app
1231
+
1232
+ upload to copyparty with one tap
1233
+
1234
+ <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
1235
+
1236
+ the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet
1237
+
1238
+ if you want to run the copyparty server on your android device, see [install on android](#install-on-android)
1239
+
1240
+
1241
+ # iOS shortcuts
1242
+
1243
+ there is no iPhone app, but the following shortcuts are almost as good:
1244
+
1245
+ * [upload to copyparty](https://www.icloud.com/shortcuts/41e98dd985cb4d3bb433222bc1e9e770) ([offline](https://github.com/9001/copyparty/raw/hovudstraum/contrib/ios/upload-to-copyparty.shortcut)) ([png](https://user-images.githubusercontent.com/241032/226118053-78623554-b0ed-482e-98e4-6d57ada58ea4.png)) based on the [original](https://www.icloud.com/shortcuts/ab415d5b4de3467b9ce6f151b439a5d7) by [Daedren](https://github.com/Daedren) (thx!)
1246
+ * can strip exif, upload files, pics, vids, links, clipboard
1247
+ * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
1248
+ * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
1249
+
1250
+
1218
1251
  # performance
1219
1252
 
1220
1253
  defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
@@ -1310,6 +1343,13 @@ by default, except for `GET` and `HEAD` operations, all requests must either:
1310
1343
  cors can be configured with `--acao` and `--acam`, or the protections entirely disabled with `--allow-csrf`
1311
1344
 
1312
1345
 
1346
+ ## https
1347
+
1348
+ both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)
1349
+
1350
+ copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
1351
+
1352
+
1313
1353
  # recovering from crashes
1314
1354
 
1315
1355
  ## client crashes
@@ -1377,7 +1417,7 @@ these are standalone programs and will never be imported / evaluated by copypart
1377
1417
 
1378
1418
  the self-contained "binary" [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
1379
1419
 
1380
- you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](#./docs/devnotes.md#sfx-repack)
1420
+ you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
1381
1421
 
1382
1422
 
1383
1423
  ## copyparty.exe
@@ -752,7 +752,7 @@ def add_ftp(ap):
752
752
 
753
753
  def add_webdav(ap):
754
754
  ap2 = ap.add_argument_group('WebDAV options')
755
- ap2.add_argument("--daw", action="store_true", help="enable full write support. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
755
+ ap2.add_argument("--daw", action="store_true", help="enable full write support, even if client may not be webdav. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
756
756
  ap2.add_argument("--dav-inf", action="store_true", help="allow depth:infinite requests (recursive file listing); extremely server-heavy but required for spec compliance -- luckily few clients rely on this")
757
757
  ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
758
758
 
@@ -940,6 +940,7 @@ def add_ui(ap, retry):
940
940
  ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
941
941
  ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
942
942
  ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
943
+ ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI)")
943
944
  ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
944
945
  ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
945
946
  ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 6, 8)
3
+ VERSION = (1, 6, 10)
4
4
  CODENAME = "cors k"
5
- BUILD_DT = (2023, 3, 12)
5
+ BUILD_DT = (2023, 3, 20)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -124,6 +124,7 @@ class FtpFs(AbstractedFS):
124
124
 
125
125
  def die(self, msg):
126
126
  self.h.die(msg)
127
+ raise Exception()
127
128
 
128
129
  def v2a(
129
130
  self,
@@ -774,8 +774,8 @@ class HttpCli(object):
774
774
  if "k304" in self.uparam:
775
775
  return self.set_k304()
776
776
 
777
- if "am_js" in self.uparam:
778
- return self.set_am_js()
777
+ if "setck" in self.uparam:
778
+ return self.setck()
779
779
 
780
780
  if "reset" in self.uparam:
781
781
  return self.set_cfg_reset()
@@ -861,7 +861,17 @@ class HttpCli(object):
861
861
  vn, rem = self.asrv.vfs.get(self.vpath, self.uname, True, False, err=401)
862
862
  depth = self.headers.get("depth", "infinity").lower()
863
863
 
864
- if depth == "infinity":
864
+ try:
865
+ topdir = {"vp": "", "st": bos.stat(vn.canonical(rem))}
866
+ except OSError as ex:
867
+ if ex.errno != errno.ENOENT:
868
+ raise
869
+ raise Pebkac(404)
870
+
871
+ if not stat.S_ISDIR(topdir["st"].st_mode):
872
+ fgen = []
873
+
874
+ elif depth == "infinity":
865
875
  if not self.args.dav_inf:
866
876
  self.log("client wants --dav-inf", 3)
867
877
  zb = b'<?xml version="1.0" encoding="utf-8"?>\n<D:error xmlns:D="DAV:"><D:propfind-finite-depth/></D:error>'
@@ -900,13 +910,6 @@ class HttpCli(object):
900
910
  t2 = " or 'infinity'" if self.args.dav_inf else ""
901
911
  raise Pebkac(412, t.format(depth, t2))
902
912
 
903
- try:
904
- topdir = {"vp": "", "st": os.stat(vn.canonical(rem))}
905
- except OSError as ex:
906
- if ex.errno != errno.ENOENT:
907
- raise
908
- raise Pebkac(404)
909
-
910
913
  fgen = itertools.chain([topdir], fgen) # type: ignore
911
914
  vtop = vjoin(self.args.R, vjoin(vn.vpath, rem))
912
915
 
@@ -1422,9 +1425,9 @@ class HttpCli(object):
1422
1425
  self.log(t, 1)
1423
1426
  raise Pebkac(403, t)
1424
1427
 
1425
- if is_put and not (self.args.no_dav or self.args.nw):
1428
+ if is_put and not (self.args.no_dav or self.args.nw) and bos.path.exists(path):
1426
1429
  # allow overwrite if...
1427
- # * volflag 'daw' is set
1430
+ # * volflag 'daw' is set, or client is definitely webdav
1428
1431
  # * and account has delete-access
1429
1432
  # or...
1430
1433
  # * file exists, is empty, sufficiently new
@@ -1434,9 +1437,11 @@ class HttpCli(object):
1434
1437
  if self.args.dotpart:
1435
1438
  tnam = "." + tnam
1436
1439
 
1437
- if (vfs.flags.get("daw") and self.can_delete) or (
1440
+ if (
1441
+ self.can_delete
1442
+ and (vfs.flags.get("daw") or "x-oc-mtime" in self.headers)
1443
+ ) or (
1438
1444
  not bos.path.exists(os.path.join(fdir, tnam))
1439
- and bos.path.exists(path)
1440
1445
  and not bos.path.getsize(path)
1441
1446
  and bos.path.getmtime(path) >= time.time() - self.args.blank_wt
1442
1447
  ):
@@ -1460,6 +1465,16 @@ class HttpCli(object):
1460
1465
  if self.args.nw:
1461
1466
  return post_sz, sha_hex, sha_b64, remains, path, ""
1462
1467
 
1468
+ at = mt = time.time() - lifetime
1469
+ cli_mt = self.headers.get("x-oc-mtime")
1470
+ if cli_mt:
1471
+ try:
1472
+ mt = int(cli_mt)
1473
+ times = (int(time.time()), mt)
1474
+ bos.utime(path, times, False)
1475
+ except:
1476
+ pass
1477
+
1463
1478
  if nameless and "magic" in vfs.flags:
1464
1479
  try:
1465
1480
  ext = self.conn.hsrv.magician.ext(path)
@@ -1482,7 +1497,6 @@ class HttpCli(object):
1482
1497
  fn = fn2
1483
1498
  path = path2
1484
1499
 
1485
- at = time.time() - lifetime
1486
1500
  if xau and not runhook(
1487
1501
  self.log,
1488
1502
  xau,
@@ -1490,7 +1504,7 @@ class HttpCli(object):
1490
1504
  self.vpath,
1491
1505
  self.host,
1492
1506
  self.uname,
1493
- at,
1507
+ mt,
1494
1508
  post_sz,
1495
1509
  self.ip,
1496
1510
  at,
@@ -1753,12 +1767,13 @@ class HttpCli(object):
1753
1767
  hits = idx.fsearch(vols, body)
1754
1768
  msg = repr(hits)
1755
1769
  taglist = []
1770
+ trunc = False
1756
1771
  else:
1757
1772
  # search by query params
1758
1773
  q = body["q"]
1759
1774
  n = body.get("n", self.args.srch_hits)
1760
1775
  self.log("qj: {} |{}|".format(q, n))
1761
- hits, taglist = idx.search(vols, q, n)
1776
+ hits, taglist, trunc = idx.search(vols, q, n)
1762
1777
  msg = len(hits)
1763
1778
 
1764
1779
  idx.p_end = time.time()
@@ -1778,7 +1793,8 @@ class HttpCli(object):
1778
1793
  for hit in hits:
1779
1794
  hit["rp"] = self.args.RS + hit["rp"]
1780
1795
 
1781
- r = json.dumps({"hits": hits, "tag_order": order}).encode("utf-8")
1796
+ rj = {"hits": hits, "tag_order": order, "trunc": trunc}
1797
+ r = json.dumps(rj).encode("utf-8")
1782
1798
  self.reply(r, mime="application/json")
1783
1799
  return True
1784
1800
 
@@ -2892,15 +2908,16 @@ class HttpCli(object):
2892
2908
  self.redirect("", "?h#cc")
2893
2909
  return True
2894
2910
 
2895
- def set_am_js(self) :
2896
- v = "n" if self.uparam["am_js"] == "n" else "y"
2897
- ck = gencookie("js", v, self.args.R, False, 86400 * 299)
2911
+ def setck(self) :
2912
+ k, v = self.uparam["setck"].split("=", 1)
2913
+ t = None if v == "" else 86400 * 299
2914
+ ck = gencookie(k, v, self.args.R, False, t)
2898
2915
  self.out_headerlist.append(("Set-Cookie", ck))
2899
- self.reply(b"promoted\n")
2916
+ self.reply(b"o7\n")
2900
2917
  return True
2901
2918
 
2902
2919
  def set_cfg_reset(self) :
2903
- for k in ("k304", "js", "cppwd", "cppws"):
2920
+ for k in ("k304", "js", "idxh", "cppwd", "cppws"):
2904
2921
  cookie = gencookie(k, "x", self.args.R, False, None)
2905
2922
  self.out_headerlist.append(("Set-Cookie", cookie))
2906
2923
 
@@ -3429,6 +3446,7 @@ class HttpCli(object):
3429
3446
  "dtheme": self.args.theme,
3430
3447
  "themes": self.args.themes,
3431
3448
  "turbolvl": self.args.turbo,
3449
+ "idxh": int(self.args.ih),
3432
3450
  "u2sort": self.args.u2sort,
3433
3451
  }
3434
3452
 
@@ -3562,6 +3580,20 @@ class HttpCli(object):
3562
3580
  files.append(item)
3563
3581
  item["rd"] = rem
3564
3582
 
3583
+ if (
3584
+ self.cookies.get("idxh") == "y"
3585
+ and "ls" not in self.uparam
3586
+ and "v" not in self.uparam
3587
+ ):
3588
+ idx_html = set(["index.htm", "index.html"])
3589
+ for item in files:
3590
+ if item["name"] in idx_html:
3591
+ # do full resolve in case of shadowed file
3592
+ vp = vjoin(self.vpath.split("?")[0], item["name"])
3593
+ vn, rem = self.asrv.vfs.get(vp, self.uname, True, False)
3594
+ ap = vn.canonical(rem)
3595
+ return self.tx_file(ap) # is no-cache
3596
+
3565
3597
  tagset = set()
3566
3598
  for fe in files:
3567
3599
  fn = fe["name"]