hfs 3.1.5 → 3.2.0-beta2

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 (224) hide show
  1. package/admin/assets/af-DWEYq388.js +1 -0
  2. package/admin/assets/am-DgLbAgj6.js +1 -0
  3. package/admin/assets/ar-DgEWkO74.js +1 -0
  4. package/admin/assets/ar-dz-DHq--Sr8.js +1 -0
  5. package/admin/assets/ar-iq-D1r3nsb9.js +1 -0
  6. package/admin/assets/ar-kw-C85fGHwp.js +1 -0
  7. package/admin/assets/ar-ly-Bq6pjGjs.js +1 -0
  8. package/admin/assets/ar-ma-SGvmh6Mj.js +1 -0
  9. package/admin/assets/ar-sa-Bv8hiFi6.js +1 -0
  10. package/admin/assets/ar-tn-Bdvo77v3.js +1 -0
  11. package/admin/assets/az-7fPndoov.js +1 -0
  12. package/admin/assets/be-wjXeIeAK.js +1 -0
  13. package/admin/assets/bg-DZwBvjzH.js +1 -0
  14. package/admin/assets/bi-qCtxvMhO.js +1 -0
  15. package/admin/assets/bm-BVPWvreb.js +1 -0
  16. package/admin/assets/bn-CdWvye7d.js +1 -0
  17. package/admin/assets/bn-bd-B5-3blmz.js +1 -0
  18. package/admin/assets/bo-BEVcgyN2.js +1 -0
  19. package/admin/assets/br-DGQD6fFs.js +1 -0
  20. package/admin/assets/bs-So4MoRua.js +1 -0
  21. package/admin/assets/ca-Bk5ytG4g.js +1 -0
  22. package/admin/assets/cs-C4NU8eW-.js +1 -0
  23. package/admin/assets/cv-Ct-s-zrW.js +1 -0
  24. package/admin/assets/cy-ojtDPj8_.js +1 -0
  25. package/admin/assets/da-CYGin6vm.js +1 -0
  26. package/admin/assets/de-BEbJ01zH.js +1 -0
  27. package/admin/assets/de-at-C7UwE1rJ.js +1 -0
  28. package/admin/assets/de-ch-CsZ7YQc_.js +1 -0
  29. package/admin/assets/dv-DaVliwLd.js +1 -0
  30. package/admin/assets/el-DM_KqKEP.js +1 -0
  31. package/admin/assets/en-DedtOfaf.js +1 -0
  32. package/admin/assets/en-au-52Bzk5D9.js +1 -0
  33. package/admin/assets/en-ca-3pzEPK2N.js +1 -0
  34. package/admin/assets/en-gb-BrwDQS2G.js +1 -0
  35. package/admin/assets/en-ie-BUXSHrkL.js +1 -0
  36. package/admin/assets/en-il-a22drDCn.js +1 -0
  37. package/admin/assets/en-in-BUjecjkp.js +1 -0
  38. package/admin/assets/en-nz-Bbo7tnB_.js +1 -0
  39. package/admin/assets/en-sg-CZVDddmd.js +1 -0
  40. package/admin/assets/en-tt-DmSGwRia.js +1 -0
  41. package/admin/assets/eo-B71nkHZU.js +1 -0
  42. package/admin/assets/es-Dk6VCuuk.js +1 -0
  43. package/admin/assets/es-do-DMErY8ol.js +1 -0
  44. package/admin/assets/es-mx-BMRmqa3u.js +1 -0
  45. package/admin/assets/es-pr-CtBQz48p.js +1 -0
  46. package/admin/assets/es-us-CrDl5pnO.js +1 -0
  47. package/admin/assets/et-CO9OHqio.js +1 -0
  48. package/admin/assets/eu-Bip44atW.js +1 -0
  49. package/admin/assets/fa-CHbJ_dTM.js +1 -0
  50. package/admin/assets/fi-DKfoLmaQ.js +1 -0
  51. package/admin/assets/fo-DG1kOEfw.js +1 -0
  52. package/admin/assets/fr-DV73GZR4.js +1 -0
  53. package/admin/assets/fr-ca-BK-RoZiC.js +1 -0
  54. package/admin/assets/fr-ch-DjqEC5E_.js +1 -0
  55. package/admin/assets/fy-znrRQdeC.js +1 -0
  56. package/admin/assets/ga-BlZeKu0N.js +1 -0
  57. package/admin/assets/gd-BmrycMnC.js +1 -0
  58. package/admin/assets/gl-CuT8e5mi.js +1 -0
  59. package/admin/assets/gom-latn-BSWVd0A6.js +1 -0
  60. package/admin/assets/gu-BHK6LfvD.js +1 -0
  61. package/admin/assets/he-DPoTUevR.js +1 -0
  62. package/admin/assets/hi-BRuLafoW.js +1 -0
  63. package/admin/assets/hr-Bzge-10P.js +1 -0
  64. package/admin/assets/ht-Ck9BCna1.js +1 -0
  65. package/admin/assets/hu-CzqqbYmU.js +1 -0
  66. package/admin/assets/hy-am-C-eV4E8v.js +1 -0
  67. package/admin/assets/id-Dv8GZQvB.js +1 -0
  68. package/admin/assets/{index-DTxjaflW.js → index-BPIX0qPj.js} +1 -1
  69. package/admin/assets/index-CFWd-FDo.css +1 -0
  70. package/admin/assets/index-D3HviM6x.js +889 -0
  71. package/admin/assets/is-CK6VY3M_.js +1 -0
  72. package/admin/assets/it-1gtki4a5.js +1 -0
  73. package/admin/assets/it-ch-C0Mj3-pC.js +1 -0
  74. package/admin/assets/ja-Dl3AfnM1.js +1 -0
  75. package/admin/assets/jv-CznX-tGV.js +1 -0
  76. package/admin/assets/ka-BNjZxCug.js +1 -0
  77. package/admin/assets/kk-80J_xldf.js +1 -0
  78. package/admin/assets/km-CuWChDRB.js +1 -0
  79. package/admin/assets/kn-BZp_PBdl.js +1 -0
  80. package/admin/assets/ko-RirpyUl_.js +1 -0
  81. package/admin/assets/ku-Dz8ACD5w.js +1 -0
  82. package/admin/assets/ky-BN3ylOhj.js +1 -0
  83. package/admin/assets/lb-D7h_YoEn.js +1 -0
  84. package/admin/assets/lo-BrlbTUPD.js +1 -0
  85. package/admin/assets/lt-cXuHFdTa.js +1 -0
  86. package/admin/assets/lv-CjIpv13Q.js +1 -0
  87. package/admin/assets/me-B-jTh39Z.js +1 -0
  88. package/admin/assets/mi-D06xdVmt.js +1 -0
  89. package/admin/assets/mk-iLbuxyOf.js +1 -0
  90. package/admin/assets/ml-2W0y6Zb2.js +1 -0
  91. package/admin/assets/mn-6zbvKjeb.js +1 -0
  92. package/admin/assets/mr-7-jrgLyw.js +1 -0
  93. package/admin/assets/ms-CRH6rEXt.js +1 -0
  94. package/admin/assets/ms-my-BIJmu2S-.js +1 -0
  95. package/admin/assets/mt-CbXmxK-D.js +1 -0
  96. package/admin/assets/my-BvMUsxU8.js +1 -0
  97. package/admin/assets/nb-DvKHgF7L.js +1 -0
  98. package/admin/assets/ne-DiZZ3Lm6.js +1 -0
  99. package/admin/assets/nl-be-Dy7PYRbC.js +1 -0
  100. package/admin/assets/nl-t_2A_VAT.js +1 -0
  101. package/admin/assets/nn-Cw7EwosO.js +1 -0
  102. package/admin/assets/oc-lnc-CuFfB75K.js +1 -0
  103. package/admin/assets/pa-in-DOFyZ-Ft.js +1 -0
  104. package/admin/assets/pl-BD7FyCJj.js +1 -0
  105. package/admin/assets/pt-DSKLLE_u.js +1 -0
  106. package/admin/assets/pt-br-BhL4gb5Z.js +1 -0
  107. package/admin/assets/rn-DoHoZZPd.js +1 -0
  108. package/admin/assets/ro-B0v-_lH0.js +1 -0
  109. package/admin/assets/ru-BMVOk5eA.js +1 -0
  110. package/admin/assets/rw-CWF0w6eL.js +1 -0
  111. package/admin/assets/sd-DO2rrjch.js +1 -0
  112. package/admin/assets/se-CAolO9WQ.js +1 -0
  113. package/admin/assets/{sha512-D936QW8l.js → sha512-ZlUYj4Hr.js} +1 -1
  114. package/admin/assets/si-D03dHfb5.js +1 -0
  115. package/admin/assets/sk-_GcZGaN3.js +1 -0
  116. package/admin/assets/sl-Cb1lUGab.js +1 -0
  117. package/admin/assets/sq-Czzt23Tr.js +1 -0
  118. package/admin/assets/sr-D76dVqKJ.js +1 -0
  119. package/admin/assets/sr-cyrl-CuoFbJjW.js +1 -0
  120. package/admin/assets/ss-g-fGaM29.js +1 -0
  121. package/admin/assets/sv-CZxc8I45.js +1 -0
  122. package/admin/assets/sv-fi-D8REJeLz.js +1 -0
  123. package/admin/assets/sw-B1n3PjWG.js +1 -0
  124. package/admin/assets/ta-K5mexJNT.js +1 -0
  125. package/admin/assets/te-DT6dj5B6.js +1 -0
  126. package/admin/assets/tet-DXwYNm_H.js +1 -0
  127. package/admin/assets/tg-BCcZKcE2.js +1 -0
  128. package/admin/assets/th-CeeeseFX.js +1 -0
  129. package/admin/assets/tk-CJ6KW44d.js +1 -0
  130. package/admin/assets/tl-ph-DzE8lDmm.js +1 -0
  131. package/admin/assets/tlh-ER6KiMxG.js +1 -0
  132. package/admin/assets/tr-D48NGNpr.js +1 -0
  133. package/admin/assets/tzl-5AsmDTYM.js +1 -0
  134. package/admin/assets/tzm-2AzZ1YPf.js +1 -0
  135. package/admin/assets/tzm-latn-Cd5hPQuT.js +1 -0
  136. package/admin/assets/ug-cn-DU-MZ9Vx.js +1 -0
  137. package/admin/assets/uk-hn6vcOkn.js +1 -0
  138. package/admin/assets/ur-jOObtqB6.js +1 -0
  139. package/admin/assets/uz-BlftYfHF.js +1 -0
  140. package/admin/assets/uz-latn-BpuI0ccM.js +1 -0
  141. package/admin/assets/vi-DVo3LusT.js +1 -0
  142. package/admin/assets/x-pseudo-BuVzNhqi.js +1 -0
  143. package/admin/assets/yo-BvWGwb4m.js +1 -0
  144. package/admin/assets/zh-D9-tfba1.js +1 -0
  145. package/admin/assets/zh-cn-CFL5sbIW.js +1 -0
  146. package/admin/assets/zh-hk-DRP8u65r.js +1 -0
  147. package/admin/assets/zh-tw-CtVs1ihI.js +1 -0
  148. package/admin/index.html +2 -2
  149. package/frontend/assets/index-legacy-D3BTBYs5.js +9 -0
  150. package/frontend/assets/{index-legacy-vmpqwZZf.js → index-legacy-DcrWtKxQ.js} +1 -1
  151. package/frontend/assets/{sha512-legacy-wI89-UHR.js → sha512-legacy-DJvEwScE.js} +1 -1
  152. package/frontend/index.html +1 -1
  153. package/npm-shrinkwrap.json +144 -71
  154. package/package.json +9 -9
  155. package/plugins/antibrute/plugin.js +150 -19
  156. package/plugins/list-uploader/public/main.js +1 -1
  157. package/src/acme.js +11 -7
  158. package/src/api.accounts.js +3 -3
  159. package/src/api.auth.js +3 -1
  160. package/src/api.get_file_list.js +17 -13
  161. package/src/api.monitor.js +47 -43
  162. package/src/api.net.js +4 -3
  163. package/src/api.vfs.js +1 -1
  164. package/src/basicWeb.js +1 -1
  165. package/src/commands.js +54 -1
  166. package/src/comments.js +7 -4
  167. package/src/config.js +9 -5
  168. package/src/consoleLog.js +39 -1
  169. package/src/const.js +4 -0
  170. package/src/cross.js +33 -6
  171. package/src/errorPages.js +20 -10
  172. package/src/events.js +3 -3
  173. package/src/expiringCache.js +8 -7
  174. package/src/fileAttr.js +73 -15
  175. package/src/frontEndApis.js +20 -15
  176. package/src/index.js +2 -1
  177. package/src/langs/hfs-lang-ar.json +2 -1
  178. package/src/langs/hfs-lang-bg.json +2 -1
  179. package/src/langs/hfs-lang-de.json +2 -1
  180. package/src/langs/hfs-lang-el.json +2 -1
  181. package/src/langs/hfs-lang-es.json +2 -1
  182. package/src/langs/hfs-lang-fi.json +2 -1
  183. package/src/langs/hfs-lang-fr.json +2 -1
  184. package/src/langs/hfs-lang-hu.json +2 -1
  185. package/src/langs/hfs-lang-it.json +2 -1
  186. package/src/langs/hfs-lang-ja.json +2 -1
  187. package/src/langs/hfs-lang-ko.json +2 -1
  188. package/src/langs/hfs-lang-lt.json +2 -1
  189. package/src/langs/hfs-lang-ms.json +2 -0
  190. package/src/langs/hfs-lang-nl.json +2 -1
  191. package/src/langs/hfs-lang-pt-br.json +2 -1
  192. package/src/langs/hfs-lang-ro.json +2 -1
  193. package/src/langs/hfs-lang-ru.json +2 -1
  194. package/src/langs/hfs-lang-sr-latn.json +2 -1
  195. package/src/langs/hfs-lang-sr.json +2 -1
  196. package/src/langs/hfs-lang-th.json +2 -1
  197. package/src/langs/hfs-lang-tr.json +2 -1
  198. package/src/langs/hfs-lang-uk.json +2 -1
  199. package/src/langs/hfs-lang-vi.json +2 -1
  200. package/src/langs/hfs-lang-zh-tw.json +2 -1
  201. package/src/langs/hfs-lang-zh.json +2 -1
  202. package/src/listen.js +2 -1
  203. package/src/log.js +27 -2
  204. package/src/middlewares.js +8 -2
  205. package/src/misc.js +14 -0
  206. package/src/nat.js +61 -21
  207. package/src/outboundProxy.js +1 -1
  208. package/src/perm.js +5 -1
  209. package/src/plugins.js +34 -5
  210. package/src/roots.js +1 -1
  211. package/src/selfCheck.js +2 -1
  212. package/src/serveGuiAndSharedFiles.js +18 -7
  213. package/src/serveGuiFiles.js +2 -1
  214. package/src/update.js +10 -18
  215. package/src/urlList.js +32 -0
  216. package/src/util-files.js +24 -9
  217. package/src/util-http.js +4 -0
  218. package/src/vfs.js +21 -9
  219. package/src/walkDir.js +7 -1
  220. package/src/webdav.js +4 -1
  221. package/src/zip.js +3 -2
  222. package/admin/assets/index-B66w-a0v.css +0 -1
  223. package/admin/assets/index-Df6vYR7s.js +0 -822
  224. package/frontend/assets/index-legacy-emBsvICj.js +0 -9
package/src/urlList.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.encodeUrlList = encodeUrlList;
5
+ exports.decodeUrlList = decodeUrlList;
6
+ // utilities to pass a list of files via url
7
+ // not easy to find chars that are not allowed in file names both for windows and unix
8
+ const URL_LIST_SEPARATOR = '//';
9
+ const URL_LIST_SAME_FOLDER = '\0'; // nul cannot be a valid file name char, and query encoding carries it as %00 without conflicting with path slashes
10
+ function encodeUrlList(entries) {
11
+ let previousFolder = '';
12
+ return entries.map(entry => {
13
+ const slash = entry.lastIndexOf('/');
14
+ const folder = slash < 0 ? '' : entry.slice(0, slash + 1);
15
+ const name = slash < 0 ? entry : entry.slice(slash + 1);
16
+ if (folder && folder === previousFolder)
17
+ return URL_LIST_SAME_FOLDER + name;
18
+ previousFolder = folder;
19
+ return entry;
20
+ }).join(URL_LIST_SEPARATOR);
21
+ }
22
+ function decodeUrlList(list) {
23
+ let previousFolder = '';
24
+ return list?.split(URL_LIST_SEPARATOR).map(entry => {
25
+ const sameFolder = entry.startsWith(URL_LIST_SAME_FOLDER);
26
+ if (sameFolder && previousFolder)
27
+ entry = previousFolder + entry.slice(URL_LIST_SAME_FOLDER.length);
28
+ const slash = entry.lastIndexOf('/');
29
+ previousFolder = slash < 0 ? '' : entry.slice(0, slash + 1);
30
+ return entry;
31
+ });
32
+ }
package/src/util-files.js CHANGED
@@ -29,8 +29,7 @@ const fast_glob_1 = __importDefault(require("fast-glob"));
29
29
  const const_1 = require("./const");
30
30
  const promises_2 = require("stream/promises");
31
31
  const stat_1 = require("./stat");
32
- // @ts-ignore
33
- const unzip_stream_1 = __importDefault(require("unzip-stream"));
32
+ const unzipper_1 = __importDefault(require("unzipper"));
34
33
  const fileTimeout = (0, config_1.defineConfig)('file_timeout', 3, x => x * 1000);
35
34
  // a smart (and a bit arbitrary) way to decide if we need the stat-workers functionality. Without it, we may be a bit faster. We'll see with experience if we need a dedicated configuration.
36
35
  const disableStatWorkers = Number(process.env.UV_THREADPOOL_SIZE) >= 10;
@@ -104,18 +103,29 @@ function escapeGlobPath(path) {
104
103
  return fast_glob_1.default.escapePath(path.replace(/\\/g, '/'));
105
104
  }
106
105
  async function unzip(stream, cb) {
106
+ const extracted = new Map();
107
107
  let chain = Promise.resolve();
108
- return new Promise((resolve, reject) => stream.pipe(unzip_stream_1.default.Parse())
109
- .on('end', () => chain.then(resolve))
108
+ return new Promise((resolve, reject) => stream.pipe(unzipper_1.default.Parse())
109
+ .on('close', () => chain.then(resolve, reject))
110
110
  .on('error', reject)
111
111
  .on('entry', (entry) => chain = chain.then(async () => {
112
112
  const { path, type } = entry;
113
113
  const dest = await (0, cross_1.try_)(() => cb(path), e => console.warn(String(e)));
114
114
  if (!dest || type !== 'File')
115
- return entry.autodrain();
115
+ return entry.autodrain().promise();
116
+ extracted.set(path, dest);
116
117
  console.debug('Unzip', dest);
118
+ // keep writes serialized so archive entries can't race while callers map paths asynchronously
117
119
  const thisFile = entry.pipe(await createSafeWriteStream(dest));
118
120
  await (0, promises_2.finished)(thisFile);
121
+ }))
122
+ // unix modes live in the central directory, so we reapply them after the file stream has been written
123
+ .on('entryInCentral', (entry) => chain = chain.then(async () => {
124
+ if (entry.type !== 'File')
125
+ return;
126
+ const dest = extracted.get(entry.path);
127
+ if (dest && entry.unixAttrs)
128
+ await (0, promises_1.chmod)(dest, entry.unixAttrs).catch(() => { });
119
129
  })));
120
130
  }
121
131
  async function ensureParentFolder(path, dirnameIt = true) {
@@ -172,19 +182,24 @@ function exists(path) {
172
182
  }
173
183
  // parse a file, caching unless timestamp has changed
174
184
  exports.parseFileCache = new Map();
175
- async function loadFileCached(path, loader) {
185
+ async function loadFileCached(path, loader, minInterval = 0) {
176
186
  const cached = exports.parseFileCache.get(path);
187
+ const now = Date.now();
188
+ if (cached && now - cached.lastCheck < minInterval)
189
+ return cached.parsed;
177
190
  const ts = await statWithTimeout(path).then(x => x.mtime, e => {
178
191
  if (e?.message !== 'timeout')
179
192
  throw e;
180
193
  return cached?.ts || new Date(0); // on timeout (e.g. thread pool saturated), serve cache if any, or attempt the loader
181
194
  });
195
+ if (cached)
196
+ cached.lastCheck = now;
182
197
  if (cached && Number(ts) === Number(cached.ts))
183
198
  return cached.parsed;
184
199
  const parsed = loader(path);
185
- exports.parseFileCache.set(path, { ts, parsed });
200
+ exports.parseFileCache.set(path, { ts, parsed, lastCheck: now });
186
201
  return parsed;
187
202
  }
188
- async function parseFile(path, parse) {
189
- return loadFileCached(path, () => (0, promises_1.readFile)(path).then(parse));
203
+ async function parseFile(path, parse, skipStatBefore = 0) {
204
+ return loadFileCached(path, () => (0, promises_1.readFile)(path).then(parse), skipStatBefore);
190
205
  }
package/src/util-http.js CHANGED
@@ -67,6 +67,8 @@ function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...op
67
67
  return Object.assign(new Promise(async (resolve, reject) => {
68
68
  proxy ??= httpStream.defaultProxy;
69
69
  options.headers ??= {};
70
+ if (httpStream.defaultUA && !Object.keys(options.headers).find(k => k.toLowerCase() === 'user-agent'))
71
+ options.headers['user-agent'] = httpStream.defaultUA;
70
72
  if (body) {
71
73
  options.method ||= 'POST';
72
74
  if (lodash_1.default.isPlainObject(body)) {
@@ -135,6 +137,8 @@ function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...op
135
137
  e.cause ??= req; // enrich the error
136
138
  reject(e);
137
139
  });
140
+ if (options.timeout) // node only emits the timeout event, so destroy the request to unblock callers waiting for the body
141
+ req.setTimeout(options.timeout, () => req.destroy(Object.assign(Error('timeout'), { code: 'ETIMEDOUT' })));
138
142
  if (body && body instanceof node_stream_1.Readable)
139
143
  body.pipe(req).on('end', () => req.end());
140
144
  else
package/src/vfs.js CHANGED
@@ -15,10 +15,11 @@ exports.getNodeByName = getNodeByName;
15
15
  exports.isRoot = isRoot;
16
16
  exports.getNodeName = getNodeName;
17
17
  exports.nodeIsFolder = nodeIsFolder;
18
- exports.hasDefaultFile = hasDefaultFile;
18
+ exports.getDefaultFile = getDefaultFile;
19
19
  exports.nodeIsLink = nodeIsLink;
20
20
  exports.hasPermission = hasPermission;
21
21
  exports.statusCodeForMissingPerm = statusCodeForMissingPerm;
22
+ exports.simpleWhoToError = simpleWhoToError;
22
23
  exports.walkNode = walkNode;
23
24
  exports.masksCouldGivePermission = masksCouldGivePermission;
24
25
  exports.parentMaskApplier = parentMaskApplier;
@@ -35,6 +36,7 @@ const fswin_1 = __importDefault(require("fswin"));
35
36
  const comments_1 = require("./comments");
36
37
  const walkDir_1 = require("./walkDir");
37
38
  const node_stream_1 = require("node:stream");
39
+ const adminApis_1 = require("./adminApis");
38
40
  const showHiddenFiles = (0, config_1.defineConfig)('show_hidden_files', false);
39
41
  function permsFromParent(parent, child) {
40
42
  const ret = {};
@@ -225,7 +227,7 @@ function nodeIsFolder(node) {
225
227
  return undefined;
226
228
  }
227
229
  }
228
- async function hasDefaultFile(node, ctx) {
230
+ async function getDefaultFile(node, ctx) {
229
231
  return node.default && nodeIsFolder(node) && await urlToNode(node.default, ctx, node) || undefined;
230
232
  }
231
233
  function nodeIsLink(node) {
@@ -254,7 +256,7 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
254
256
  if ((0, misc_1.isWhoObject)(who))
255
257
  who = who.this;
256
258
  who ??= misc_1.defaultPerms[cur];
257
- if (typeof who !== 'string' || who === misc_1.WHO_ANY_ACCOUNT)
259
+ if (typeof who !== 'string' || who === misc_1.WHO_ANY_ACCOUNT || who === misc_1.WHO_ADMIN)
258
260
  break;
259
261
  if (!max--) {
260
262
  console.error(`Endless loop in permission ${perm}=${node[perm] ?? misc_1.defaultPerms[perm]} for ${node.url || getNodeName(node)}`);
@@ -262,19 +264,29 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
262
264
  }
263
265
  cur = who;
264
266
  } while (1);
267
+ if ((0, misc_1.isWhoObject)(who) || isWhoVfsPerms(who))
268
+ throw Error(`permission type-guard: ${JSON.stringify(who)}`);
265
269
  const eventName = 'checkVfsPermission';
266
270
  if (events_1.default.anyListener(eventName)) {
267
271
  const first = lodash_1.default.max(events_1.default.emit(eventName, { who, node, perm, ctx }));
268
272
  if (first !== undefined)
269
273
  return first;
270
274
  }
271
- if (Array.isArray(who))
272
- return (0, perm_1.ctxBelongsTo)(ctx, who) ? 0 : const_1.HTTP_UNAUTHORIZED;
273
- return typeof who === 'boolean' ? (who ? 0 : const_1.HTTP_FORBIDDEN)
274
- : who === misc_1.WHO_ANY_ACCOUNT ? ((0, auth_1.getCurrentUsername)(ctx) ? 0 : const_1.HTTP_UNAUTHORIZED)
275
- : (0, misc_1.throw_)(Error(`invalid permission: ${perm}=${(0, misc_1.try_)(() => JSON.stringify(who))}`));
275
+ return simpleWhoToError(who, ctx)
276
+ ?? (0, misc_1.throw_)(Error(`invalid permission: ${perm}=${(0, misc_1.try_)(() => JSON.stringify(who))}`));
276
277
  }
277
278
  }
279
+ function simpleWhoToError(who, ctx) {
280
+ if (Array.isArray(who))
281
+ return (0, perm_1.ctxBelongsTo)(ctx, who) ? 0 : const_1.HTTP_UNAUTHORIZED;
282
+ return typeof who === 'boolean' ? (who ? 0 : const_1.HTTP_FORBIDDEN)
283
+ : who === misc_1.WHO_ANY_ACCOUNT ? ((0, auth_1.getCurrentUsername)(ctx) ? 0 : const_1.HTTP_UNAUTHORIZED)
284
+ : who === misc_1.WHO_ADMIN ? ((0, adminApis_1.ctxAdminAccess)(ctx) ? 0 : const_1.HTTP_UNAUTHORIZED)
285
+ : undefined;
286
+ }
287
+ function isWhoVfsPerms(who) {
288
+ return typeof who === 'string' && misc_1.PERM_KEYS.includes(who);
289
+ }
278
290
  // it's the responsibility of the caller to verify you have list permission on parent, as callers have different needs.
279
291
  async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requiredPerm, onlyFolders = false, onlyFiles = false, parallelizeRecursion = true, } = {}) {
280
292
  let started = false;
@@ -337,7 +349,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
337
349
  const name = prefixPath + (renamed || path);
338
350
  if (taken?.has(normalizeFilename(name))) // taken by vfs node above
339
351
  return false; // false just in case it's a folder
340
- const item = { name, isFolder, source: (0, path_1.join)(source, path), parent };
352
+ const item = { name, isFolder, source: (0, path_1.join)(source, path), parent, stats: entry.stats };
341
353
  // masks containing '/' must be matched against the relative path while keeping walkDir recursion enabled
342
354
  await pathMaskApplier(item, renamed || path);
343
355
  if (await cantSee(item)) // can't see: don't produce and don't recur
package/src/walkDir.js CHANGED
@@ -56,7 +56,13 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
56
56
  work(Object.assign(Object.create(direntMethods), {
57
57
  isDir: f.IS_DIRECTORY,
58
58
  name: f.LONG_NAME,
59
- stats: { size: f.SIZE, birthtime: f.CREATION_TIME, mtime: f.LAST_WRITE_TIME }
59
+ stats: {
60
+ size: f.SIZE,
61
+ birthtime: f.CREATION_TIME, birthtimeMs: f.CREATION_TIME.getTime(),
62
+ mtime: f.LAST_WRITE_TIME, mtimeMs: f.LAST_WRITE_TIME.getTime(),
63
+ isFile: () => !f.IS_DIRECTORY,
64
+ isDirectory: () => f.IS_DIRECTORY,
65
+ }
60
66
  }));
61
67
  }, true));
62
68
  }
package/src/webdav.js CHANGED
@@ -23,6 +23,7 @@ const config_1 = require("./config");
23
23
  const expiringCache_1 = require("./expiringCache");
24
24
  const fast_xml_parser_1 = require("fast-xml-parser");
25
25
  const lodash_1 = __importDefault(require("lodash"));
26
+ const fileAttr_1 = require("./fileAttr");
26
27
  const forceWebdavLogin = (0, config_1.defineConfig)(cross_1.CFG.force_webdav_login, true, compileWebdavAgentRegex);
27
28
  const webdavInitialAuth = (0, config_1.defineConfig)(cross_1.CFG.webdav_initial_auth, 'WebDAVFS', compileWebdavAgentRegex);
28
29
  const webdavPrompted = (0, expiringCache_1.expiringCache)(cross_1.DAY);
@@ -148,7 +149,9 @@ const webdav = async (ctx, next) => {
148
149
  canOverwrite.delete(overwriteGraceKey);
149
150
  const node = await (0, vfs_1.urlToNode)(path, ctx);
150
151
  if (node?.source)
151
- await (0, promises_1.rm)(node.source).catch(() => { });
152
+ await (0, promises_1.rm)(node.source)
153
+ .then(() => (0, fileAttr_1.deleteStoredFileAttrs)(node.source))
154
+ .catch(() => { });
152
155
  }
153
156
  if (x && ctx.length === undefined) // missing length can make PUT fail
154
157
  ctx.req.headers['content-length'] = x;
package/src/zip.js CHANGED
@@ -12,9 +12,10 @@ const serveFile_1 = require("./serveFile");
12
12
  const const_1 = require("./const");
13
13
  const api_get_file_list_1 = require("./api.get_file_list");
14
14
  const comments_1 = require("./comments");
15
+ const urlList_1 = require("./urlList");
15
16
  // expects 'node' to have had permissions checked by caller
16
17
  async function zipStreamFromFolder(node, ctx) {
17
- const list = (0, misc_1.wantArray)(ctx.query.list)[0]?.split('//'); // slash is the only char not allowed in file names both for windows and unix, but still we need to encode whole paths, so the only safe choice to separate the entries is the double slash
18
+ const list = (0, urlList_1.decodeUrlList)((0, misc_1.wantArray)(ctx.query.list)[0]);
18
19
  if (!list && (0, vfs_1.statusCodeForMissingPerm)(node, 'can_archive', ctx))
19
20
  return;
20
21
  ctx.status = const_1.HTTP_OK;
@@ -39,7 +40,7 @@ async function zipStreamFromFolder(node, ctx) {
39
40
  if ((0, vfs_1.nodeIsFolder)(subNode)) { // a directory needs to be walked
40
41
  if ((0, vfs_1.hasPermission)(subNode, 'can_list', ctx) && (0, vfs_1.hasPermission)(subNode, 'can_archive', ctx)) {
41
42
  yield subNode; // it could be empty
42
- yield* (0, vfs_1.walkNode)(subNode, { ctx, prefixPath: decodeURI(uri) + '/', requiredPerm: 'can_archive' });
43
+ yield* (0, vfs_1.walkNode)(subNode, { ctx, prefixPath: (0, misc_1.pathDecodeSegments)(uri) + '/', requiredPerm: 'can_archive' });
43
44
  }
44
45
  continue;
45
46
  }
@@ -1 +0,0 @@
1
- .ariaOnly{position:absolute;clip-path:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.icon{font-size:1.2em;height:1.2em;width:1.4em;display:inline-block;text-align:center}@keyframes blink{0%{opacity:1}50%{opacity:.3}}:root{height:100dvh;--success: #5c5}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;height:100vh;background:#000}#root{min-height:100%;display:flex}main{word-break:break-word}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}img.flag{width:1.5em;height:1em;vertical-align:text-top;margin-top:1px}.icon-w-text{margin-right:.5em}form{max-width:100%}.MuiSvgIcon-root{vertical-align:bottom}div.MuiTreeItem-content{padding:0}.MuiDataGrid-columnHeaders{background-color:#8882}.MuiDataGrid-cell{line-height:1.2em}.MuiDataGrid-filterForm{flex-wrap:wrap;justify-content:space-evenly;row-gap:1em}.wrap[role=cell]{white-space:normal!important}ol,ul{margin-top:.2em;margin-bottom:.2em;padding-left:1.5em}h2.MuiDialogTitle-root{padding-top:2px;padding-bottom:2px}.dialog-alert .MuiDialogContent-root{max-width:45em;padding:.5em 1em;white-space:pre-wrap}.release code{font-size:95%;background-color:#8883;padding:0 .2em;border-radius:.2em}.MuiAppBar-root .MuiAlert-standardInfo .MuiAlert-icon{color:#fff8!important}.MuiAppBar-root .MuiAlert-standardInfo .MuiAlert-message{color:#fffa!important}.MuiAppBar-root .MuiAlert-colorWarning .MuiAlert-icon{color:#ff08!important}.MuiAppBar-root .MuiAlert-colorWarning .MuiAlert-message{color:#ff0a!important}.animated-dashed-line{width:100%;height:2px;background-image:linear-gradient(to right,currentColor 50%,transparent 50%);background-size:10px 100%;animation:animate-dash 1s alternate linear infinite}@keyframes animate-dash{to{background-position:20px 0}}@keyframes success{50%{transform:scale(1.3);color:var(--success)}to{transform:inherit;color:inherit}}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:#8888}::-webkit-scrollbar-track{background:#8882}.token.operator{background:unset!important}.fflag{width:26px;height:17px;vertical-align:text-bottom;border:none}.dialog-backdrop{position:fixed;inset:0;background:#8886;backdrop-filter:blur(2px);display:flex;justify-content:center;align-items:center;z-index:1000}.dialog{background:#fff;background:var(--bg);padding:.5em;padding:max(.5em,1vw,2vh);padding-top:0;border-radius:1em;position:relative;margin:0 3vw;overflow:hidden;max-height:calc(100vh - 2em);display:flex;flex-direction:column;justify-content:center}.dialog-icon{color:#fff;background-color:var(--color);position:absolute;top:0;width:2em;height:1.8em;text-align:center;border-radius:.8em 0}.dialog-icon-text{display:flex;align-items:center;justify-content:center}.dialog-title{font-size:120%;font-weight:400;margin:.3em 0;padding:0 .5em;min-height:1.2em}.dialog-closer~.dialog-title{margin-right:2em}.dialog-type~.dialog-title{margin-left:2em}.dialog-icon~.dialog-title{text-align:center}.dialog-closer{border-radius:0 .8em;right:0;padding:0;background-color:#c88}.dialog-icon~.dialog-content{margin-top:1em}.dialog-type{left:0;top:0;overflow:hidden;opacity:.7}.dialog-content{overflow:auto;max-height:calc(100vh - 4.5em)}.dialog-content p{white-space:pre-wrap;margin:.5em 0}.dialog-confirm .dialog-content button{margin-top:1em}.dialog-alert-info{--color: #282 }.dialog-alert-warning{--color: #c91 }.dialog-alert-error{--color: #822}@media (max-width: 42em){.dialog-icon{font-size:120%}.dialog-icon~.dialog-content{margin-top:1.5em}}.dialog-prompt label{display:block;margin:.5em .1em}.toasts{position:fixed;right:0;display:flex;flex-direction:column;margin-top:.5em;gap:.5em;z-index:1001}.toast{background:var(--faint-contrast);color:var(--text-high-contrast);transition:all .5s ease-in;position:relative;left:-.5em;overflow:hidden;display:flex;align-items:center;padding:.3em .6em;border-radius:.5em;box-shadow:0 0 .3em .3em #8883;box-sizing:border-box}.toast .toast-icon{margin-right:.3em;animation:zoomRotating 1.5s}.toast.toast-success{background-color:var(--success)}.toast.toast-warning{background-color:var(--warning)}.toast.toast-error{background-color:var(--error)}.toast.before{left:100%;transform:scale(.1);padding-top:0;padding-bottom:0}.toast.after{height:0!important;padding-top:0;padding-bottom:0;transition-duration:.2s;transform:scale(0)}@keyframes zoomRotating{0%{transform:scale(0)}to{transform:scale(1) rotate(360deg)}}code[class*=language-],pre[class*=language-]{color:#657b83;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,code[class*=language-] ::-moz-selection{background:#073642}pre[class*=language-]::selection,pre[class*=language-] ::selection,code[class*=language-]::selection,code[class*=language-] ::selection{background:#073642}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background-color:#fdf6e3}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:#93a1a1}.token.punctuation{color:#586e75}.token.namespace{opacity:.7}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.deleted{color:#268bd2}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.url,.token.inserted{color:#2aa198}.token.entity{color:#657b83;background:#eee8d5}.token.atrule,.token.attr-value,.token.keyword{color:#859900}.token.function,.token.class-name{color:#b58900}.token.regex,.token.important,.token.variable{color:#cb4b16}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}