imapkit 4.0.2 → 4.1.0

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 (485) hide show
  1. package/README.md +32 -19
  2. package/bin/imapkit.js +12 -16
  3. package/dist/cjs/addressparser.d.ts +21 -0
  4. package/dist/cjs/addressparser.js +277 -0
  5. package/dist/cjs/arguments.d.ts +31 -0
  6. package/{lib → dist/cjs}/arguments.js +9 -21
  7. package/dist/cjs/bodystructure.d.ts +25 -0
  8. package/dist/cjs/bodystructure.js +150 -0
  9. package/dist/cjs/cert.d.ts +2 -0
  10. package/dist/cjs/cert.js +7 -0
  11. package/dist/cjs/command-states.d.ts +36 -0
  12. package/dist/cjs/command-states.js +102 -0
  13. package/dist/cjs/commands/append.d.ts +88 -0
  14. package/dist/cjs/commands/append.js +299 -0
  15. package/dist/cjs/commands/capability.d.ts +2 -0
  16. package/dist/cjs/commands/capability.js +44 -0
  17. package/dist/cjs/commands/check.d.ts +2 -0
  18. package/dist/cjs/commands/check.js +28 -0
  19. package/dist/cjs/commands/close.d.ts +2 -0
  20. package/dist/cjs/commands/close.js +35 -0
  21. package/dist/cjs/commands/copy.d.ts +30 -0
  22. package/dist/cjs/commands/copy.js +107 -0
  23. package/dist/cjs/commands/create.d.ts +2 -0
  24. package/dist/cjs/commands/create.js +53 -0
  25. package/dist/cjs/commands/delete.d.ts +2 -0
  26. package/dist/cjs/commands/delete.js +63 -0
  27. package/dist/cjs/commands/examine.d.ts +2 -0
  28. package/dist/cjs/commands/examine.js +19 -0
  29. package/dist/cjs/commands/expunge.d.ts +2 -0
  30. package/dist/cjs/commands/expunge.js +32 -0
  31. package/dist/cjs/commands/fetch.d.ts +23 -0
  32. package/dist/cjs/commands/fetch.js +208 -0
  33. package/dist/cjs/commands/handlers/fetch.d.ts +12 -0
  34. package/dist/cjs/commands/handlers/fetch.js +195 -0
  35. package/dist/cjs/commands/handlers/flags.d.ts +17 -0
  36. package/dist/cjs/commands/handlers/flags.js +39 -0
  37. package/dist/cjs/commands/handlers/search.d.ts +85 -0
  38. package/dist/cjs/commands/handlers/search.js +504 -0
  39. package/dist/cjs/commands/handlers/status.d.ts +34 -0
  40. package/dist/cjs/commands/handlers/status.js +81 -0
  41. package/dist/cjs/commands/handlers/store.d.ts +3 -0
  42. package/dist/cjs/commands/handlers/store.js +117 -0
  43. package/dist/cjs/commands/index.d.ts +2 -0
  44. package/dist/cjs/commands/index.js +61 -0
  45. package/dist/cjs/commands/list.d.ts +2 -0
  46. package/dist/cjs/commands/list.js +86 -0
  47. package/dist/cjs/commands/login.d.ts +2 -0
  48. package/dist/cjs/commands/login.js +62 -0
  49. package/dist/cjs/commands/logout.d.ts +2 -0
  50. package/dist/cjs/commands/logout.js +40 -0
  51. package/dist/cjs/commands/lsub.d.ts +2 -0
  52. package/dist/cjs/commands/lsub.js +79 -0
  53. package/dist/cjs/commands/noop.d.ts +2 -0
  54. package/dist/cjs/commands/noop.js +28 -0
  55. package/dist/cjs/commands/rename.d.ts +2 -0
  56. package/dist/cjs/commands/rename.js +98 -0
  57. package/dist/cjs/commands/search.d.ts +10 -0
  58. package/dist/cjs/commands/search.js +71 -0
  59. package/dist/cjs/commands/select.d.ts +19 -0
  60. package/dist/cjs/commands/select.js +230 -0
  61. package/dist/cjs/commands/status.d.ts +2 -0
  62. package/dist/cjs/commands/status.js +57 -0
  63. package/dist/cjs/commands/store.d.ts +10 -0
  64. package/dist/cjs/commands/store.js +134 -0
  65. package/dist/cjs/commands/subscribe.d.ts +2 -0
  66. package/dist/cjs/commands/subscribe.js +51 -0
  67. package/dist/cjs/commands/uid-copy.d.ts +2 -0
  68. package/dist/cjs/commands/uid-copy.js +19 -0
  69. package/dist/cjs/commands/uid-fetch.d.ts +3 -0
  70. package/dist/cjs/commands/uid-fetch.js +17 -0
  71. package/dist/cjs/commands/uid-search.d.ts +3 -0
  72. package/dist/cjs/commands/uid-search.js +17 -0
  73. package/dist/cjs/commands/uid-store.d.ts +3 -0
  74. package/dist/cjs/commands/uid-store.js +17 -0
  75. package/dist/cjs/commands/unsubscribe.d.ts +2 -0
  76. package/dist/cjs/commands/unsubscribe.js +50 -0
  77. package/dist/cjs/dates.d.ts +64 -0
  78. package/dist/cjs/dates.js +124 -0
  79. package/dist/cjs/deflate-layer.d.ts +83 -0
  80. package/dist/cjs/deflate-layer.js +232 -0
  81. package/dist/cjs/encoded-words.d.ts +15 -0
  82. package/dist/cjs/encoded-words.js +85 -0
  83. package/dist/cjs/envelope.d.ts +33 -0
  84. package/dist/cjs/envelope.js +86 -0
  85. package/dist/cjs/esearch.d.ts +96 -0
  86. package/dist/cjs/esearch.js +190 -0
  87. package/dist/cjs/framing.d.ts +66 -0
  88. package/{lib → dist/cjs}/framing.js +9 -26
  89. package/dist/cjs/index.d.ts +9 -0
  90. package/dist/cjs/index.js +56 -0
  91. package/dist/cjs/list-extensions.d.ts +50 -0
  92. package/dist/cjs/list-extensions.js +35 -0
  93. package/dist/cjs/load-plugins.d.ts +21 -0
  94. package/dist/cjs/load-plugins.js +89 -0
  95. package/dist/cjs/mailbox-name.d.ts +26 -0
  96. package/dist/cjs/mailbox-name.js +127 -0
  97. package/dist/cjs/mimeparser.d.ts +137 -0
  98. package/dist/cjs/mimeparser.js +727 -0
  99. package/dist/cjs/mock-client.d.ts +39 -0
  100. package/dist/cjs/mock-client.js +236 -0
  101. package/dist/cjs/numbers.d.ts +28 -0
  102. package/dist/cjs/numbers.js +52 -0
  103. package/dist/cjs/package.json +3 -0
  104. package/dist/cjs/plugins/acl.d.ts +2 -0
  105. package/dist/cjs/plugins/acl.js +888 -0
  106. package/dist/cjs/plugins/appendlimit.d.ts +16 -0
  107. package/dist/cjs/plugins/appendlimit.js +87 -0
  108. package/dist/cjs/plugins/auth-plain.d.ts +2 -0
  109. package/dist/cjs/plugins/auth-plain.js +89 -0
  110. package/dist/cjs/plugins/binary.d.ts +30 -0
  111. package/dist/cjs/plugins/binary.js +247 -0
  112. package/dist/cjs/plugins/catenate.d.ts +14 -0
  113. package/dist/cjs/plugins/catenate.js +250 -0
  114. package/dist/cjs/plugins/compress.d.ts +7 -0
  115. package/dist/cjs/plugins/compress.js +79 -0
  116. package/dist/cjs/plugins/condstore.d.ts +5 -0
  117. package/dist/cjs/plugins/condstore.js +492 -0
  118. package/dist/cjs/plugins/context-search.d.ts +8 -0
  119. package/dist/cjs/plugins/context-search.js +304 -0
  120. package/dist/cjs/plugins/context-sort.d.ts +6 -0
  121. package/dist/cjs/plugins/context-sort.js +32 -0
  122. package/dist/cjs/plugins/create-special-use.d.ts +7 -0
  123. package/dist/cjs/plugins/create-special-use.js +101 -0
  124. package/dist/cjs/plugins/enable.d.ts +23 -0
  125. package/dist/cjs/plugins/enable.js +127 -0
  126. package/dist/cjs/plugins/esearch.d.ts +6 -0
  127. package/dist/cjs/plugins/esearch.js +142 -0
  128. package/dist/cjs/plugins/esort.d.ts +6 -0
  129. package/dist/cjs/plugins/esort.js +60 -0
  130. package/dist/cjs/plugins/id.d.ts +5 -0
  131. package/dist/cjs/plugins/id.js +123 -0
  132. package/dist/cjs/plugins/idle.d.ts +5 -0
  133. package/dist/cjs/plugins/idle.js +85 -0
  134. package/dist/cjs/plugins/imap4rev2.d.ts +6 -0
  135. package/dist/cjs/plugins/imap4rev2.js +184 -0
  136. package/dist/cjs/plugins/index.d.ts +2 -0
  137. package/dist/cjs/plugins/index.js +119 -0
  138. package/dist/cjs/plugins/list-extended.d.ts +2 -0
  139. package/dist/cjs/plugins/list-extended.js +235 -0
  140. package/dist/cjs/plugins/list-status.d.ts +6 -0
  141. package/dist/cjs/plugins/list-status.js +42 -0
  142. package/dist/cjs/plugins/literalminus.d.ts +8 -0
  143. package/dist/cjs/plugins/literalminus.js +32 -0
  144. package/dist/cjs/plugins/literalplus.d.ts +6 -0
  145. package/dist/cjs/plugins/literalplus.js +30 -0
  146. package/dist/cjs/plugins/logindisabled.d.ts +5 -0
  147. package/dist/cjs/plugins/logindisabled.js +54 -0
  148. package/dist/cjs/plugins/messagelimit.d.ts +19 -0
  149. package/dist/cjs/plugins/messagelimit.js +227 -0
  150. package/dist/cjs/plugins/metadata-server.d.ts +7 -0
  151. package/dist/cjs/plugins/metadata-server.js +24 -0
  152. package/dist/cjs/plugins/metadata.d.ts +11 -0
  153. package/dist/cjs/plugins/metadata.js +436 -0
  154. package/dist/cjs/plugins/move.d.ts +11 -0
  155. package/dist/cjs/plugins/move.js +96 -0
  156. package/dist/cjs/plugins/multiappend.d.ts +12 -0
  157. package/dist/cjs/plugins/multiappend.js +38 -0
  158. package/dist/cjs/plugins/multisearch.d.ts +6 -0
  159. package/dist/cjs/plugins/multisearch.js +263 -0
  160. package/dist/cjs/plugins/namespace.d.ts +5 -0
  161. package/dist/cjs/plugins/namespace.js +57 -0
  162. package/dist/cjs/plugins/notify.d.ts +2 -0
  163. package/dist/cjs/plugins/notify.js +628 -0
  164. package/dist/cjs/plugins/oauthbearer.d.ts +32 -0
  165. package/dist/cjs/plugins/oauthbearer.js +202 -0
  166. package/dist/cjs/plugins/objectid.d.ts +23 -0
  167. package/dist/cjs/plugins/objectid.js +221 -0
  168. package/dist/cjs/plugins/partial.d.ts +6 -0
  169. package/dist/cjs/plugins/partial.js +78 -0
  170. package/dist/cjs/plugins/preview.d.ts +31 -0
  171. package/dist/cjs/plugins/preview.js +378 -0
  172. package/dist/cjs/plugins/qresync.d.ts +11 -0
  173. package/dist/cjs/plugins/qresync.js +473 -0
  174. package/dist/cjs/plugins/quota.d.ts +18 -0
  175. package/dist/cjs/plugins/quota.js +254 -0
  176. package/dist/cjs/plugins/replace.d.ts +14 -0
  177. package/dist/cjs/plugins/replace.js +130 -0
  178. package/dist/cjs/plugins/sasl-ir.d.ts +5 -0
  179. package/dist/cjs/plugins/sasl-ir.js +24 -0
  180. package/dist/cjs/plugins/savedate.d.ts +17 -0
  181. package/dist/cjs/plugins/savedate.js +64 -0
  182. package/dist/cjs/plugins/savelimit.d.ts +10 -0
  183. package/dist/cjs/plugins/savelimit.js +30 -0
  184. package/dist/cjs/plugins/searchres.d.ts +6 -0
  185. package/dist/cjs/plugins/searchres.js +91 -0
  186. package/dist/cjs/plugins/sort-display.d.ts +11 -0
  187. package/dist/cjs/plugins/sort-display.js +37 -0
  188. package/dist/cjs/plugins/sort.d.ts +11 -0
  189. package/dist/cjs/plugins/sort.js +125 -0
  190. package/dist/cjs/plugins/special-use.d.ts +10 -0
  191. package/dist/cjs/plugins/special-use.js +95 -0
  192. package/dist/cjs/plugins/starttls.d.ts +5 -0
  193. package/dist/cjs/plugins/starttls.js +55 -0
  194. package/dist/cjs/plugins/status-size.d.ts +7 -0
  195. package/dist/cjs/plugins/status-size.js +30 -0
  196. package/dist/cjs/plugins/thread-orderedsubject.d.ts +11 -0
  197. package/dist/cjs/plugins/thread-orderedsubject.js +28 -0
  198. package/dist/cjs/plugins/thread-references.d.ts +11 -0
  199. package/dist/cjs/plugins/thread-references.js +28 -0
  200. package/dist/cjs/plugins/uidonly.d.ts +15 -0
  201. package/dist/cjs/plugins/uidonly.js +126 -0
  202. package/dist/cjs/plugins/uidplus.d.ts +15 -0
  203. package/dist/cjs/plugins/uidplus.js +119 -0
  204. package/dist/cjs/plugins/unauthenticate.d.ts +7 -0
  205. package/dist/cjs/plugins/unauthenticate.js +32 -0
  206. package/dist/cjs/plugins/unselect.d.ts +5 -0
  207. package/dist/cjs/plugins/unselect.js +36 -0
  208. package/dist/cjs/plugins/utf8-accept.d.ts +6 -0
  209. package/dist/cjs/plugins/utf8-accept.js +72 -0
  210. package/dist/cjs/plugins/x-gm-ext-1.d.ts +26 -0
  211. package/dist/cjs/plugins/x-gm-ext-1.js +422 -0
  212. package/dist/cjs/plugins/xoauth2.d.ts +2 -0
  213. package/dist/cjs/plugins/xoauth2.js +155 -0
  214. package/dist/cjs/plugins/xtoybird.d.ts +2 -0
  215. package/dist/cjs/plugins/xtoybird.js +236 -0
  216. package/dist/cjs/server.d.ts +876 -0
  217. package/dist/cjs/server.js +2597 -0
  218. package/dist/cjs/smtp-listener.d.ts +13 -0
  219. package/dist/cjs/smtp-listener.js +47 -0
  220. package/dist/cjs/sorting.d.ts +77 -0
  221. package/dist/cjs/sorting.js +290 -0
  222. package/dist/cjs/threading.d.ts +30 -0
  223. package/dist/cjs/threading.js +318 -0
  224. package/dist/cjs/types.d.ts +327 -0
  225. package/dist/cjs/types.js +3 -0
  226. package/dist/cjs/utf8-session.d.ts +17 -0
  227. package/dist/cjs/utf8-session.js +148 -0
  228. package/dist/cjs/vanished.d.ts +21 -0
  229. package/dist/cjs/vanished.js +55 -0
  230. package/dist/esm/addressparser.d.ts +21 -0
  231. package/{lib → dist/esm}/addressparser.js +102 -123
  232. package/dist/esm/arguments.d.ts +31 -0
  233. package/dist/esm/arguments.js +96 -0
  234. package/dist/esm/bodystructure.d.ts +25 -0
  235. package/{lib → dist/esm}/bodystructure.js +17 -33
  236. package/dist/esm/cert.d.ts +2 -0
  237. package/dist/esm/cert.js +4 -0
  238. package/dist/esm/command-states.d.ts +36 -0
  239. package/{lib → dist/esm}/command-states.js +4 -16
  240. package/dist/esm/commands/append.d.ts +88 -0
  241. package/{lib → dist/esm}/commands/append.js +47 -85
  242. package/dist/esm/commands/capability.d.ts +2 -0
  243. package/dist/esm/commands/capability.js +29 -0
  244. package/dist/esm/commands/check.d.ts +2 -0
  245. package/dist/esm/commands/check.js +13 -0
  246. package/dist/esm/commands/close.d.ts +2 -0
  247. package/dist/esm/commands/close.js +20 -0
  248. package/dist/esm/commands/copy.d.ts +30 -0
  249. package/{lib → dist/esm}/commands/copy.js +31 -55
  250. package/dist/esm/commands/create.d.ts +2 -0
  251. package/{lib → dist/esm}/commands/create.js +25 -39
  252. package/dist/esm/commands/delete.d.ts +2 -0
  253. package/{lib → dist/esm}/commands/delete.js +25 -41
  254. package/dist/esm/commands/examine.d.ts +2 -0
  255. package/dist/esm/commands/examine.js +4 -0
  256. package/dist/esm/commands/expunge.d.ts +2 -0
  257. package/dist/esm/commands/expunge.js +17 -0
  258. package/dist/esm/commands/fetch.d.ts +23 -0
  259. package/{lib → dist/esm}/commands/fetch.js +34 -75
  260. package/dist/esm/commands/handlers/fetch.d.ts +12 -0
  261. package/{lib → dist/esm}/commands/handlers/fetch.js +11 -42
  262. package/dist/esm/commands/handlers/flags.d.ts +17 -0
  263. package/{lib → dist/esm}/commands/handlers/flags.js +3 -11
  264. package/dist/esm/commands/handlers/search.d.ts +85 -0
  265. package/{lib → dist/esm}/commands/handlers/search.js +33 -71
  266. package/dist/esm/commands/handlers/status.d.ts +34 -0
  267. package/{lib → dist/esm}/commands/handlers/status.js +1 -9
  268. package/dist/esm/commands/handlers/store.d.ts +3 -0
  269. package/{lib → dist/esm}/commands/handlers/store.js +20 -44
  270. package/dist/esm/commands/index.d.ts +2 -0
  271. package/dist/esm/commands/index.js +55 -0
  272. package/dist/esm/commands/list.d.ts +2 -0
  273. package/dist/esm/commands/list.js +71 -0
  274. package/dist/esm/commands/login.d.ts +2 -0
  275. package/{lib → dist/esm}/commands/login.js +24 -44
  276. package/dist/esm/commands/logout.d.ts +2 -0
  277. package/dist/esm/commands/logout.js +25 -0
  278. package/dist/esm/commands/lsub.d.ts +2 -0
  279. package/dist/esm/commands/lsub.js +64 -0
  280. package/dist/esm/commands/noop.d.ts +2 -0
  281. package/dist/esm/commands/noop.js +13 -0
  282. package/dist/esm/commands/rename.d.ts +2 -0
  283. package/{lib → dist/esm}/commands/rename.js +32 -51
  284. package/dist/esm/commands/search.d.ts +10 -0
  285. package/dist/esm/commands/search.js +54 -0
  286. package/dist/esm/commands/select.d.ts +19 -0
  287. package/dist/esm/commands/select.js +214 -0
  288. package/dist/esm/commands/status.d.ts +2 -0
  289. package/{lib → dist/esm}/commands/status.js +18 -39
  290. package/dist/esm/commands/store.d.ts +10 -0
  291. package/{lib → dist/esm}/commands/store.js +39 -73
  292. package/dist/esm/commands/subscribe.d.ts +2 -0
  293. package/{lib → dist/esm}/commands/subscribe.js +22 -39
  294. package/dist/esm/commands/uid-copy.d.ts +2 -0
  295. package/dist/esm/commands/uid-copy.js +4 -0
  296. package/dist/esm/commands/uid-fetch.d.ts +3 -0
  297. package/dist/esm/commands/uid-fetch.js +3 -0
  298. package/dist/esm/commands/uid-search.d.ts +3 -0
  299. package/dist/esm/commands/uid-search.js +3 -0
  300. package/dist/esm/commands/uid-store.d.ts +3 -0
  301. package/dist/esm/commands/uid-store.js +3 -0
  302. package/dist/esm/commands/unsubscribe.d.ts +2 -0
  303. package/{lib → dist/esm}/commands/unsubscribe.js +22 -37
  304. package/dist/esm/dates.d.ts +64 -0
  305. package/{lib → dist/esm}/dates.js +6 -14
  306. package/dist/esm/deflate-layer.d.ts +83 -0
  307. package/{lib → dist/esm}/deflate-layer.js +9 -26
  308. package/dist/esm/encoded-words.d.ts +15 -0
  309. package/{lib → dist/esm}/encoded-words.js +9 -25
  310. package/dist/esm/envelope.d.ts +33 -0
  311. package/{lib → dist/esm}/envelope.js +8 -19
  312. package/dist/esm/esearch.d.ts +96 -0
  313. package/{lib → dist/esm}/esearch.js +12 -39
  314. package/dist/esm/framing.d.ts +66 -0
  315. package/dist/esm/framing.js +82 -0
  316. package/dist/esm/index.d.ts +9 -0
  317. package/dist/esm/index.js +6 -0
  318. package/dist/esm/list-extensions.d.ts +50 -0
  319. package/{lib → dist/esm}/list-extensions.js +1 -4
  320. package/dist/esm/load-plugins.d.ts +21 -0
  321. package/{lib → dist/esm}/load-plugins.js +9 -43
  322. package/dist/esm/mailbox-name.d.ts +26 -0
  323. package/{lib → dist/esm}/mailbox-name.js +5 -26
  324. package/dist/esm/mimeparser.d.ts +137 -0
  325. package/{lib → dist/esm}/mimeparser.js +74 -152
  326. package/dist/esm/mock-client.d.ts +39 -0
  327. package/{lib → dist/esm}/mock-client.js +26 -40
  328. package/dist/esm/numbers.d.ts +28 -0
  329. package/{lib → dist/esm}/numbers.js +1 -8
  330. package/dist/esm/package.json +3 -0
  331. package/dist/esm/plugins/acl.d.ts +2 -0
  332. package/{lib → dist/esm}/plugins/acl.js +146 -240
  333. package/dist/esm/plugins/appendlimit.d.ts +16 -0
  334. package/{lib → dist/esm}/plugins/appendlimit.js +5 -16
  335. package/dist/esm/plugins/auth-plain.d.ts +2 -0
  336. package/{lib → dist/esm}/plugins/auth-plain.js +11 -31
  337. package/dist/esm/plugins/binary.d.ts +30 -0
  338. package/{lib → dist/esm}/plugins/binary.js +17 -43
  339. package/dist/esm/plugins/catenate.d.ts +14 -0
  340. package/{lib → dist/esm}/plugins/catenate.js +19 -40
  341. package/dist/esm/plugins/compress.d.ts +7 -0
  342. package/dist/esm/plugins/compress.js +61 -0
  343. package/dist/esm/plugins/condstore.d.ts +5 -0
  344. package/{lib → dist/esm}/plugins/condstore.js +66 -152
  345. package/dist/esm/plugins/context-search.d.ts +8 -0
  346. package/{lib → dist/esm}/plugins/context-search.js +53 -88
  347. package/dist/esm/plugins/context-sort.d.ts +6 -0
  348. package/{lib → dist/esm}/plugins/context-sort.js +4 -9
  349. package/dist/esm/plugins/create-special-use.d.ts +7 -0
  350. package/{lib → dist/esm}/plugins/create-special-use.js +33 -55
  351. package/dist/esm/plugins/enable.d.ts +23 -0
  352. package/dist/esm/plugins/enable.js +111 -0
  353. package/dist/esm/plugins/esearch.d.ts +6 -0
  354. package/{lib → dist/esm}/plugins/esearch.js +13 -42
  355. package/dist/esm/plugins/esort.d.ts +6 -0
  356. package/{lib → dist/esm}/plugins/esort.js +7 -25
  357. package/dist/esm/plugins/id.d.ts +5 -0
  358. package/dist/esm/plugins/id.js +108 -0
  359. package/dist/esm/plugins/idle.d.ts +5 -0
  360. package/dist/esm/plugins/idle.js +70 -0
  361. package/dist/esm/plugins/imap4rev2.d.ts +6 -0
  362. package/{lib → dist/esm}/plugins/imap4rev2.js +24 -60
  363. package/dist/esm/plugins/index.d.ts +2 -0
  364. package/dist/esm/plugins/index.js +113 -0
  365. package/dist/esm/plugins/list-extended.d.ts +2 -0
  366. package/{lib → dist/esm}/plugins/list-extended.js +28 -66
  367. package/dist/esm/plugins/list-status.d.ts +6 -0
  368. package/{lib → dist/esm}/plugins/list-status.js +5 -12
  369. package/dist/esm/plugins/literalminus.d.ts +8 -0
  370. package/{lib → dist/esm}/plugins/literalminus.js +2 -5
  371. package/dist/esm/plugins/literalplus.d.ts +6 -0
  372. package/{lib → dist/esm}/plugins/literalplus.js +2 -5
  373. package/dist/esm/plugins/logindisabled.d.ts +5 -0
  374. package/dist/esm/plugins/logindisabled.js +39 -0
  375. package/dist/esm/plugins/messagelimit.d.ts +19 -0
  376. package/{lib → dist/esm}/plugins/messagelimit.js +18 -41
  377. package/dist/esm/plugins/metadata-server.d.ts +7 -0
  378. package/{lib → dist/esm}/plugins/metadata-server.js +3 -7
  379. package/dist/esm/plugins/metadata.d.ts +11 -0
  380. package/dist/esm/plugins/metadata.js +421 -0
  381. package/dist/esm/plugins/move.d.ts +11 -0
  382. package/{lib → dist/esm}/plugins/move.js +37 -66
  383. package/dist/esm/plugins/multiappend.d.ts +12 -0
  384. package/{lib → dist/esm}/plugins/multiappend.js +2 -5
  385. package/dist/esm/plugins/multisearch.d.ts +6 -0
  386. package/{lib → dist/esm}/plugins/multisearch.js +24 -48
  387. package/dist/esm/plugins/namespace.d.ts +5 -0
  388. package/dist/esm/plugins/namespace.js +42 -0
  389. package/dist/esm/plugins/notify.d.ts +2 -0
  390. package/{lib → dist/esm}/plugins/notify.js +90 -134
  391. package/dist/esm/plugins/oauthbearer.d.ts +32 -0
  392. package/{lib → dist/esm}/plugins/oauthbearer.js +18 -48
  393. package/dist/esm/plugins/objectid.d.ts +23 -0
  394. package/{lib → dist/esm}/plugins/objectid.js +21 -58
  395. package/dist/esm/plugins/partial.d.ts +6 -0
  396. package/{lib → dist/esm}/plugins/partial.js +7 -15
  397. package/dist/esm/plugins/preview.d.ts +31 -0
  398. package/{lib → dist/esm}/plugins/preview.js +28 -66
  399. package/dist/esm/plugins/qresync.d.ts +11 -0
  400. package/{lib → dist/esm}/plugins/qresync.js +64 -134
  401. package/dist/esm/plugins/quota.d.ts +18 -0
  402. package/{lib → dist/esm}/plugins/quota.js +89 -135
  403. package/dist/esm/plugins/replace.d.ts +14 -0
  404. package/{lib → dist/esm}/plugins/replace.js +21 -51
  405. package/dist/esm/plugins/sasl-ir.d.ts +5 -0
  406. package/{lib → dist/esm}/plugins/sasl-ir.js +3 -6
  407. package/dist/esm/plugins/savedate.d.ts +17 -0
  408. package/{lib → dist/esm}/plugins/savedate.js +5 -15
  409. package/dist/esm/plugins/savelimit.d.ts +10 -0
  410. package/{lib → dist/esm}/plugins/savelimit.js +3 -6
  411. package/dist/esm/plugins/searchres.d.ts +6 -0
  412. package/{lib → dist/esm}/plugins/searchres.js +13 -22
  413. package/dist/esm/plugins/sort-display.d.ts +11 -0
  414. package/{lib → dist/esm}/plugins/sort-display.js +6 -10
  415. package/dist/esm/plugins/sort.d.ts +11 -0
  416. package/{lib → dist/esm}/plugins/sort.js +24 -46
  417. package/dist/esm/plugins/special-use.d.ts +10 -0
  418. package/{lib → dist/esm}/plugins/special-use.js +9 -24
  419. package/dist/esm/plugins/starttls.d.ts +5 -0
  420. package/{lib → dist/esm}/plugins/starttls.js +20 -37
  421. package/dist/esm/plugins/status-size.d.ts +7 -0
  422. package/{lib → dist/esm}/plugins/status-size.js +4 -11
  423. package/dist/esm/plugins/thread-orderedsubject.d.ts +11 -0
  424. package/{lib → dist/esm}/plugins/thread-orderedsubject.js +3 -6
  425. package/dist/esm/plugins/thread-references.d.ts +11 -0
  426. package/{lib → dist/esm}/plugins/thread-references.js +3 -6
  427. package/dist/esm/plugins/uidonly.d.ts +15 -0
  428. package/{lib → dist/esm}/plugins/uidonly.js +11 -35
  429. package/dist/esm/plugins/uidplus.d.ts +15 -0
  430. package/{lib → dist/esm}/plugins/uidplus.js +25 -45
  431. package/dist/esm/plugins/unauthenticate.d.ts +7 -0
  432. package/dist/esm/plugins/unauthenticate.js +17 -0
  433. package/dist/esm/plugins/unselect.d.ts +5 -0
  434. package/dist/esm/plugins/unselect.js +21 -0
  435. package/dist/esm/plugins/utf8-accept.d.ts +6 -0
  436. package/{lib → dist/esm}/plugins/utf8-accept.js +10 -21
  437. package/dist/esm/plugins/x-gm-ext-1.d.ts +26 -0
  438. package/{lib → dist/esm}/plugins/x-gm-ext-1.js +48 -100
  439. package/dist/esm/plugins/xoauth2.d.ts +2 -0
  440. package/dist/esm/plugins/xoauth2.js +140 -0
  441. package/dist/esm/plugins/xtoybird.d.ts +2 -0
  442. package/dist/esm/plugins/xtoybird.js +218 -0
  443. package/dist/esm/server.d.ts +876 -0
  444. package/dist/esm/server.js +2543 -0
  445. package/dist/esm/smtp-listener.d.ts +13 -0
  446. package/{lib → dist/esm}/smtp-listener.js +5 -12
  447. package/dist/esm/sorting.d.ts +77 -0
  448. package/{lib → dist/esm}/sorting.js +40 -53
  449. package/dist/esm/threading.d.ts +30 -0
  450. package/{lib → dist/esm}/threading.js +29 -70
  451. package/dist/esm/types.d.ts +327 -0
  452. package/dist/esm/types.js +2 -0
  453. package/dist/esm/utf8-session.d.ts +17 -0
  454. package/{lib → dist/esm}/utf8-session.js +15 -25
  455. package/dist/esm/vanished.d.ts +21 -0
  456. package/{lib → dist/esm}/vanished.js +16 -21
  457. package/dist/plugin-help.json +298 -0
  458. package/package.json +54 -11
  459. package/lib/commands/capability.js +0 -47
  460. package/lib/commands/check.js +0 -21
  461. package/lib/commands/close.js +0 -30
  462. package/lib/commands/examine.js +0 -7
  463. package/lib/commands/expunge.js +0 -27
  464. package/lib/commands/list.js +0 -100
  465. package/lib/commands/logout.js +0 -41
  466. package/lib/commands/lsub.js +0 -87
  467. package/lib/commands/noop.js +0 -21
  468. package/lib/commands/search.js +0 -76
  469. package/lib/commands/select.js +0 -289
  470. package/lib/commands/uid copy.js +0 -7
  471. package/lib/commands/uid fetch.js +0 -5
  472. package/lib/commands/uid search.js +0 -5
  473. package/lib/commands/uid store.js +0 -5
  474. package/lib/plugins/compress.js +0 -76
  475. package/lib/plugins/enable.js +0 -155
  476. package/lib/plugins/id.js +0 -138
  477. package/lib/plugins/idle.js +0 -105
  478. package/lib/plugins/logindisabled.js +0 -50
  479. package/lib/plugins/metadata.js +0 -475
  480. package/lib/plugins/namespace.js +0 -67
  481. package/lib/plugins/unauthenticate.js +0 -28
  482. package/lib/plugins/unselect.js +0 -36
  483. package/lib/plugins/xoauth2.js +0 -188
  484. package/lib/plugins/xtoybird.js +0 -282
  485. package/lib/server.js +0 -2880
@@ -0,0 +1,888 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = aclPlugin;
7
+ const command_states_js_1 = require("../command-states.js");
8
+ const flags_js_1 = require("../commands/handlers/flags.js");
9
+ const store_js_1 = __importDefault(require("../commands/handlers/store.js"));
10
+ const list_extensions_js_1 = require("../list-extensions.js");
11
+ const select_js_1 = require("../commands/select.js");
12
+ const append_js_1 = require("../commands/append.js");
13
+ const arguments_js_1 = require("../arguments.js");
14
+ /**
15
+ * @help Adds ACL [RFC4314] capability with RIGHTS=texk
16
+ * @help The owner (server option "aclOwner", default "testuser")
17
+ * @help has all rights on every mailbox. Other users get rights
18
+ * @help from the "acl" property of a mailbox in storage, e.g.
19
+ * @help {"otheruser": "lr", "anyone": "l"}, or from SETACL. With
20
+ * @help LIST-EXTENDED, LIST-MYRIGHTS [RFC8440] is supported as well
21
+ *
22
+ * ACL: https://www.rfc-editor.org/rfc/rfc4314
23
+ *
24
+ * LIST-MYRIGHTS: https://www.rfc-editor.org/rfc/rfc8440
25
+ *
26
+ * Additional commands:
27
+ * - SETACL, DELETEACL, GETACL, LISTRIGHTS, MYRIGHTS
28
+ * - the MYRIGHTS return option of LIST, when LIST-EXTENDED is loaded
29
+ *
30
+ * Users other than the owner can only do what their rights allow (RFC 4314 section 4).
31
+ */
32
+ // RFC 4314 section 2.1, in the order they are listed in responses
33
+ const RIGHTS = 'lrswipkxtea';
34
+ const ALL_RIGHTS = new Set(RIGHTS);
35
+ // RFC 4314 section 2.1.1: the obsolete "c" and "d" rights are macros. Like Dovecot, "c" is "kx"
36
+ // ("create" for servers that used "c" for DELETE) and "d" is "et"
37
+ const VIRTUAL_RIGHTS = { c: 'kx', d: 'et' };
38
+ // RFC 4314 section 5.2: with none of these the mailbox is READ-ONLY. All flags are shared between
39
+ // the users of a mailbox, so the "shared flag rights" are "s", "w" and "t"
40
+ const WRITE_RIGHTS = ['i', 'e', 's', 'w', 't'];
41
+ // RFC 4314 section 4: the rights for changing flags
42
+ const FLAG_RIGHTS = ['s', 'w', 't'];
43
+ // RFC 4314 section 4: MYRIGHTS needs any of these
44
+ const ANY_RIGHTS = ['l', 'r', 'i', 'k', 'x', 'a'];
45
+ const STORE_ITEMS = ['FLAGS', '+FLAGS', '-FLAGS', 'FLAGS.SILENT', '+FLAGS.SILENT', '-FLAGS.SILENT'];
46
+ /**
47
+ * Formats an astring for a response. The compiler sends an ATOM value that is not a valid atom as a
48
+ * quoted string or a literal, only a leading "\\" (allowed for flags) and NIL have to be kept away from it
49
+ *
50
+ * @param {String} value
51
+ * @return {Object|String}
52
+ */
53
+ function astring(value) {
54
+ // identifiers are unicode strings, sent as UTF-8 (RFC 4314 section 3)
55
+ value = Buffer.from(value, 'utf-8').toString('binary');
56
+ // an atom NIL would read back as NIL, not as an identifier
57
+ return value && value.charAt(0) !== '\\' && !/^NIL$/i.test(value) ? { type: 'ATOM', value } : value;
58
+ }
59
+ /**
60
+ * Creates an error that is answered with BAD
61
+ *
62
+ * @param {String} message Error text
63
+ * @return {Error}
64
+ */
65
+ function badInput(message) {
66
+ const err = new Error(message);
67
+ err.imapResponse = 'BAD';
68
+ return err;
69
+ }
70
+ /**
71
+ * Parses a rights string, the virtual "c" and "d" rights are expanded (RFC 4314 section 2.1.1)
72
+ *
73
+ * @param {String} value Rights, e.g. "lrswd"
74
+ * @return {Set} rights
75
+ * @throws {Error} BAD error for unknown rights (RFC 4314 section 3.1)
76
+ */
77
+ function parseRights(value) {
78
+ const rights = new Set();
79
+ for (const chr of value) {
80
+ if (ALL_RIGHTS.has(chr)) {
81
+ rights.add(chr);
82
+ }
83
+ else if (Object.hasOwn(VIRTUAL_RIGHTS, chr)) {
84
+ VIRTUAL_RIGHTS[chr].split('').forEach((right) => rights.add(right));
85
+ }
86
+ else if (/^[A-Z]$/.test(chr)) {
87
+ throw badInput('Uppercase rights are not allowed');
88
+ }
89
+ else {
90
+ throw badInput('The ' + (/^[\x21-\x7e]$/.test(chr) ? chr + ' ' : '') + 'right is not supported');
91
+ }
92
+ }
93
+ return rights;
94
+ }
95
+ /**
96
+ * Builds the attributes of an ACL, LISTRIGHTS or MYRIGHTS response. The mailbox name is a MAILBOX
97
+ * attribute, so each session gets it in the form it uses (modified UTF-7, or UTF-8 after ENABLE
98
+ * UTF8=ACCEPT)
99
+ *
100
+ * @param {Object} mailbox Mailbox object
101
+ * @param {Array} values Values that follow the mailbox name
102
+ * @return {Array} Response attributes
103
+ */
104
+ function mailboxResponse(mailbox, values) {
105
+ return [{ type: 'MAILBOX', value: mailbox.path }].concat(values.map(value => astring(value)));
106
+ }
107
+ /**
108
+ * Formats rights for ACL and MYRIGHTS responses. "c" and "d" are added when any of their
109
+ * member rights is set (RFC 4314 section 2.1.1)
110
+ *
111
+ * @param {Set} rights
112
+ * @return {String}
113
+ */
114
+ function formatRights(rights) {
115
+ let result = RIGHTS.split('')
116
+ .filter(right => rights.has(right))
117
+ .join('');
118
+ Object.keys(VIRTUAL_RIGHTS).forEach(virtual => {
119
+ if (VIRTUAL_RIGHTS[virtual].split('').some((right) => rights.has(right))) {
120
+ result += virtual;
121
+ }
122
+ });
123
+ return result;
124
+ }
125
+ /**
126
+ * Checks an identifier argument. RFC 4314 section 3 asks for SASLprep, which is not implemented,
127
+ * but an identifier that SASLprep would refuse (control characters, invalid UTF-8) or that is
128
+ * empty gets BAD, as does a lone "-" (section 2: "-" prefixes the identifier of a negative right)
129
+ *
130
+ * @param {String} identifier Identifier as a binary string
131
+ * @return {String|Boolean} error text, or false if the identifier is fine
132
+ */
133
+ function identifierError(identifier) {
134
+ if (!identifier || identifier === '-') {
135
+ return 'Empty identifier';
136
+ }
137
+ // eslint-disable-next-line no-control-regex
138
+ if (/[\x00-\x1f\x7f]/.test(identifier)) {
139
+ return 'Identifier contains control characters';
140
+ }
141
+ const decoded = Buffer.from(identifier, 'binary').toString('utf-8');
142
+ if (Buffer.from(decoded, 'utf-8').toString('binary') !== identifier) {
143
+ return 'Identifier is not valid UTF-8';
144
+ }
145
+ return false;
146
+ }
147
+ function aclPlugin(server) {
148
+ server.registerCapability('ACL');
149
+ // RFC 4314 section 5.1.1: the rights not defined in RFC 2086, MUST include "t", "e", "x" and "k"
150
+ server.registerCapability('RIGHTS=texk');
151
+ const owner = typeof server.options.aclOwner === 'string' ? server.options.aclOwner : 'testuser';
152
+ const isOwner = (connection) => connection.username === owner;
153
+ /**
154
+ * Returns the ACL of a mailbox as a Map of identifier to a Set of rights. The ACL from the
155
+ * storage (an object of identifier to rights string) is converted on first use
156
+ *
157
+ * @param {Object} mailbox Mailbox object
158
+ * @return {Map}
159
+ */
160
+ const getAcl = (mailbox) => {
161
+ if (!(mailbox.acl instanceof Map)) {
162
+ const acl = new Map();
163
+ const source = mailbox.acl && typeof mailbox.acl === 'object' ? mailbox.acl : {};
164
+ Object.keys(source).forEach(identifier => {
165
+ try {
166
+ acl.set(identifier, parseRights(String(source[identifier])));
167
+ }
168
+ catch (err) {
169
+ // not a client error, so this is answered with NO [SERVERBUG]
170
+ throw new Error('Invalid ACL in storage for ' + mailbox.path + ': ' + err.message, { cause: err });
171
+ }
172
+ });
173
+ mailbox.acl = acl;
174
+ }
175
+ return mailbox.acl;
176
+ };
177
+ /**
178
+ * Returns the rights a user has on a mailbox. The owner has all rights, other users get the
179
+ * union of the rights of their own identifier and "anyone", minus the negative rights of
180
+ * "-user" and "-anyone" (RFC 4314 section 2)
181
+ *
182
+ * @param {Object} connection IMAP connection of the user
183
+ * @param {Object} mailbox Mailbox object
184
+ * @param {Map} [acl] ACL to use instead of the current one of the mailbox
185
+ * @return {Set} rights
186
+ */
187
+ const getRights = (connection, mailbox, acl) => {
188
+ const username = connection.username;
189
+ if (username === owner) {
190
+ return ALL_RIGHTS;
191
+ }
192
+ acl = acl || getAcl(mailbox);
193
+ const identifiers = ['anyone'].concat(typeof username === 'string' && username ? username : []);
194
+ const rights = new Set();
195
+ identifiers.forEach(identifier => (acl.get(identifier) || []).forEach((right) => rights.add(right)));
196
+ identifiers.forEach(identifier => (acl.get('-' + identifier) || []).forEach((right) => rights.delete(right)));
197
+ return rights;
198
+ };
199
+ // for plugins that report only what a user may see (e.g. NOTIFY)
200
+ server.acl = { getRights, isOwner };
201
+ // Tells plugins that the ACL of a mailbox changed, with an `acl` event `(mailbox, previousAcl)`
202
+ const changeAcl = (mailbox, change) => {
203
+ const acl = getAcl(mailbox);
204
+ const previous = new Map(acl);
205
+ change(acl);
206
+ server.emit('acl', mailbox, previous);
207
+ };
208
+ // CATENATE (RFC 4469 section 5): reading a message through an IMAP URL needs the "r" right, a
209
+ // mailbox without "l" is reported like one that does not exist (RFC 4314 section 6)
210
+ server.urlAccessChecks.push((connection, mailbox) => {
211
+ const rights = getRights(connection, mailbox);
212
+ if (!rights.has('r')) {
213
+ return { text: rights.has('l') ? 'Permission denied' : 'Mailbox does not exist' };
214
+ }
215
+ return false;
216
+ });
217
+ // MULTISEARCH (RFC 7377 section 2.2): searching needs "r", and "l" for a mailbox that the client did not name.
218
+ // Other mailboxes are left out without an error
219
+ server.searchAccessChecks.push((connection, mailbox, named) => {
220
+ const rights = getRights(connection, mailbox);
221
+ return rights.has('r') && (named || rights.has('l'));
222
+ });
223
+ // Rights on the selected mailbox are taken when it is selected (RFC 4314 section 5.1.1 allows caching
224
+ // them), so the FLAGS and PERMANENTFLAGS a session got stay correct until the next SELECT
225
+ const getSelectedRights = (connection) => connection.aclRights || ALL_RIGHTS;
226
+ // RFC 4314 section 4: "t" for \Deleted, "s" for \Seen and "w" for all other flags
227
+ const flagRight = (flag) => {
228
+ flag = (0, flags_js_1.normalizeSystemFlag)(flag);
229
+ if (flag === '\\Deleted') {
230
+ return 't';
231
+ }
232
+ if (flag === '\\Seen') {
233
+ return 's';
234
+ }
235
+ return 'w';
236
+ };
237
+ // flags are strings or parsed atoms
238
+ const flagValue = (flag) => (typeof flag === 'string' ? flag : String((flag && flag.value) || ''));
239
+ const canSetFlag = (rights, flag) => rights.has(flagRight(flagValue(flag)));
240
+ const mailboxArgument = (parsed, position) => {
241
+ const attr = (parsed.attributes || [])[position];
242
+ return (0, arguments_js_1.isAstring)(attr) ? attr.value : false;
243
+ };
244
+ // the mailbox named by an argument, false if there is no such mailbox or the argument is invalid
245
+ const argumentMailbox = (parsed, position) => {
246
+ const name = mailboxArgument(parsed, position);
247
+ return (name !== false && server.getMailbox(name)) || false;
248
+ };
249
+ const sendNoPerm = (connection, parsed, data, description) => connection.sendStatus(parsed, data, 'NO', 'Permission denied', 'NOPERM', description);
250
+ /**
251
+ * Refuses a command for lack of rights. Without the "l" right the user gets the same error
252
+ * as for a mailbox that does not exist, so the existence of the mailbox is not disclosed
253
+ * (RFC 4314 section 6), otherwise NO [NOPERM] (RFC 5530 section 3)
254
+ */
255
+ const sendDenied = (connection, parsed, data, rights, description, notFound) => {
256
+ if (rights.has('l')) {
257
+ sendNoPerm(connection, parsed, data, description);
258
+ }
259
+ else if (notFound === 'TRYCREATE') {
260
+ connection.sendStatus(parsed, data, 'NO', 'Target mailbox does not exist', 'TRYCREATE', description);
261
+ }
262
+ else {
263
+ connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT', description);
264
+ }
265
+ };
266
+ /**
267
+ * Finds the nearest existing parent of a mailbox name (RFC 4314 section 4, CREATE)
268
+ *
269
+ * @param {String} path Mailbox path
270
+ * @return {Object|Boolean} parent mailbox or false if there is none
271
+ */
272
+ const getParent = (path) => {
273
+ for (let parent = server.getParentPath(path); parent; parent = server.getParentPath(parent)) {
274
+ const mailbox = server.getMailbox(parent);
275
+ if (mailbox) {
276
+ return mailbox;
277
+ }
278
+ }
279
+ return false;
280
+ };
281
+ // Checks for the ACL commands, true if the command can go ahead
282
+ const checkAclCommand = (connection, parsed, data, mailbox, isAllowed) => {
283
+ if (!mailbox) {
284
+ connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT');
285
+ return false;
286
+ }
287
+ const rights = getRights(connection, mailbox);
288
+ if (!isAllowed(rights)) {
289
+ sendDenied(connection, parsed, data, rights);
290
+ return false;
291
+ }
292
+ return true;
293
+ };
294
+ const hasAdmin = (rights) => rights.has('a');
295
+ /**
296
+ * Validates the arguments of an ACL command: `count` astrings, the first one a mailbox name and
297
+ * the second one (if any) an identifier
298
+ *
299
+ * @return {Boolean} true if the arguments are fine, otherwise BAD was sent
300
+ */
301
+ const checkArguments = (connection, parsed, data, count) => {
302
+ const args = parsed.attributes || [];
303
+ const command = parsed.command.toUpperCase();
304
+ if (args.length !== count || args.some(arg => !(0, arguments_js_1.isAstring)(arg))) {
305
+ connection.sendStatus(parsed, data, 'BAD', command + ' expects ' + (count === 1 ? 'a mailbox name' : count + ' string arguments'));
306
+ return false;
307
+ }
308
+ const error = count > 1 && identifierError(args[1].value);
309
+ if (error) {
310
+ // RFC 4314 section 3: an identifier that fails preparation is refused with BAD
311
+ connection.sendStatus(parsed, data, 'BAD', error);
312
+ return false;
313
+ }
314
+ if (count > 1) {
315
+ // user names and identifiers are unicode strings, whatever way the user logged in
316
+ args[1].value = Buffer.from(args[1].value, 'binary').toString('utf-8');
317
+ }
318
+ return true;
319
+ };
320
+ // RFC 4314 section 2: an implementation MAY force rights to always be granted to an identifier
321
+ const refuseOwnerIdentifier = (connection, parsed, data) => {
322
+ const identifier = parsed.attributes[1].value;
323
+ if (identifier !== owner && identifier !== '-' + owner) {
324
+ return false;
325
+ }
326
+ connection.sendStatus(parsed, data, 'NO', 'Rights of the mailbox owner can not be changed', 'CANNOT');
327
+ return true;
328
+ };
329
+ const aclCommand = (count, handler) => (connection, parsed, data, callback) => {
330
+ if (checkArguments(connection, parsed, data, count)) {
331
+ handler(connection, parsed, data, server.getMailbox(parsed.attributes[0].value));
332
+ }
333
+ callback();
334
+ };
335
+ const aclOptions = { states: command_states_js_1.states.AUTHENTICATED, mailboxArguments: [0] };
336
+ // RFC 4314 section 7: identifier and mod-rights are astrings
337
+ const identifierOptions = Object.assign({ astringArguments: [1] }, aclOptions);
338
+ // RFC 4314 section 3.8: myrights-data = "MYRIGHTS" SP mailbox SP rights
339
+ const sendMyRights = (connection, mailbox, parsed, data) => connection.send({ tag: '*', command: 'MYRIGHTS', attributes: mailboxResponse(mailbox, [formatRights(getRights(connection, mailbox))]) }, 'MYRIGHTS', parsed, data, mailbox);
340
+ // RFC 8440 section 3: the MYRIGHTS return option of an extended LIST. The registry is shared with
341
+ // LIST-EXTENDED, which accepts the option only when it is loaded, in any load order
342
+ const listExtensions = (0, list_extensions_js_1.getListExtensions)(server);
343
+ server.registerCapability('LIST-MYRIGHTS', () => listExtensions.enabled);
344
+ listExtensions.returnOptions.MYRIGHTS = {
345
+ // a MYRIGHTS response follows the LIST response of every listed mailbox that matches the
346
+ // selection criteria and exists, not the ones listed only for CHILDINFO or as \NonExistent
347
+ onItem: (connection, folder, value, info, parsed, data) => {
348
+ if (info.matched && info.exists) {
349
+ sendMyRights(connection, folder, parsed, data);
350
+ }
351
+ }
352
+ };
353
+ // RFC 4314 section 3.1
354
+ server.setCommandHandler('SETACL', aclCommand(3, (connection, parsed, data, mailbox) => {
355
+ const identifier = parsed.attributes[1].value;
356
+ const modification = parsed.attributes[2].value;
357
+ const mode = /^[+-]/.test(modification) ? modification.charAt(0) : '';
358
+ // an unrecognized right MUST cause BAD, before anything else is checked
359
+ const rights = parseRights(modification.substr(mode.length));
360
+ if (!checkAclCommand(connection, parsed, data, mailbox, hasAdmin) || refuseOwnerIdentifier(connection, parsed, data)) {
361
+ return;
362
+ }
363
+ changeAcl(mailbox, acl => {
364
+ const current = new Set(acl.get(identifier) || []);
365
+ if (mode === '+') {
366
+ rights.forEach(right => current.add(right));
367
+ }
368
+ else if (mode === '-') {
369
+ rights.forEach(right => current.delete(right));
370
+ }
371
+ const updated = mode ? current : rights;
372
+ if (updated.size) {
373
+ acl.set(identifier, updated);
374
+ }
375
+ else {
376
+ acl.delete(identifier);
377
+ }
378
+ });
379
+ connection.sendStatus(parsed, data, 'OK', 'Setacl complete');
380
+ }), Object.assign({ astringArguments: [1, 2] }, aclOptions));
381
+ // RFC 4314 section 3.2
382
+ server.setCommandHandler('DELETEACL', aclCommand(2, (connection, parsed, data, mailbox) => {
383
+ if (!checkAclCommand(connection, parsed, data, mailbox, hasAdmin) || refuseOwnerIdentifier(connection, parsed, data)) {
384
+ return;
385
+ }
386
+ changeAcl(mailbox, acl => acl.delete(parsed.attributes[1].value));
387
+ connection.sendStatus(parsed, data, 'OK', 'Deleteacl complete');
388
+ }), identifierOptions);
389
+ // RFC 4314 sections 3.3 and 3.6
390
+ server.setCommandHandler('GETACL', aclCommand(1, (connection, parsed, data, mailbox) => {
391
+ if (!checkAclCommand(connection, parsed, data, mailbox, hasAdmin)) {
392
+ return;
393
+ }
394
+ const attributes = [owner, formatRights(ALL_RIGHTS)];
395
+ getAcl(mailbox).forEach((rights, identifier) => {
396
+ attributes.push(identifier, formatRights(rights));
397
+ });
398
+ connection.send({ tag: '*', command: 'ACL', attributes: mailboxResponse(mailbox, attributes) }, 'ACL', parsed, data, mailbox);
399
+ connection.sendStatus(parsed, data, 'OK', 'Getacl complete');
400
+ }), aclOptions);
401
+ // RFC 4314 sections 3.4 and 3.7
402
+ server.setCommandHandler('LISTRIGHTS', aclCommand(2, (connection, parsed, data, mailbox) => {
403
+ if (!checkAclCommand(connection, parsed, data, mailbox, hasAdmin)) {
404
+ return;
405
+ }
406
+ // the identifier is sent back in the same form as the client used
407
+ const identifier = parsed.attributes[1].value;
408
+ let attributes;
409
+ if (identifier === owner) {
410
+ // the owner always has every right, nothing is optional
411
+ attributes = [identifier, formatRights(ALL_RIGHTS)];
412
+ }
413
+ else {
414
+ // nothing is granted always, every right can be granted on its own, including the virtual ones
415
+ attributes = [identifier, ''].concat(RIGHTS.split(''), Object.keys(VIRTUAL_RIGHTS));
416
+ }
417
+ connection.send({ tag: '*', command: 'LISTRIGHTS', attributes: mailboxResponse(mailbox, attributes) }, 'LISTRIGHTS', parsed, data, mailbox);
418
+ connection.sendStatus(parsed, data, 'OK', 'Listrights complete');
419
+ }), identifierOptions);
420
+ // RFC 4314 sections 3.5 and 3.8
421
+ server.setCommandHandler('MYRIGHTS', aclCommand(1, (connection, parsed, data, mailbox) => {
422
+ if (!checkAclCommand(connection, parsed, data, mailbox, (rights) => ANY_RIGHTS.some(right => rights.has(right)))) {
423
+ return;
424
+ }
425
+ sendMyRights(connection, mailbox, parsed, data);
426
+ connection.sendStatus(parsed, data, 'OK', 'Myrights complete');
427
+ }), aclOptions);
428
+ // Enforcement (RFC 4314 section 4). Commands are wrapped once every plugin is loaded, so that
429
+ // the commands of every plugin (MOVE, UID EXPUNGE, ...) are covered whatever the load order is
430
+ const wrappers = {};
431
+ // the handlers that the enforcement wraps
432
+ const prevHandlers = {};
433
+ const wrapCommands = () => {
434
+ Object.keys(wrappers).forEach(command => {
435
+ const prevHandler = server.getCommandHandler(command);
436
+ if (prevHandler) {
437
+ prevHandlers[command] = prevHandler;
438
+ server.setCommandHandler(command, (connection, parsed, data, callback) => {
439
+ if (isOwner(connection)) {
440
+ return prevHandler(connection, parsed, data, callback);
441
+ }
442
+ return wrappers[command](prevHandler, connection, parsed, data, callback);
443
+ });
444
+ }
445
+ });
446
+ // Return options of an extended LIST (LIST-STATUS, LIST-MYRIGHTS) add responses for the listed
447
+ // mailboxes, so they skip the mailboxes that are not listed for the user. STATUS also needs "r"
448
+ // (RFC 5819 section 2: the STATUS response MUST NOT be returned if the mailbox can not be selected)
449
+ Object.keys(listExtensions.returnOptions).forEach(name => {
450
+ const option = listExtensions.returnOptions[name];
451
+ if (typeof option.onItem !== 'function') {
452
+ return;
453
+ }
454
+ const onItem = option.onItem;
455
+ const right = name === 'STATUS' ? 'r' : 'l';
456
+ option.onItem = (connection, folder, ...args) => {
457
+ if (isOwner(connection)) {
458
+ return onItem(connection, folder, ...args);
459
+ }
460
+ const rights = getRights(connection, folder);
461
+ if (rights.has('l') && rights.has(right)) {
462
+ return onItem(connection, folder, ...args);
463
+ }
464
+ };
465
+ });
466
+ // RFC 4314 section 4: STORE changes only the flags the user has rights for
467
+ STORE_ITEMS.forEach(item => {
468
+ const prevHandler = server.storeHandlers[item] || store_js_1.default[item];
469
+ server.storeHandlers[item] = (connection, message, flags, ...args) => {
470
+ const rights = getSelectedRights(connection);
471
+ if (FLAG_RIGHTS.every(right => rights.has(right))) {
472
+ return prevHandler(connection, message, flags, ...args);
473
+ }
474
+ flags = [].concat(flags);
475
+ // invalid flags are still refused with BAD, even if the user could not set them anyway
476
+ flags.forEach((flag) => (0, flags_js_1.checkSystemFlags)(connection, (0, flags_js_1.normalizeSystemFlag)(flagValue(flag))));
477
+ let allowed = flags.filter((flag) => canSetFlag(rights, flag));
478
+ if (item.charAt(0) !== '+' && item.charAt(0) !== '-') {
479
+ // replacing the flags keeps the ones the user can not change
480
+ allowed = message.flags.filter(flag => !canSetFlag(rights, flag)).concat(allowed);
481
+ }
482
+ return prevHandler(connection, message, allowed, ...args);
483
+ };
484
+ });
485
+ };
486
+ server.once('pluginsLoaded', wrapCommands);
487
+ const selectWrapper = (prevHandler, connection, parsed, data, callback) => {
488
+ const mailbox = argumentMailbox(parsed, 0);
489
+ if (!mailbox) {
490
+ return prevHandler(connection, parsed, data, callback);
491
+ }
492
+ const command = parsed.command.toUpperCase();
493
+ const rights = getRights(connection, mailbox);
494
+ if (!rights.has('r')) {
495
+ // a failed SELECT leaves no mailbox selected (RFC 3501 section 6.3.1)
496
+ (0, select_js_1.beginSelect)(connection, parsed, data);
497
+ connection.closeMailbox();
498
+ sendDenied(connection, parsed, data, rights, command + ' FAILED');
499
+ return callback();
500
+ }
501
+ connection.aclRights = rights;
502
+ if (command === 'SELECT' && !WRITE_RIGHTS.some(right => rights.has(right))) {
503
+ // RFC 4314 section 5.2: the server MUST answer READ-ONLY, so the mailbox is opened like EXAMINE does
504
+ return prevHandlers.EXAMINE(connection, parsed, data, callback);
505
+ }
506
+ return prevHandler(connection, parsed, data, callback);
507
+ };
508
+ wrappers.SELECT = selectWrapper;
509
+ wrappers.EXAMINE = selectWrapper;
510
+ // Checks that the user has a right on the mailbox named by an argument. Unknown mailboxes and
511
+ // invalid arguments are left to the command handler
512
+ const hasMailboxRight = (right, position, notFound) => (connection, parsed, data) => {
513
+ const mailbox = argumentMailbox(parsed, position);
514
+ const rights = mailbox && getRights(connection, mailbox);
515
+ if (rights && !rights.has(right)) {
516
+ sendDenied(connection, parsed, data, rights, undefined, notFound);
517
+ return false;
518
+ }
519
+ return true;
520
+ };
521
+ const requireRight = (right, position, notFound) => {
522
+ const check = hasMailboxRight(right, position, notFound);
523
+ return (prevHandler, connection, parsed, data, callback) => check(connection, parsed, data) ? prevHandler(connection, parsed, data, callback) : callback();
524
+ };
525
+ wrappers.STATUS = requireRight('r', 0);
526
+ // SUBSCRIBE checks that the mailbox exists, so it needs "l". UNSUBSCRIBE needs no rights
527
+ wrappers.SUBSCRIBE = requireRight('l', 0);
528
+ wrappers.DELETE = requireRight('x', 0);
529
+ // CREATE needs "k" on the nearest existing parent, a mailbox at the top level can not be created
530
+ const checkCreateRight = (connection, parsed, data, path) => {
531
+ const parent = getParent(path);
532
+ if (!parent || !getRights(connection, parent).has('k')) {
533
+ sendNoPerm(connection, parsed, data);
534
+ return false;
535
+ }
536
+ return true;
537
+ };
538
+ wrappers.CREATE = (prevHandler, connection, parsed, data, callback) => {
539
+ const name = mailboxArgument(parsed, 0);
540
+ if (name !== false && !checkCreateRight(connection, parsed, data, name)) {
541
+ return callback();
542
+ }
543
+ return prevHandler(connection, parsed, data, callback);
544
+ };
545
+ // RENAME needs "x" on the mailbox and "k" on the new parent
546
+ const hasRenameRight = hasMailboxRight('x', 0);
547
+ wrappers.RENAME = (prevHandler, connection, parsed, data, callback) => {
548
+ if (!hasRenameRight(connection, parsed, data)) {
549
+ return callback();
550
+ }
551
+ const name = mailboxArgument(parsed, 1);
552
+ if (name !== false && argumentMailbox(parsed, 0) && !checkCreateRight(connection, parsed, data, name)) {
553
+ return callback();
554
+ }
555
+ return prevHandler(connection, parsed, data, callback);
556
+ };
557
+ // APPEND needs "i", and only the flags the user has rights for are stored
558
+ wrappers.APPEND = (prevHandler, connection, parsed, data, callback) => {
559
+ const mailbox = argumentMailbox(parsed, 0);
560
+ if (!mailbox) {
561
+ return prevHandler(connection, parsed, data, callback);
562
+ }
563
+ const rights = getRights(connection, mailbox);
564
+ if (!rights.has('i')) {
565
+ sendDenied(connection, parsed, data, rights, 'APPEND FAILED', 'TRYCREATE');
566
+ return callback();
567
+ }
568
+ const flags = parsed.attributes[1];
569
+ if (Array.isArray(flags) && flags.every((flag) => flag && flag.type === 'ATOM')) {
570
+ try {
571
+ flags.forEach((flag) => (0, flags_js_1.checkSystemFlags)(connection, (0, flags_js_1.normalizeSystemFlag)(flag.value)));
572
+ // the server MUST NOT fail APPEND for flags the user can not set
573
+ parsed.attributes[1] = flags.filter((flag) => canSetFlag(rights, flag));
574
+ }
575
+ catch {
576
+ // invalid flags are refused with BAD by the APPEND handler
577
+ }
578
+ }
579
+ return prevHandler(connection, parsed, data, callback);
580
+ };
581
+ // the refusal for a target mailbox without "i", see sendDenied
582
+ const insertRefusal = (connection, mailbox) => {
583
+ const rights = mailbox ? getRights(connection, mailbox) : false;
584
+ if (!rights || rights.has('i')) {
585
+ return false;
586
+ }
587
+ return rights.has('l')
588
+ ? { command: 'NO', code: 'NOPERM', text: 'Permission denied' }
589
+ : { command: 'NO', code: 'TRYCREATE', text: 'Target mailbox does not exist' };
590
+ };
591
+ // COPY and MOVE need "i" on the target, MOVE also needs what UID STORE \Deleted and UID EXPUNGE
592
+ // need on the source, "t" and "e" (RFC 6851 section 4.2)
593
+ const requireInsert = requireRight('i', 1, 'TRYCREATE');
594
+ const copyWrapper = (isMove) => (prevHandler, connection, parsed, data, callback) => {
595
+ const command = parsed.command.toUpperCase();
596
+ if (isMove && !connection.readOnly) {
597
+ const rights = getSelectedRights(connection);
598
+ if (!rights.has('t') || !rights.has('e')) {
599
+ sendNoPerm(connection, parsed, data, command + ' FAIL');
600
+ return callback();
601
+ }
602
+ }
603
+ return requireInsert(prevHandler, connection, parsed, data, callback);
604
+ };
605
+ wrappers.COPY = copyWrapper(false);
606
+ wrappers['UID COPY'] = copyWrapper(false);
607
+ wrappers.MOVE = copyWrapper(true);
608
+ wrappers['UID MOVE'] = copyWrapper(true);
609
+ // RFC 8508 section 4.1: REPLACE needs the rights of UID STORE (\Deleted, "t") and UID EXPUNGE ("e") on the
610
+ // selected mailbox, and of APPEND ("i") on the target. A read-only session is refused by the command handler
611
+ const replaceRefusal = (connection, mailbox) => {
612
+ const selected = getSelectedRights(connection);
613
+ if (!connection.readOnly && (!selected.has('t') || !selected.has('e'))) {
614
+ return { command: 'NO', code: 'NOPERM', text: 'Permission denied' };
615
+ }
616
+ return insertRefusal(connection, mailbox);
617
+ };
618
+ const replaceWrapper = (prevHandler, connection, parsed, data, callback) => {
619
+ const refusal = replaceRefusal(connection, argumentMailbox(parsed, 1));
620
+ if (refusal) {
621
+ connection.sendStatus(parsed, data, refusal.command, refusal.text, refusal.code, parsed.command.toUpperCase() + ' FAILED');
622
+ return callback();
623
+ }
624
+ return prevHandler(connection, parsed, data, callback);
625
+ };
626
+ wrappers.REPLACE = replaceWrapper;
627
+ wrappers['UID REPLACE'] = replaceWrapper;
628
+ // The message literal of APPEND and REPLACE is refused before it is sent when the user may not add messages to the
629
+ // target, with the same answer as after the literal. A mailbox the user can not see ("l") is refused like a
630
+ // missing one, which the core literal filter refuses before the literal too, so the transcripts do not tell
631
+ // a hidden mailbox from a missing one (RFC 4314 section 6)
632
+ server.literalFilters.unshift((connection, command, line) => {
633
+ if (isOwner(connection) || connection.isBusy()) {
634
+ return false;
635
+ }
636
+ const path = (0, append_js_1.getPendingTarget)(connection, command, line);
637
+ const mailbox = path !== false && server.getMailbox(path);
638
+ if (!mailbox) {
639
+ return false;
640
+ }
641
+ return command === 'APPEND' ? insertRefusal(connection, mailbox) : replaceRefusal(connection, mailbox);
642
+ });
643
+ // RFC 5464 section 3.3: mailbox annotations need "l" and any of "r", "s", "w", "i" or "p". Server
644
+ // annotations (the empty mailbox name) are not covered by mailbox ACLs
645
+ const METADATA_RIGHTS = ['r', 's', 'w', 'i', 'p'];
646
+ const canUseMetadata = (rights) => rights.has('l') && METADATA_RIGHTS.some(right => rights.has(right));
647
+ const metadataWrapper = (prevHandler, connection, parsed, data, callback) => {
648
+ const args = parsed.attributes || [];
649
+ // GETMETADATA takes its options before the mailbox name (RFC 5464 errata 2785)
650
+ const position = parsed.command.toUpperCase() === 'GETMETADATA' && Array.isArray(args[0]) ? 1 : 0;
651
+ const mailbox = argumentMailbox(parsed, position);
652
+ if (mailbox && mailboxArgument(parsed, position) !== '') {
653
+ const rights = getRights(connection, mailbox);
654
+ if (!canUseMetadata(rights)) {
655
+ sendDenied(connection, parsed, data, rights);
656
+ return callback();
657
+ }
658
+ }
659
+ return prevHandler(connection, parsed, data, callback);
660
+ };
661
+ wrappers.GETMETADATA = metadataWrapper;
662
+ wrappers.SETMETADATA = metadataWrapper;
663
+ // Unsolicited METADATA responses (RFC 5464 section 4.4.2) only go to sessions that could read the
664
+ // annotations of the mailbox. Server annotations have an empty mailbox name
665
+ server.notifyFilters.push((connection, notification) => {
666
+ const command = notification.command || {};
667
+ if (command.command !== 'METADATA' || isOwner(connection)) {
668
+ return true;
669
+ }
670
+ const name = command.attributes && command.attributes[0];
671
+ const path = name && typeof name === 'object' ? name.value : name;
672
+ const mailbox = typeof path === 'string' && path !== '' && server.getMailbox(path);
673
+ return !mailbox || canUseMetadata(getRights(connection, mailbox));
674
+ });
675
+ // RFC 9208 section 6: GETQUOTA needs no rights. GETQUOTAROOT needs "r" for the MESSAGE and STORAGE
676
+ // resources, which tell about the messages of the mailbox, only MAILBOX needs no right. Without "r"
677
+ // (or for a mailbox that does not exist, so nothing is disclosed) the QUOTA responses only list MAILBOX
678
+ wrappers.GETQUOTAROOT = (prevHandler, connection, parsed, data, callback) => {
679
+ const mailbox = argumentMailbox(parsed, 0);
680
+ if (!mailbox || !getRights(connection, mailbox).has('r')) {
681
+ parsed.aclQuotaMailboxOnly = true;
682
+ }
683
+ return prevHandler(connection, parsed, data, callback);
684
+ };
685
+ // RFC 9208 section 6: SETQUOTA needs "a". The quota root is not a mailbox, so the user needs "a" on
686
+ // every mailbox the quota root applies to (all personal mailboxes)
687
+ wrappers.SETQUOTA = (prevHandler, connection, parsed, data, callback) => {
688
+ const args = parsed.attributes || [];
689
+ if (args.length === 2 && Array.isArray(args[1])) {
690
+ const isAdmin = Object.keys(server.folderCache).every(path => {
691
+ const mailbox = server.folderCache[path];
692
+ return !server.isPersonal(mailbox) || getRights(connection, mailbox).has('a');
693
+ });
694
+ if (!isAdmin) {
695
+ sendNoPerm(connection, parsed, data);
696
+ return callback();
697
+ }
698
+ }
699
+ return prevHandler(connection, parsed, data, callback);
700
+ };
701
+ // EXPUNGE needs "e". In a read-only session the command handler refuses it already
702
+ const expungeWrapper = (prevHandler, connection, parsed, data, callback) => {
703
+ if (!connection.readOnly && !getSelectedRights(connection).has('e')) {
704
+ sendNoPerm(connection, parsed, data);
705
+ return callback();
706
+ }
707
+ return prevHandler(connection, parsed, data, callback);
708
+ };
709
+ wrappers.EXPUNGE = expungeWrapper;
710
+ wrappers['UID EXPUNGE'] = expungeWrapper;
711
+ // UNAUTHENTICATE (RFC 8437): rights of the previous user must not stay with the session
712
+ server.resetHandlers.push((connection) => {
713
+ connection.aclRights = null;
714
+ });
715
+ server.connectionHandlers.push((connection) => {
716
+ // A FETCH that implies setting \Seen MUST NOT set it without "s", and without "e" CLOSE MUST
717
+ // close the mailbox without expunging (RFC 4314 section 4)
718
+ const canSetSeen = connection.canSetSeen;
719
+ const canExpunge = connection.canExpunge;
720
+ connection.canSetSeen = () => canSetSeen.call(connection) && getSelectedRights(connection).has('s');
721
+ connection.canExpunge = () => canExpunge.call(connection) && getSelectedRights(connection).has('e');
722
+ });
723
+ // STORE needs the right of at least one of the flags it changes, it SHOULD NOT fail otherwise.
724
+ // The flags themselves are filtered by the store handlers
725
+ const storeWrapper = (prevHandler, connection, parsed, data, callback) => {
726
+ const rights = getSelectedRights(connection);
727
+ const args = parsed.attributes || [];
728
+ const item = args.length >= 3 && args[args.length - 2] && args[args.length - 2].type === 'ATOM' && args[args.length - 2].value.toUpperCase();
729
+ if (connection.readOnly || !item) {
730
+ return prevHandler(connection, parsed, data, callback);
731
+ }
732
+ if (STORE_ITEMS.indexOf(item) < 0) {
733
+ // other items of plugins (e.g. X-GM-LABELS) change shared message data like keywords do, so they
734
+ // need "w". Unknown items are refused with BAD by the STORE handler
735
+ if (Object.hasOwn(server.storeHandlers, item) && !rights.has('w')) {
736
+ sendNoPerm(connection, parsed, data);
737
+ return callback();
738
+ }
739
+ return prevHandler(connection, parsed, data, callback);
740
+ }
741
+ const flags = [].concat(args[args.length - 1] || []);
742
+ try {
743
+ flags.forEach(flag => (0, flags_js_1.checkSystemFlags)(connection, (0, flags_js_1.normalizeSystemFlag)(flagValue(flag))));
744
+ }
745
+ catch {
746
+ // invalid flags are refused with BAD by the STORE handler
747
+ return prevHandler(connection, parsed, data, callback);
748
+ }
749
+ let allowed;
750
+ if (item.charAt(0) === '+' || item.charAt(0) === '-') {
751
+ allowed = !flags.length || flags.some(flag => canSetFlag(rights, flag));
752
+ }
753
+ else {
754
+ // replacing the flags changes every flag the user has rights for
755
+ allowed = FLAG_RIGHTS.some(right => rights.has(right));
756
+ }
757
+ if (!allowed) {
758
+ sendNoPerm(connection, parsed, data);
759
+ return callback();
760
+ }
761
+ return prevHandler(connection, parsed, data, callback);
762
+ };
763
+ wrappers.STORE = storeWrapper;
764
+ wrappers['UID STORE'] = storeWrapper;
765
+ // Checks if an extended LIST command asks for the STATUS return option (RFC 5819)
766
+ const hasStatusReturnOption = (parsed) => {
767
+ const args = (parsed && parsed.attributes) || [];
768
+ return args.some((arg, i) => arg &&
769
+ arg.type === 'ATOM' &&
770
+ /^RETURN$/i.test(arg.value) &&
771
+ Array.isArray(args[i + 1]) &&
772
+ args[i + 1].some((option) => option && option.type === 'ATOM' && /^STATUS$/i.test(option.value)));
773
+ };
774
+ // Checks if any mailbox below a mailbox is visible to the user
775
+ const hasVisibleChildren = (connection, mailbox) => server.hasDescendant(mailbox.path, (child) => getRights(connection, child).has('l'));
776
+ server.outputHandlers.push((connection, response, description, parsed, data, extra) => {
777
+ if (isOwner(connection)) {
778
+ return;
779
+ }
780
+ if ((description === 'LIST ITEM' || description === 'LSUB ITEM') && extra && extra.path) {
781
+ // LIST and LSUB need "l", the mailbox is left out without it and never answered with NO
782
+ if (!getRights(connection, extra).has('l')) {
783
+ response.skipResponse = true;
784
+ return;
785
+ }
786
+ const flags = response.attributes[0];
787
+ if (!Array.isArray(flags)) {
788
+ return;
789
+ }
790
+ response.attributes[0] = flags.slice();
791
+ const index = flags.findIndex((flag) => flag && flag.value === '\\HasChildren');
792
+ if (index >= 0 && !hasVisibleChildren(connection, extra)) {
793
+ // children that the user can not see do not count (RFC 3348 section 4)
794
+ response.attributes[0][index] = { type: 'ATOM', value: '\\HasNoChildren' };
795
+ }
796
+ // RFC 5819 section 2: a mailbox that gets no STATUS response because it can not be selected
797
+ // (no "r" right) MUST be listed with \Noselect
798
+ const isSelectable = !flags.some((flag) => flag && /^\\(Noselect|NonExistent)$/i.test(flag.value));
799
+ if (isSelectable && hasStatusReturnOption(parsed) && !getRights(connection, extra).has('r')) {
800
+ response.attributes[0].push({ type: 'ATOM', value: '\\Noselect' });
801
+ }
802
+ return;
803
+ }
804
+ if (description === 'QUOTA' && parsed && parsed.aclQuotaMailboxOnly && Array.isArray(response.attributes[1])) {
805
+ // quota-resource = resource-name SP resource-usage SP resource-limit
806
+ const list = response.attributes[1];
807
+ const filtered = [];
808
+ for (let i = 0; i < list.length; i += 3) {
809
+ if (list[i] && String(list[i].value).toUpperCase() === 'MAILBOX') {
810
+ filtered.push(list[i], list[i + 1], list[i + 2]);
811
+ }
812
+ }
813
+ // quota-list needs at least one resource, so without a MAILBOX limit there is no QUOTA response
814
+ if (filtered.length) {
815
+ response.attributes[1] = filtered;
816
+ }
817
+ else {
818
+ response.skipResponse = true;
819
+ }
820
+ return;
821
+ }
822
+ if (description === 'SELECT PERMANENTFLAGS') {
823
+ // RFC 4314 section 5.1.1: PERMANENTFLAGS MUST reflect the rights of the user
824
+ const section = response.attributes[0] && response.attributes[0].section;
825
+ const rights = getSelectedRights(connection);
826
+ if (section && Array.isArray(section[1])) {
827
+ section[1] = section[1].filter((flag) => (flag.value === '\\*' ? rights.has('w') : canSetFlag(rights, flag)));
828
+ }
829
+ return;
830
+ }
831
+ if (extra && extra.targetUids && extra.mailbox && /^(UID )?(COPY|MOVE COPYUID)$/.test(description)) {
832
+ // RFC 4314 section 4: the copies only keep the flags the user has rights for in the target
833
+ const rights = getRights(connection, extra.mailbox);
834
+ const uids = new Set(extra.targetUids);
835
+ extra.mailbox.messages.forEach((message) => {
836
+ if (uids.has(message.uid)) {
837
+ message.flags = message.flags.filter((flag) => canSetFlag(rights, flag));
838
+ }
839
+ });
840
+ }
841
+ });
842
+ // The DELETE command MUST delete the ACL of the mailbox (RFC 4314 section 4), this matters when a
843
+ // \Noselect placeholder stays for the children of the mailbox
844
+ // DELETE and CREATE are core commands, so the handlers exist
845
+ const prevDelete = server.getCommandHandler('DELETE');
846
+ server.setCommandHandler('DELETE', (connection, parsed, data, callback) => {
847
+ const mailbox = argumentMailbox(parsed, 0);
848
+ prevDelete(connection, parsed, data, () => {
849
+ const placeholder = argumentMailbox(parsed, 0);
850
+ if (placeholder && placeholder !== mailbox) {
851
+ delete placeholder.acl;
852
+ }
853
+ callback();
854
+ });
855
+ });
856
+ // A new mailbox inherits the ACL of its parent (RFC 4314 section 4, CREATE SHOULD)
857
+ const prevCreate = server.getCommandHandler('CREATE');
858
+ server.setCommandHandler('CREATE', (connection, parsed, data, callback) => {
859
+ const existing = new Set(Object.keys(server.folderCache));
860
+ prevCreate(connection, parsed, data, () => {
861
+ Object.keys(server.folderCache)
862
+ .filter(path => !existing.has(path))
863
+ .sort((a, b) => a.length - b.length)
864
+ .forEach(path => {
865
+ const parent = getParent(path);
866
+ const mailbox = server.folderCache[path];
867
+ if (parent && parent !== mailbox) {
868
+ const acl = new Map();
869
+ getAcl(parent).forEach((rights, identifier) => acl.set(identifier, new Set(rights)));
870
+ mailbox.acl = acl;
871
+ }
872
+ });
873
+ callback();
874
+ });
875
+ });
876
+ }
877
+ {
878
+ const defaultExport = exports.default;
879
+ if (defaultExport && (typeof defaultExport === 'function' || typeof defaultExport === 'object')) {
880
+ for (const key of Object.keys(exports)) {
881
+ if (key !== 'default' && !(key in defaultExport)) {
882
+ defaultExport[key] = exports[key];
883
+ }
884
+ }
885
+ Object.defineProperty(defaultExport, 'default', { value: defaultExport, enumerable: false, writable: true, configurable: true });
886
+ module.exports = defaultExport;
887
+ }
888
+ }