copyparty 1.18.6__tar.gz → 1.18.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 (126) hide show
  1. {copyparty-1.18.6 → copyparty-1.18.8}/PKG-INFO +23 -1
  2. {copyparty-1.18.6 → copyparty-1.18.8}/README.md +22 -0
  3. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/__main__.py +5 -3
  4. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/__version__.py +2 -2
  5. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/authsrv.py +37 -2
  6. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/bos/bos.py +14 -3
  7. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/cfg.py +4 -0
  8. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/ftpd.py +4 -4
  9. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/httpcli.py +71 -53
  10. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/smbd.py +1 -1
  11. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/svchub.py +3 -3
  12. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/tftpd.py +6 -3
  13. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/th_srv.py +2 -2
  14. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/up2k.py +10 -7
  15. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/util.py +60 -22
  16. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/browser.html +2 -2
  17. copyparty-1.18.8/copyparty/web/browser.js.gz +0 -0
  18. copyparty-1.18.8/copyparty/web/shares.js.gz +0 -0
  19. copyparty-1.18.8/copyparty/web/splash.js.gz +0 -0
  20. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/svcs.html +15 -3
  21. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/PKG-INFO +23 -1
  22. copyparty-1.18.6/copyparty/web/browser.js.gz +0 -0
  23. copyparty-1.18.6/copyparty/web/shares.js.gz +0 -0
  24. copyparty-1.18.6/copyparty/web/splash.js.gz +0 -0
  25. {copyparty-1.18.6 → copyparty-1.18.8}/LICENSE +0 -0
  26. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/__init__.py +0 -0
  27. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/bos/__init__.py +0 -0
  28. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/bos/path.py +0 -0
  29. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/broker_mp.py +0 -0
  30. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/broker_mpw.py +0 -0
  31. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/broker_thr.py +0 -0
  32. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/broker_util.py +0 -0
  33. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/cert.py +0 -0
  34. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/dxml.py +0 -0
  35. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/fsutil.py +0 -0
  36. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/httpconn.py +0 -0
  37. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/httpsrv.py +0 -0
  38. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/ico.py +0 -0
  39. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/mdns.py +0 -0
  40. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/metrics.py +0 -0
  41. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/mtag.py +0 -0
  42. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/multicast.py +0 -0
  43. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/pwhash.py +0 -0
  44. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/res/COPYING.txt +0 -0
  45. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/res/__init__.py +0 -0
  46. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/res/insecure.pem +0 -0
  47. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/ssdp.py +0 -0
  48. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/star.py +0 -0
  49. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/__init__.py +0 -0
  50. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/__init__.py +0 -0
  51. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/bimap.py +0 -0
  52. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/bit.py +0 -0
  53. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/buffer.py +0 -0
  54. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/dns.py +0 -0
  55. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/label.py +0 -0
  56. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/lex.py +0 -0
  57. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/dnslib/ranges.py +0 -0
  58. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/ifaddr/__init__.py +0 -0
  59. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/ifaddr/_posix.py +0 -0
  60. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/ifaddr/_shared.py +0 -0
  61. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/ifaddr/_win32.py +0 -0
  62. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/qrcodegen.py +0 -0
  63. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/stolen/surrogateescape.py +0 -0
  64. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/sutil.py +0 -0
  65. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/szip.py +0 -0
  66. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/tcpsrv.py +0 -0
  67. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/th_cli.py +0 -0
  68. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/u2idx.py +0 -0
  69. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/a/__init__.py +0 -0
  70. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/a/partyfuse.py +0 -0
  71. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/a/u2c.py +0 -0
  72. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/a/webdav-cfg.bat +0 -0
  73. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/baguettebox.js.gz +0 -0
  74. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/browser.css.gz +0 -0
  75. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/browser2.html +0 -0
  76. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/cf.html +0 -0
  77. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dbg-audio.js.gz +0 -0
  78. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dd/2.png +0 -0
  79. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dd/3.png +0 -0
  80. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dd/4.png +0 -0
  81. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dd/5.png +0 -0
  82. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/dd/__init__.py +0 -0
  83. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/__init__.py +0 -0
  84. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/busy.mp3.gz +0 -0
  85. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/easymde.css.gz +0 -0
  86. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/easymde.js.gz +0 -0
  87. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/fuse.py +0 -0
  88. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/marked.js.gz +0 -0
  89. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/mini-fa.css.gz +0 -0
  90. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/mini-fa.woff +0 -0
  91. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/prism.css.gz +0 -0
  92. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/prism.js.gz +0 -0
  93. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/prismd.css.gz +0 -0
  94. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/scp.woff2 +0 -0
  95. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  96. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  97. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/idp.html +0 -0
  98. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/md.css.gz +0 -0
  99. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/md.html +0 -0
  100. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/md.js.gz +0 -0
  101. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/md2.css.gz +0 -0
  102. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/md2.js.gz +0 -0
  103. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/mde.css.gz +0 -0
  104. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/mde.html +0 -0
  105. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/mde.js.gz +0 -0
  106. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/msg.css.gz +0 -0
  107. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/msg.html +0 -0
  108. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/rups.css.gz +0 -0
  109. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/rups.html +0 -0
  110. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/rups.js.gz +0 -0
  111. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/shares.css.gz +0 -0
  112. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/shares.html +0 -0
  113. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/splash.css.gz +0 -0
  114. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/splash.html +0 -0
  115. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/svcs.js.gz +0 -0
  116. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/ui.css.gz +0 -0
  117. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/up2k.js.gz +0 -0
  118. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/util.js.gz +0 -0
  119. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty/web/w.hash.js.gz +0 -0
  120. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/SOURCES.txt +0 -0
  121. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/dependency_links.txt +0 -0
  122. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/entry_points.txt +0 -0
  123. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/requires.txt +0 -0
  124. {copyparty-1.18.6 → copyparty-1.18.8}/copyparty.egg-info/top_level.txt +0 -0
  125. {copyparty-1.18.6 → copyparty-1.18.8}/pyproject.toml +0 -0
  126. {copyparty-1.18.6 → copyparty-1.18.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.18.6
3
+ Version: 1.18.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
@@ -138,6 +138,7 @@ made in Norway 🇳🇴
138
138
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
139
139
  * [upload rules](#upload-rules) - set upload rules using volflags
140
140
  * [compress uploads](#compress-uploads) - files can be autocompressed on upload
141
+ * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
141
142
  * [other flags](#other-flags)
142
143
  * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
143
144
  * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
@@ -1707,6 +1708,26 @@ some examples,
1707
1708
  allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
1708
1709
 
1709
1710
 
1711
+ ## chmod and chown
1712
+
1713
+ per-volume filesystem-permissions and ownership
1714
+
1715
+ by default:
1716
+ * all folders are chmod 755
1717
+ * files are usually chmod 644 (umask-defined)
1718
+ * user/group is whatever copyparty is running as
1719
+
1720
+ this can be configured per-volume:
1721
+ * volflag `chmod_f` sets file permissions; default=`644` (usually)
1722
+ * volflag `chmod_d` sets directory permissions; default=`755`
1723
+ * volflag `uid` sets the owner user-id
1724
+ * volflag `gid` sets the owner group-id
1725
+
1726
+ notes:
1727
+ * `gid` can only be set to one of the groups which the copyparty process is a member of
1728
+ * `uid` can only be set if copyparty is running as root (i appreciate your faith)
1729
+
1730
+
1710
1731
  ## other flags
1711
1732
 
1712
1733
  * `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
@@ -2284,6 +2305,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
2284
2305
  | env-var | what it does |
2285
2306
  | ------------------------ | ------------ |
2286
2307
  | `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
2308
+ | `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
2287
2309
 
2288
2310
 
2289
2311
  # packages
@@ -80,6 +80,7 @@ made in Norway 🇳🇴
80
80
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
81
81
  * [upload rules](#upload-rules) - set upload rules using volflags
82
82
  * [compress uploads](#compress-uploads) - files can be autocompressed on upload
83
+ * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
83
84
  * [other flags](#other-flags)
84
85
  * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
85
86
  * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
@@ -1649,6 +1650,26 @@ some examples,
1649
1650
  allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
1650
1651
 
1651
1652
 
1653
+ ## chmod and chown
1654
+
1655
+ per-volume filesystem-permissions and ownership
1656
+
1657
+ by default:
1658
+ * all folders are chmod 755
1659
+ * files are usually chmod 644 (umask-defined)
1660
+ * user/group is whatever copyparty is running as
1661
+
1662
+ this can be configured per-volume:
1663
+ * volflag `chmod_f` sets file permissions; default=`644` (usually)
1664
+ * volflag `chmod_d` sets directory permissions; default=`755`
1665
+ * volflag `uid` sets the owner user-id
1666
+ * volflag `gid` sets the owner group-id
1667
+
1668
+ notes:
1669
+ * `gid` can only be set to one of the groups which the copyparty process is a member of
1670
+ * `uid` can only be set if copyparty is running as root (i appreciate your faith)
1671
+
1672
+
1652
1673
  ## other flags
1653
1674
 
1654
1675
  * `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
@@ -2226,6 +2247,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
2226
2247
  | env-var | what it does |
2227
2248
  | ------------------------ | ------------ |
2228
2249
  | `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
2250
+ | `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
2229
2251
 
2230
2252
 
2231
2253
  # packages
@@ -53,13 +53,13 @@ from .util import (
53
53
  PYFTPD_VER,
54
54
  RAM_AVAIL,
55
55
  RAM_TOTAL,
56
+ RE_ANSI,
56
57
  SQLITE_VER,
57
58
  UNPLICATIONS,
58
59
  URL_BUG,
59
60
  URL_PRJ,
60
61
  Daemon,
61
62
  align_tab,
62
- ansi_re,
63
63
  b64enc,
64
64
  dedent,
65
65
  has_resource,
@@ -161,7 +161,7 @@ def lprint(*a , **ka ) :
161
161
  txt = " ".join(unicode(x) for x in a) + eol
162
162
  printed.append(txt)
163
163
  if not VT100:
164
- txt = ansi_re.sub("", txt)
164
+ txt = RE_ANSI.sub("", txt)
165
165
 
166
166
  print(txt, end="", **ka)
167
167
 
@@ -1045,6 +1045,8 @@ def add_upload(ap):
1045
1045
  ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
1046
1046
  ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
1047
1047
  ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
1048
+ ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
1049
+ ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
1048
1050
  ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
1049
1051
  ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
1050
1052
  ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
@@ -1547,7 +1549,7 @@ def add_ui(ap, retry):
1547
1549
  ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
1548
1550
  ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
1549
1551
  ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
1550
- ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1552
+ ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1551
1553
  ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
1552
1554
  ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
1553
1555
  ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 18, 6)
3
+ VERSION = (1, 18, 8)
4
4
  CODENAME = "logtail"
5
- BUILD_DT = (2025, 7, 28)
5
+ BUILD_DT = (2025, 7, 31)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -33,6 +33,7 @@ from .util import (
33
33
  afsenc,
34
34
  get_df,
35
35
  humansize,
36
+ json_hesc,
36
37
  min_ex,
37
38
  odfusion,
38
39
  read_utf8,
@@ -63,6 +64,25 @@ if PY2:
63
64
 
64
65
 
65
66
  LEELOO_DALLAS = "leeloo_dallas"
67
+ ##
68
+ ## you might be curious what Leeloo Dallas is doing here, so let me explain:
69
+ ##
70
+ ## certain daemonic tasks, namely:
71
+ ## * deletion of expired files, running on a timer
72
+ ## * deletion of sidecar files, initiated by plugins
73
+ ## need to skip the usual permission-checks to do their thing,
74
+ ## so we let Leeloo handle these
75
+ ##
76
+ ## and also, the smb-server has really shitty support for user-accounts
77
+ ## so one popular way to avoid issues is by running copyparty without users;
78
+ ## this makes all smb-clients identify as LD to gain unrestricted access
79
+ ##
80
+ ## Leeloo, being a fictional character from The Fifth Element,
81
+ ## obviously does not exist and will never be able to access any copyparty
82
+ ## instances from the outside (the username is rejected at every entrypoint)
83
+ ##
84
+ ## thanks for coming to my ted talk
85
+
66
86
 
67
87
  SEE_LOG = "see log for details"
68
88
  SEESLOG = " (see serverlog for details)"
@@ -114,6 +134,8 @@ class Lim(object):
114
134
  self.reg = None # up2k registry
115
135
 
116
136
  self.chmod_d = 0o755
137
+ self.uid = self.gid = -1
138
+ self.chown = False
117
139
 
118
140
  self.nups = {} # num tracker
119
141
  self.bups = {} # byte tracker list
@@ -276,6 +298,8 @@ class Lim(object):
276
298
  # no branches yet; make one
277
299
  sub = os.path.join(path, "0")
278
300
  bos.mkdir(sub, self.chmod_d)
301
+ if self.chown:
302
+ os.chown(sub, self.uid, self.gid)
279
303
  else:
280
304
  # try newest branch only
281
305
  sub = os.path.join(path, str(dirs[-1]))
@@ -291,6 +315,8 @@ class Lim(object):
291
315
  # make a branch
292
316
  sub = os.path.join(path, str(dirs[-1] + 1))
293
317
  bos.mkdir(sub, self.chmod_d)
318
+ if self.chown:
319
+ os.chown(sub, self.uid, self.gid)
294
320
  ret = self.dive(sub, lvs - 1)
295
321
  if ret is None:
296
322
  raise Pebkac(500, "rotation bug")
@@ -2153,7 +2179,7 @@ class AuthSrv(object):
2153
2179
  if vf not in vol.flags:
2154
2180
  vol.flags[vf] = getattr(self.args, ga)
2155
2181
 
2156
- zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who"
2182
+ zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
2157
2183
  for k in zs.split():
2158
2184
  if k in vol.flags:
2159
2185
  vol.flags[k] = int(vol.flags[k])
@@ -2190,8 +2216,17 @@ class AuthSrv(object):
2190
2216
  if (is_d and zi != 0o755) or not is_d:
2191
2217
  free_umask = True
2192
2218
 
2219
+ vol.flags.pop("chown", None)
2220
+ if vol.flags["uid"] != -1 or vol.flags["gid"] != -1:
2221
+ vol.flags["chown"] = True
2222
+ vol.flags.pop("fperms", None)
2223
+ if "chown" in vol.flags or vol.flags.get("chmod_f"):
2224
+ vol.flags["fperms"] = True
2193
2225
  if vol.lim:
2194
2226
  vol.lim.chmod_d = vol.flags["chmod_d"]
2227
+ vol.lim.chown = "chown" in vol.flags
2228
+ vol.lim.uid = vol.flags["uid"]
2229
+ vol.lim.gid = vol.flags["gid"]
2195
2230
 
2196
2231
  if vol.flags.get("og"):
2197
2232
  self.args.uqe = True
@@ -2742,7 +2777,7 @@ class AuthSrv(object):
2742
2777
  "lifetime": vn.js_ls["lifetime"],
2743
2778
  "u2sort": self.args.u2sort,
2744
2779
  }
2745
- vn.js_htm = json.dumps(js_htm)
2780
+ vn.js_htm = json_hesc(json.dumps(js_htm))
2746
2781
 
2747
2782
  vols = list(vfs.all_nodes.values())
2748
2783
  if enshare:
@@ -6,8 +6,11 @@ import os
6
6
  from ..util import SYMTIME, fsdec, fsenc
7
7
  from . import path as path
8
8
 
9
- _ = (path,)
10
- __all__ = ["path"]
9
+ MKD_755 = {"chmod_d": 0o755}
10
+ MKD_700 = {"chmod_d": 0o700}
11
+
12
+ _ = (path, MKD_755, MKD_700)
13
+ __all__ = ["path", "MKD_755", "MKD_700"]
11
14
 
12
15
  # grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
13
16
  # printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
@@ -17,11 +20,15 @@ def chmod(p , mode ) :
17
20
  return os.chmod(fsenc(p), mode)
18
21
 
19
22
 
23
+ def chown(p , uid , gid ) :
24
+ return os.chown(fsenc(p), uid, gid)
25
+
26
+
20
27
  def listdir(p = ".") :
21
28
  return [fsdec(x) for x in os.listdir(fsenc(p))]
22
29
 
23
30
 
24
- def makedirs(name , mode = 0o755, exist_ok = True) :
31
+ def makedirs(name , vf = MKD_755, exist_ok = True) :
25
32
  # os.makedirs does 777 for all but leaf; this does mode on all
26
33
  todo = []
27
34
  bname = fsenc(name)
@@ -34,9 +41,13 @@ def makedirs(name , mode = 0o755, exist_ok = True) :
34
41
  if not exist_ok:
35
42
  os.mkdir(bname) # to throw
36
43
  return False
44
+ mode = vf["chmod_d"]
45
+ chown = "chown" in vf
37
46
  for zb in todo[::-1]:
38
47
  try:
39
48
  os.mkdir(zb, mode)
49
+ if chown:
50
+ os.chown(zb, vf["uid"], vf["gid"])
40
51
  except:
41
52
  if os.path.isdir(zb):
42
53
  continue
@@ -114,6 +114,8 @@ def vf_vmap() :
114
114
  "unlist",
115
115
  "u2abort",
116
116
  "u2ts",
117
+ "uid",
118
+ "gid",
117
119
  "ups_who",
118
120
  "zip_who",
119
121
  "zipmaxn",
@@ -175,6 +177,8 @@ flagcats = {
175
177
  "nodupe": "rejects existing files (instead of linking/cloning them)",
176
178
  "chmod_d=755": "unix-permission for new dirs/folders",
177
179
  "chmod_f=644": "unix-permission for new files",
180
+ "uid=573": "change owner of new files/folders to unix-user 573",
181
+ "gid=999": "change owner of new files/folders to unix-group 999",
178
182
  "sparse": "force use of sparse files, mainly for s3-backed storage",
179
183
  "nosparse": "deny use of sparse files, mainly for slow storage",
180
184
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
@@ -31,6 +31,7 @@ from .util import (
31
31
  relchk,
32
32
  runhook,
33
33
  sanitize_fn,
34
+ set_fperms,
34
35
  vjoin,
35
36
  wunlink,
36
37
  )
@@ -258,8 +259,8 @@ class FtpFs(AbstractedFS):
258
259
  wunlink(self.log, ap, VF_CAREFUL)
259
260
 
260
261
  ret = open(fsenc(ap), mode, self.args.iobuf)
261
- if w and "chmod_f" in vfs.flags:
262
- os.fchmod(ret.fileno(), vfs.flags["chmod_f"])
262
+ if w and "fperms" in vfs.flags:
263
+ set_fperms(ret, vfs.flags)
263
264
 
264
265
  return ret
265
266
 
@@ -293,8 +294,7 @@ class FtpFs(AbstractedFS):
293
294
 
294
295
  def mkdir(self, path ) :
295
296
  ap, vfs, _ = self.rv2a(path, w=True)
296
- chmod = vfs.flags["chmod_d"]
297
- bos.makedirs(ap, chmod) # filezilla expects this
297
+ bos.makedirs(ap, vf=vfs.flags) # filezilla expects this
298
298
 
299
299
  def listdir(self, path ) :
300
300
  vpath = join(self.cwd, path)
@@ -33,7 +33,7 @@ except:
33
33
 
34
34
  from .__init__ import ANYWIN, PY2, RES, TYPE_CHECKING, EnvParams, unicode
35
35
  from .__version__ import S_VERSION
36
- from .authsrv import VFS # typechk
36
+ from .authsrv import LEELOO_DALLAS, VFS # typechk
37
37
  from .bos import bos
38
38
  from .star import StreamTar
39
39
  from .stolen.qrcodegen import QrCode, qr2svg
@@ -79,8 +79,10 @@ from .util import (
79
79
  hidedir,
80
80
  html_bescape,
81
81
  html_escape,
82
+ html_sh_esc,
82
83
  humansize,
83
84
  ipnorm,
85
+ json_hesc,
84
86
  justcopy,
85
87
  load_resource,
86
88
  loadpy,
@@ -103,6 +105,7 @@ from .util import (
103
105
  sanitize_vpath,
104
106
  sendfile_kern,
105
107
  sendfile_py,
108
+ set_fperms,
106
109
  stat_resource,
107
110
  ub64dec,
108
111
  ub64enc,
@@ -617,6 +620,9 @@ class HttpCli(object):
617
620
  ) or self.args.idp_h_key in self.headers
618
621
 
619
622
  if trusted_key and trusted_xff:
623
+ if idp_usr.lower() == LEELOO_DALLAS:
624
+ self.loud_reply("send her back", status=403)
625
+ return False
620
626
  self.asrv.idp_checkin(self.conn.hsrv.broker, idp_usr, idp_grp)
621
627
  else:
622
628
  if not trusted_key:
@@ -1105,15 +1111,18 @@ class HttpCli(object):
1105
1111
  else:
1106
1112
  return True
1107
1113
 
1114
+ host = self.host.lower()
1115
+ if host.startswith("["):
1116
+ if "]:" in host:
1117
+ host = host.split("]:")[0] + "]"
1118
+ else:
1119
+ host = host.split(":")[0]
1120
+
1108
1121
  oh = self.out_headers
1109
1122
  origin = origin.lower()
1110
- good_origins = self.args.acao + [
1111
- "%s://%s"
1112
- % (
1113
- "https" if self.is_https else "http",
1114
- self.host.lower().split(":")[0],
1115
- )
1116
- ]
1123
+ proto = "https" if self.is_https else "http"
1124
+ good_origins = self.args.acao + ["%s://%s" % (proto, host)]
1125
+
1117
1126
  if "pw" in ih or re.sub(r"(:[0-9]{1,5})?/?$", "", origin) in good_origins:
1118
1127
  good_origin = True
1119
1128
  bad_hdrs = ("",)
@@ -1570,6 +1579,18 @@ class HttpCli(object):
1570
1579
  self.log("inaccessible: %r" % ("/" + self.vpath,))
1571
1580
  raise Pebkac(401, "authenticate")
1572
1581
 
1582
+ if "quota-available-bytes" in props and not self.args.nid:
1583
+ bfree, btot, _ = get_df(vn.realpath, False)
1584
+ if btot:
1585
+ df = {
1586
+ "quota-available-bytes": str(bfree),
1587
+ "quota-used-bytes": str(btot - bfree),
1588
+ }
1589
+ else:
1590
+ df = {}
1591
+ else:
1592
+ df = {}
1593
+
1573
1594
  fgen = itertools.chain([topdir], fgen)
1574
1595
  vtop = vjoin(self.args.R, vjoin(vn.vpath, rem))
1575
1596
 
@@ -1612,6 +1633,9 @@ class HttpCli(object):
1612
1633
  ap = os.path.join(tap, x["vp"])
1613
1634
  pvs["getcontenttype"] = html_escape(guess_mime(rp, ap))
1614
1635
  pvs["getcontentlength"] = str(st.st_size)
1636
+ elif df:
1637
+ pvs.update(df)
1638
+ df = {}
1615
1639
 
1616
1640
  for k, v in pvs.items():
1617
1641
  if k not in props:
@@ -2060,7 +2084,7 @@ class HttpCli(object):
2060
2084
  fdir, fn = os.path.split(fdir)
2061
2085
  rem, _ = vsplit(rem)
2062
2086
 
2063
- bos.makedirs(fdir, vfs.flags["chmod_d"])
2087
+ bos.makedirs(fdir, vf=vfs.flags)
2064
2088
 
2065
2089
  open_ka = {"fun": open}
2066
2090
  open_a = ["wb", self.args.iobuf]
@@ -2117,9 +2141,7 @@ class HttpCli(object):
2117
2141
  if nameless:
2118
2142
  fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip())
2119
2143
 
2120
- params = {"suffix": suffix, "fdir": fdir}
2121
- if "chmod_f" in vfs.flags:
2122
- params["chmod"] = vfs.flags["chmod_f"]
2144
+ params = {"suffix": suffix, "fdir": fdir, "vf": vfs.flags}
2123
2145
  if self.args.nw:
2124
2146
  params = {}
2125
2147
  fn = os.devnull
@@ -2167,7 +2189,7 @@ class HttpCli(object):
2167
2189
  if self.args.nw:
2168
2190
  fn = os.devnull
2169
2191
  else:
2170
- bos.makedirs(fdir, vfs.flags["chmod_d"])
2192
+ bos.makedirs(fdir, vf=vfs.flags)
2171
2193
  path = os.path.join(fdir, fn)
2172
2194
  if not nameless:
2173
2195
  self.vpath = vjoin(self.vpath, fn)
@@ -2299,7 +2321,7 @@ class HttpCli(object):
2299
2321
  if self.args.hook_v:
2300
2322
  log_reloc(self.log, hr["reloc"], x, path, vp, fn, vfs, rem)
2301
2323
  fdir, self.vpath, fn, (vfs, rem) = x
2302
- bos.makedirs(fdir, vfs.flags["chmod_d"])
2324
+ bos.makedirs(fdir, vf=vfs.flags)
2303
2325
  path2 = os.path.join(fdir, fn)
2304
2326
  atomic_move(self.log, path, path2, vfs.flags)
2305
2327
  path = path2
@@ -2584,7 +2606,7 @@ class HttpCli(object):
2584
2606
  dst = vfs.canonical(rem)
2585
2607
  try:
2586
2608
  if not bos.path.isdir(dst):
2587
- bos.makedirs(dst, vfs.flags["chmod_d"])
2609
+ bos.makedirs(dst, vf=vfs.flags)
2588
2610
  except OSError as ex:
2589
2611
  self.log("makedirs failed %r" % (dst,))
2590
2612
  if not bos.path.isdir(dst):
@@ -3027,7 +3049,7 @@ class HttpCli(object):
3027
3049
  raise Pebkac(405, 'folder "/%s" already exists' % (vpath,))
3028
3050
 
3029
3051
  try:
3030
- bos.makedirs(fn, vfs.flags["chmod_d"])
3052
+ bos.makedirs(fn, vf=vfs.flags)
3031
3053
  except OSError as ex:
3032
3054
  if ex.errno == errno.EACCES:
3033
3055
  raise Pebkac(500, "the server OS denied write-access")
@@ -3068,8 +3090,8 @@ class HttpCli(object):
3068
3090
 
3069
3091
  with open(fsenc(fn), "wb") as f:
3070
3092
  f.write(b"`GRUNNUR`\n")
3071
- if "chmod_f" in vfs.flags:
3072
- os.fchmod(f.fileno(), vfs.flags["chmod_f"])
3093
+ if "fperms" in vfs.flags:
3094
+ set_fperms(f, vfs.flags)
3073
3095
 
3074
3096
  vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
3075
3097
  self.redirect(vpath, "?edit")
@@ -3143,7 +3165,7 @@ class HttpCli(object):
3143
3165
  )
3144
3166
  upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/")
3145
3167
  if not nullwrite:
3146
- bos.makedirs(fdir_base, vfs.flags["chmod_d"])
3168
+ bos.makedirs(fdir_base, vf=vfs.flags)
3147
3169
 
3148
3170
  rnd, lifetime, xbu, xau = self.upload_flags(vfs)
3149
3171
  zs = self.uparam.get("want") or self.headers.get("accept") or ""
@@ -3176,7 +3198,7 @@ class HttpCli(object):
3176
3198
  if rnd:
3177
3199
  fname = rand_name(fdir, fname, rnd)
3178
3200
 
3179
- open_args = {"fdir": fdir, "suffix": suffix}
3201
+ open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
3180
3202
 
3181
3203
  if "replace" in self.uparam:
3182
3204
  if not self.can_delete:
@@ -3238,11 +3260,8 @@ class HttpCli(object):
3238
3260
  else:
3239
3261
  open_args["fdir"] = fdir
3240
3262
 
3241
- if "chmod_f" in vfs.flags:
3242
- open_args["chmod"] = vfs.flags["chmod_f"]
3243
-
3244
3263
  if p_file and not nullwrite:
3245
- bos.makedirs(fdir, vfs.flags["chmod_d"])
3264
+ bos.makedirs(fdir, vf=vfs.flags)
3246
3265
 
3247
3266
  # reserve destination filename
3248
3267
  f, fname = ren_open(fname, "wb", fdir=fdir, suffix=suffix)
@@ -3346,7 +3365,7 @@ class HttpCli(object):
3346
3365
  if nullwrite:
3347
3366
  fdir = ap2 = ""
3348
3367
  else:
3349
- bos.makedirs(fdir, vfs.flags["chmod_d"])
3368
+ bos.makedirs(fdir, vf=vfs.flags)
3350
3369
  atomic_move(self.log, abspath, ap2, vfs.flags)
3351
3370
  abspath = ap2
3352
3371
  sz = bos.path.getsize(abspath)
@@ -3467,8 +3486,8 @@ class HttpCli(object):
3467
3486
  ft = "{}:{}".format(self.ip, self.addr[1])
3468
3487
  ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
3469
3488
  f.write(ft.encode("utf-8"))
3470
- if "chmod_f" in vfs.flags:
3471
- os.fchmod(f.fileno(), vfs.flags["chmod_f"])
3489
+ if "fperms" in vfs.flags:
3490
+ set_fperms(f, vfs.flags)
3472
3491
  except Exception as ex:
3473
3492
  suf = "\nfailed to write the upload report: {}".format(ex)
3474
3493
 
@@ -3518,7 +3537,7 @@ class HttpCli(object):
3518
3537
  lim = vfs.get_dbv(rem)[0].lim
3519
3538
  if lim:
3520
3539
  fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker)
3521
- bos.makedirs(fp, vfs.flags["chmod_d"])
3540
+ bos.makedirs(fp, vf=vfs.flags)
3522
3541
 
3523
3542
  fp = os.path.join(fp, fn)
3524
3543
  rem = "{}/{}".format(rp, fn).strip("/")
@@ -3586,15 +3605,17 @@ class HttpCli(object):
3586
3605
  zs = ub64enc(zb).decode("ascii")[:24].lower()
3587
3606
  dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs)
3588
3607
  self.log("moving old version to %s/%s" % (dp, mfile2))
3589
- if bos.makedirs(dp, vfs.flags["chmod_d"]):
3608
+ if bos.makedirs(dp, vf=vfs.flags):
3590
3609
  with open(os.path.join(dp, "dir.txt"), "wb") as f:
3591
3610
  f.write(afsenc(vrd))
3592
- if "chmod_f" in vfs.flags:
3593
- os.fchmod(f.fileno(), vfs.flags["chmod_f"])
3611
+ if "fperms" in vfs.flags:
3612
+ set_fperms(f, vfs.flags)
3594
3613
  elif hist_cfg == "s":
3595
3614
  dp = os.path.join(mdir, ".hist")
3596
3615
  try:
3597
3616
  bos.mkdir(dp, vfs.flags["chmod_d"])
3617
+ if "chown" in vfs.flags:
3618
+ bos.chown(dp, vfs.flags["uid"], vfs.flags["gid"])
3598
3619
  hidedir(dp)
3599
3620
  except:
3600
3621
  pass
@@ -3632,8 +3653,8 @@ class HttpCli(object):
3632
3653
  wunlink(self.log, fp, vfs.flags)
3633
3654
 
3634
3655
  with open(fsenc(fp), "wb", self.args.iobuf) as f:
3635
- if "chmod_f" in vfs.flags:
3636
- os.fchmod(f.fileno(), vfs.flags["chmod_f"])
3656
+ if "fperms" in vfs.flags:
3657
+ set_fperms(f, vfs.flags)
3637
3658
  sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
3638
3659
 
3639
3660
  if lim:
@@ -4870,11 +4891,8 @@ class HttpCli(object):
4870
4891
  else:
4871
4892
  rip = host
4872
4893
 
4873
- # safer than html_escape/quotep since this avoids both XSS and shell-stuff
4874
- pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
4875
- vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
4876
- pw = pw.replace(" ", "%20")
4877
- vp = vp.replace(" ", "%20")
4894
+ vp = (self.uparam["hc"] or "").lstrip("/")
4895
+ pw = self.pw or "hunter2"
4878
4896
  if pw in self.asrv.sesa:
4879
4897
  pw = "hunter2"
4880
4898
 
@@ -4883,14 +4901,14 @@ class HttpCli(object):
4883
4901
  args=self.args,
4884
4902
  accs=bool(self.asrv.acct),
4885
4903
  s="s" if self.is_https else "",
4886
- rip=rip,
4887
- ep=ep,
4888
- vp=vp,
4889
- rvp=vjoin(self.args.R, vp),
4890
- host=host,
4891
- hport=hport,
4904
+ rip=html_sh_esc(rip),
4905
+ ep=html_sh_esc(ep),
4906
+ vp=html_sh_esc(vp),
4907
+ rvp=html_sh_esc(vjoin(self.args.R, vp)),
4908
+ host=html_sh_esc(host),
4909
+ hport=html_sh_esc(hport),
4892
4910
  aname=aname,
4893
- pw=pw,
4911
+ pw=html_sh_esc(pw),
4894
4912
  )
4895
4913
  self.reply(html.encode("utf-8"))
4896
4914
  return True
@@ -5552,7 +5570,7 @@ class HttpCli(object):
5552
5570
  self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
5553
5571
  return True
5554
5572
 
5555
- html = self.j2s("rups", this=self, v=jtxt)
5573
+ html = self.j2s("rups", this=self, v=json_hesc(jtxt))
5556
5574
  self.reply(html.encode("utf-8"), status=200)
5557
5575
  return True
5558
5576
 
@@ -5616,15 +5634,15 @@ class HttpCli(object):
5616
5634
  raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
5617
5635
  raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
5618
5636
 
5637
+ skey = self.uparam.get("skey") or self.vpath.split("/")[-1]
5638
+
5619
5639
  if self.args.shr_v:
5620
- self.log("handle_eshare: " + self.req)
5640
+ self.log("handle_eshare: " + skey)
5621
5641
 
5622
5642
  cur = idx.get_shr()
5623
5643
  if not cur:
5624
5644
  raise Pebkac(400, "huh, sharing must be disabled in the server config...")
5625
5645
 
5626
- skey = self.vpath.split("/")[-1]
5627
-
5628
5646
  rows = cur.execute("select un, t1 from sh where k = ?", (skey,)).fetchall()
5629
5647
  un = rows[0][0] if rows and rows[0] else ""
5630
5648
 
@@ -6133,13 +6151,13 @@ class HttpCli(object):
6133
6151
  self.log("#wow #whoa")
6134
6152
 
6135
6153
  if not self.args.nid:
6136
- free, total, _ = get_df(abspath, False)
6137
- if total is not None:
6154
+ free, total, zs = get_df(abspath, False)
6155
+ if total:
6138
6156
  h1 = humansize(free or 0)
6139
6157
  h2 = humansize(total)
6140
6158
  srv_info.append("{} free of {}".format(h1, h2))
6141
- elif free is not None:
6142
- srv_info.append(humansize(free, True) + " free")
6159
+ elif zs:
6160
+ self.log("diskfree(%r): %s" % (abspath, zs), 3)
6143
6161
 
6144
6162
  srv_infot = "</span> // <span>".join(srv_info)
6145
6163
 
@@ -317,7 +317,7 @@ class SMB(object):
317
317
 
318
318
  self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2)
319
319
  try:
320
- bos.makedirs(ap2, vfs2.flags["chmod_d"])
320
+ bos.makedirs(ap2, vf=vfs2.flags)
321
321
  except:
322
322
  pass
323
323