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.
- package/README.md +32 -19
- package/bin/imapkit.js +12 -16
- package/dist/cjs/addressparser.d.ts +21 -0
- package/dist/cjs/addressparser.js +277 -0
- package/dist/cjs/arguments.d.ts +31 -0
- package/{lib → dist/cjs}/arguments.js +9 -21
- package/dist/cjs/bodystructure.d.ts +25 -0
- package/dist/cjs/bodystructure.js +150 -0
- package/dist/cjs/cert.d.ts +2 -0
- package/dist/cjs/cert.js +7 -0
- package/dist/cjs/command-states.d.ts +36 -0
- package/dist/cjs/command-states.js +102 -0
- package/dist/cjs/commands/append.d.ts +88 -0
- package/dist/cjs/commands/append.js +299 -0
- package/dist/cjs/commands/capability.d.ts +2 -0
- package/dist/cjs/commands/capability.js +44 -0
- package/dist/cjs/commands/check.d.ts +2 -0
- package/dist/cjs/commands/check.js +28 -0
- package/dist/cjs/commands/close.d.ts +2 -0
- package/dist/cjs/commands/close.js +35 -0
- package/dist/cjs/commands/copy.d.ts +30 -0
- package/dist/cjs/commands/copy.js +107 -0
- package/dist/cjs/commands/create.d.ts +2 -0
- package/dist/cjs/commands/create.js +53 -0
- package/dist/cjs/commands/delete.d.ts +2 -0
- package/dist/cjs/commands/delete.js +63 -0
- package/dist/cjs/commands/examine.d.ts +2 -0
- package/dist/cjs/commands/examine.js +19 -0
- package/dist/cjs/commands/expunge.d.ts +2 -0
- package/dist/cjs/commands/expunge.js +32 -0
- package/dist/cjs/commands/fetch.d.ts +23 -0
- package/dist/cjs/commands/fetch.js +208 -0
- package/dist/cjs/commands/handlers/fetch.d.ts +12 -0
- package/dist/cjs/commands/handlers/fetch.js +195 -0
- package/dist/cjs/commands/handlers/flags.d.ts +17 -0
- package/dist/cjs/commands/handlers/flags.js +39 -0
- package/dist/cjs/commands/handlers/search.d.ts +85 -0
- package/dist/cjs/commands/handlers/search.js +504 -0
- package/dist/cjs/commands/handlers/status.d.ts +34 -0
- package/dist/cjs/commands/handlers/status.js +81 -0
- package/dist/cjs/commands/handlers/store.d.ts +3 -0
- package/dist/cjs/commands/handlers/store.js +117 -0
- package/dist/cjs/commands/index.d.ts +2 -0
- package/dist/cjs/commands/index.js +61 -0
- package/dist/cjs/commands/list.d.ts +2 -0
- package/dist/cjs/commands/list.js +86 -0
- package/dist/cjs/commands/login.d.ts +2 -0
- package/dist/cjs/commands/login.js +62 -0
- package/dist/cjs/commands/logout.d.ts +2 -0
- package/dist/cjs/commands/logout.js +40 -0
- package/dist/cjs/commands/lsub.d.ts +2 -0
- package/dist/cjs/commands/lsub.js +79 -0
- package/dist/cjs/commands/noop.d.ts +2 -0
- package/dist/cjs/commands/noop.js +28 -0
- package/dist/cjs/commands/rename.d.ts +2 -0
- package/dist/cjs/commands/rename.js +98 -0
- package/dist/cjs/commands/search.d.ts +10 -0
- package/dist/cjs/commands/search.js +71 -0
- package/dist/cjs/commands/select.d.ts +19 -0
- package/dist/cjs/commands/select.js +230 -0
- package/dist/cjs/commands/status.d.ts +2 -0
- package/dist/cjs/commands/status.js +57 -0
- package/dist/cjs/commands/store.d.ts +10 -0
- package/dist/cjs/commands/store.js +134 -0
- package/dist/cjs/commands/subscribe.d.ts +2 -0
- package/dist/cjs/commands/subscribe.js +51 -0
- package/dist/cjs/commands/uid-copy.d.ts +2 -0
- package/dist/cjs/commands/uid-copy.js +19 -0
- package/dist/cjs/commands/uid-fetch.d.ts +3 -0
- package/dist/cjs/commands/uid-fetch.js +17 -0
- package/dist/cjs/commands/uid-search.d.ts +3 -0
- package/dist/cjs/commands/uid-search.js +17 -0
- package/dist/cjs/commands/uid-store.d.ts +3 -0
- package/dist/cjs/commands/uid-store.js +17 -0
- package/dist/cjs/commands/unsubscribe.d.ts +2 -0
- package/dist/cjs/commands/unsubscribe.js +50 -0
- package/dist/cjs/dates.d.ts +64 -0
- package/dist/cjs/dates.js +124 -0
- package/dist/cjs/deflate-layer.d.ts +83 -0
- package/dist/cjs/deflate-layer.js +232 -0
- package/dist/cjs/encoded-words.d.ts +15 -0
- package/dist/cjs/encoded-words.js +85 -0
- package/dist/cjs/envelope.d.ts +33 -0
- package/dist/cjs/envelope.js +86 -0
- package/dist/cjs/esearch.d.ts +96 -0
- package/dist/cjs/esearch.js +190 -0
- package/dist/cjs/framing.d.ts +66 -0
- package/{lib → dist/cjs}/framing.js +9 -26
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.js +56 -0
- package/dist/cjs/list-extensions.d.ts +50 -0
- package/dist/cjs/list-extensions.js +35 -0
- package/dist/cjs/load-plugins.d.ts +21 -0
- package/dist/cjs/load-plugins.js +89 -0
- package/dist/cjs/mailbox-name.d.ts +26 -0
- package/dist/cjs/mailbox-name.js +127 -0
- package/dist/cjs/mimeparser.d.ts +137 -0
- package/dist/cjs/mimeparser.js +727 -0
- package/dist/cjs/mock-client.d.ts +39 -0
- package/dist/cjs/mock-client.js +236 -0
- package/dist/cjs/numbers.d.ts +28 -0
- package/dist/cjs/numbers.js +52 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plugins/acl.d.ts +2 -0
- package/dist/cjs/plugins/acl.js +888 -0
- package/dist/cjs/plugins/appendlimit.d.ts +16 -0
- package/dist/cjs/plugins/appendlimit.js +87 -0
- package/dist/cjs/plugins/auth-plain.d.ts +2 -0
- package/dist/cjs/plugins/auth-plain.js +89 -0
- package/dist/cjs/plugins/binary.d.ts +30 -0
- package/dist/cjs/plugins/binary.js +247 -0
- package/dist/cjs/plugins/catenate.d.ts +14 -0
- package/dist/cjs/plugins/catenate.js +250 -0
- package/dist/cjs/plugins/compress.d.ts +7 -0
- package/dist/cjs/plugins/compress.js +79 -0
- package/dist/cjs/plugins/condstore.d.ts +5 -0
- package/dist/cjs/plugins/condstore.js +492 -0
- package/dist/cjs/plugins/context-search.d.ts +8 -0
- package/dist/cjs/plugins/context-search.js +304 -0
- package/dist/cjs/plugins/context-sort.d.ts +6 -0
- package/dist/cjs/plugins/context-sort.js +32 -0
- package/dist/cjs/plugins/create-special-use.d.ts +7 -0
- package/dist/cjs/plugins/create-special-use.js +101 -0
- package/dist/cjs/plugins/enable.d.ts +23 -0
- package/dist/cjs/plugins/enable.js +127 -0
- package/dist/cjs/plugins/esearch.d.ts +6 -0
- package/dist/cjs/plugins/esearch.js +142 -0
- package/dist/cjs/plugins/esort.d.ts +6 -0
- package/dist/cjs/plugins/esort.js +60 -0
- package/dist/cjs/plugins/id.d.ts +5 -0
- package/dist/cjs/plugins/id.js +123 -0
- package/dist/cjs/plugins/idle.d.ts +5 -0
- package/dist/cjs/plugins/idle.js +85 -0
- package/dist/cjs/plugins/imap4rev2.d.ts +6 -0
- package/dist/cjs/plugins/imap4rev2.js +184 -0
- package/dist/cjs/plugins/index.d.ts +2 -0
- package/dist/cjs/plugins/index.js +119 -0
- package/dist/cjs/plugins/list-extended.d.ts +2 -0
- package/dist/cjs/plugins/list-extended.js +235 -0
- package/dist/cjs/plugins/list-status.d.ts +6 -0
- package/dist/cjs/plugins/list-status.js +42 -0
- package/dist/cjs/plugins/literalminus.d.ts +8 -0
- package/dist/cjs/plugins/literalminus.js +32 -0
- package/dist/cjs/plugins/literalplus.d.ts +6 -0
- package/dist/cjs/plugins/literalplus.js +30 -0
- package/dist/cjs/plugins/logindisabled.d.ts +5 -0
- package/dist/cjs/plugins/logindisabled.js +54 -0
- package/dist/cjs/plugins/messagelimit.d.ts +19 -0
- package/dist/cjs/plugins/messagelimit.js +227 -0
- package/dist/cjs/plugins/metadata-server.d.ts +7 -0
- package/dist/cjs/plugins/metadata-server.js +24 -0
- package/dist/cjs/plugins/metadata.d.ts +11 -0
- package/dist/cjs/plugins/metadata.js +436 -0
- package/dist/cjs/plugins/move.d.ts +11 -0
- package/dist/cjs/plugins/move.js +96 -0
- package/dist/cjs/plugins/multiappend.d.ts +12 -0
- package/dist/cjs/plugins/multiappend.js +38 -0
- package/dist/cjs/plugins/multisearch.d.ts +6 -0
- package/dist/cjs/plugins/multisearch.js +263 -0
- package/dist/cjs/plugins/namespace.d.ts +5 -0
- package/dist/cjs/plugins/namespace.js +57 -0
- package/dist/cjs/plugins/notify.d.ts +2 -0
- package/dist/cjs/plugins/notify.js +628 -0
- package/dist/cjs/plugins/oauthbearer.d.ts +32 -0
- package/dist/cjs/plugins/oauthbearer.js +202 -0
- package/dist/cjs/plugins/objectid.d.ts +23 -0
- package/dist/cjs/plugins/objectid.js +221 -0
- package/dist/cjs/plugins/partial.d.ts +6 -0
- package/dist/cjs/plugins/partial.js +78 -0
- package/dist/cjs/plugins/preview.d.ts +31 -0
- package/dist/cjs/plugins/preview.js +378 -0
- package/dist/cjs/plugins/qresync.d.ts +11 -0
- package/dist/cjs/plugins/qresync.js +473 -0
- package/dist/cjs/plugins/quota.d.ts +18 -0
- package/dist/cjs/plugins/quota.js +254 -0
- package/dist/cjs/plugins/replace.d.ts +14 -0
- package/dist/cjs/plugins/replace.js +130 -0
- package/dist/cjs/plugins/sasl-ir.d.ts +5 -0
- package/dist/cjs/plugins/sasl-ir.js +24 -0
- package/dist/cjs/plugins/savedate.d.ts +17 -0
- package/dist/cjs/plugins/savedate.js +64 -0
- package/dist/cjs/plugins/savelimit.d.ts +10 -0
- package/dist/cjs/plugins/savelimit.js +30 -0
- package/dist/cjs/plugins/searchres.d.ts +6 -0
- package/dist/cjs/plugins/searchres.js +91 -0
- package/dist/cjs/plugins/sort-display.d.ts +11 -0
- package/dist/cjs/plugins/sort-display.js +37 -0
- package/dist/cjs/plugins/sort.d.ts +11 -0
- package/dist/cjs/plugins/sort.js +125 -0
- package/dist/cjs/plugins/special-use.d.ts +10 -0
- package/dist/cjs/plugins/special-use.js +95 -0
- package/dist/cjs/plugins/starttls.d.ts +5 -0
- package/dist/cjs/plugins/starttls.js +55 -0
- package/dist/cjs/plugins/status-size.d.ts +7 -0
- package/dist/cjs/plugins/status-size.js +30 -0
- package/dist/cjs/plugins/thread-orderedsubject.d.ts +11 -0
- package/dist/cjs/plugins/thread-orderedsubject.js +28 -0
- package/dist/cjs/plugins/thread-references.d.ts +11 -0
- package/dist/cjs/plugins/thread-references.js +28 -0
- package/dist/cjs/plugins/uidonly.d.ts +15 -0
- package/dist/cjs/plugins/uidonly.js +126 -0
- package/dist/cjs/plugins/uidplus.d.ts +15 -0
- package/dist/cjs/plugins/uidplus.js +119 -0
- package/dist/cjs/plugins/unauthenticate.d.ts +7 -0
- package/dist/cjs/plugins/unauthenticate.js +32 -0
- package/dist/cjs/plugins/unselect.d.ts +5 -0
- package/dist/cjs/plugins/unselect.js +36 -0
- package/dist/cjs/plugins/utf8-accept.d.ts +6 -0
- package/dist/cjs/plugins/utf8-accept.js +72 -0
- package/dist/cjs/plugins/x-gm-ext-1.d.ts +26 -0
- package/dist/cjs/plugins/x-gm-ext-1.js +422 -0
- package/dist/cjs/plugins/xoauth2.d.ts +2 -0
- package/dist/cjs/plugins/xoauth2.js +155 -0
- package/dist/cjs/plugins/xtoybird.d.ts +2 -0
- package/dist/cjs/plugins/xtoybird.js +236 -0
- package/dist/cjs/server.d.ts +876 -0
- package/dist/cjs/server.js +2597 -0
- package/dist/cjs/smtp-listener.d.ts +13 -0
- package/dist/cjs/smtp-listener.js +47 -0
- package/dist/cjs/sorting.d.ts +77 -0
- package/dist/cjs/sorting.js +290 -0
- package/dist/cjs/threading.d.ts +30 -0
- package/dist/cjs/threading.js +318 -0
- package/dist/cjs/types.d.ts +327 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/utf8-session.d.ts +17 -0
- package/dist/cjs/utf8-session.js +148 -0
- package/dist/cjs/vanished.d.ts +21 -0
- package/dist/cjs/vanished.js +55 -0
- package/dist/esm/addressparser.d.ts +21 -0
- package/{lib → dist/esm}/addressparser.js +102 -123
- package/dist/esm/arguments.d.ts +31 -0
- package/dist/esm/arguments.js +96 -0
- package/dist/esm/bodystructure.d.ts +25 -0
- package/{lib → dist/esm}/bodystructure.js +17 -33
- package/dist/esm/cert.d.ts +2 -0
- package/dist/esm/cert.js +4 -0
- package/dist/esm/command-states.d.ts +36 -0
- package/{lib → dist/esm}/command-states.js +4 -16
- package/dist/esm/commands/append.d.ts +88 -0
- package/{lib → dist/esm}/commands/append.js +47 -85
- package/dist/esm/commands/capability.d.ts +2 -0
- package/dist/esm/commands/capability.js +29 -0
- package/dist/esm/commands/check.d.ts +2 -0
- package/dist/esm/commands/check.js +13 -0
- package/dist/esm/commands/close.d.ts +2 -0
- package/dist/esm/commands/close.js +20 -0
- package/dist/esm/commands/copy.d.ts +30 -0
- package/{lib → dist/esm}/commands/copy.js +31 -55
- package/dist/esm/commands/create.d.ts +2 -0
- package/{lib → dist/esm}/commands/create.js +25 -39
- package/dist/esm/commands/delete.d.ts +2 -0
- package/{lib → dist/esm}/commands/delete.js +25 -41
- package/dist/esm/commands/examine.d.ts +2 -0
- package/dist/esm/commands/examine.js +4 -0
- package/dist/esm/commands/expunge.d.ts +2 -0
- package/dist/esm/commands/expunge.js +17 -0
- package/dist/esm/commands/fetch.d.ts +23 -0
- package/{lib → dist/esm}/commands/fetch.js +34 -75
- package/dist/esm/commands/handlers/fetch.d.ts +12 -0
- package/{lib → dist/esm}/commands/handlers/fetch.js +11 -42
- package/dist/esm/commands/handlers/flags.d.ts +17 -0
- package/{lib → dist/esm}/commands/handlers/flags.js +3 -11
- package/dist/esm/commands/handlers/search.d.ts +85 -0
- package/{lib → dist/esm}/commands/handlers/search.js +33 -71
- package/dist/esm/commands/handlers/status.d.ts +34 -0
- package/{lib → dist/esm}/commands/handlers/status.js +1 -9
- package/dist/esm/commands/handlers/store.d.ts +3 -0
- package/{lib → dist/esm}/commands/handlers/store.js +20 -44
- package/dist/esm/commands/index.d.ts +2 -0
- package/dist/esm/commands/index.js +55 -0
- package/dist/esm/commands/list.d.ts +2 -0
- package/dist/esm/commands/list.js +71 -0
- package/dist/esm/commands/login.d.ts +2 -0
- package/{lib → dist/esm}/commands/login.js +24 -44
- package/dist/esm/commands/logout.d.ts +2 -0
- package/dist/esm/commands/logout.js +25 -0
- package/dist/esm/commands/lsub.d.ts +2 -0
- package/dist/esm/commands/lsub.js +64 -0
- package/dist/esm/commands/noop.d.ts +2 -0
- package/dist/esm/commands/noop.js +13 -0
- package/dist/esm/commands/rename.d.ts +2 -0
- package/{lib → dist/esm}/commands/rename.js +32 -51
- package/dist/esm/commands/search.d.ts +10 -0
- package/dist/esm/commands/search.js +54 -0
- package/dist/esm/commands/select.d.ts +19 -0
- package/dist/esm/commands/select.js +214 -0
- package/dist/esm/commands/status.d.ts +2 -0
- package/{lib → dist/esm}/commands/status.js +18 -39
- package/dist/esm/commands/store.d.ts +10 -0
- package/{lib → dist/esm}/commands/store.js +39 -73
- package/dist/esm/commands/subscribe.d.ts +2 -0
- package/{lib → dist/esm}/commands/subscribe.js +22 -39
- package/dist/esm/commands/uid-copy.d.ts +2 -0
- package/dist/esm/commands/uid-copy.js +4 -0
- package/dist/esm/commands/uid-fetch.d.ts +3 -0
- package/dist/esm/commands/uid-fetch.js +3 -0
- package/dist/esm/commands/uid-search.d.ts +3 -0
- package/dist/esm/commands/uid-search.js +3 -0
- package/dist/esm/commands/uid-store.d.ts +3 -0
- package/dist/esm/commands/uid-store.js +3 -0
- package/dist/esm/commands/unsubscribe.d.ts +2 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +22 -37
- package/dist/esm/dates.d.ts +64 -0
- package/{lib → dist/esm}/dates.js +6 -14
- package/dist/esm/deflate-layer.d.ts +83 -0
- package/{lib → dist/esm}/deflate-layer.js +9 -26
- package/dist/esm/encoded-words.d.ts +15 -0
- package/{lib → dist/esm}/encoded-words.js +9 -25
- package/dist/esm/envelope.d.ts +33 -0
- package/{lib → dist/esm}/envelope.js +8 -19
- package/dist/esm/esearch.d.ts +96 -0
- package/{lib → dist/esm}/esearch.js +12 -39
- package/dist/esm/framing.d.ts +66 -0
- package/dist/esm/framing.js +82 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/list-extensions.d.ts +50 -0
- package/{lib → dist/esm}/list-extensions.js +1 -4
- package/dist/esm/load-plugins.d.ts +21 -0
- package/{lib → dist/esm}/load-plugins.js +9 -43
- package/dist/esm/mailbox-name.d.ts +26 -0
- package/{lib → dist/esm}/mailbox-name.js +5 -26
- package/dist/esm/mimeparser.d.ts +137 -0
- package/{lib → dist/esm}/mimeparser.js +74 -152
- package/dist/esm/mock-client.d.ts +39 -0
- package/{lib → dist/esm}/mock-client.js +26 -40
- package/dist/esm/numbers.d.ts +28 -0
- package/{lib → dist/esm}/numbers.js +1 -8
- package/dist/esm/package.json +3 -0
- package/dist/esm/plugins/acl.d.ts +2 -0
- package/{lib → dist/esm}/plugins/acl.js +146 -240
- package/dist/esm/plugins/appendlimit.d.ts +16 -0
- package/{lib → dist/esm}/plugins/appendlimit.js +5 -16
- package/dist/esm/plugins/auth-plain.d.ts +2 -0
- package/{lib → dist/esm}/plugins/auth-plain.js +11 -31
- package/dist/esm/plugins/binary.d.ts +30 -0
- package/{lib → dist/esm}/plugins/binary.js +17 -43
- package/dist/esm/plugins/catenate.d.ts +14 -0
- package/{lib → dist/esm}/plugins/catenate.js +19 -40
- package/dist/esm/plugins/compress.d.ts +7 -0
- package/dist/esm/plugins/compress.js +61 -0
- package/dist/esm/plugins/condstore.d.ts +5 -0
- package/{lib → dist/esm}/plugins/condstore.js +66 -152
- package/dist/esm/plugins/context-search.d.ts +8 -0
- package/{lib → dist/esm}/plugins/context-search.js +53 -88
- package/dist/esm/plugins/context-sort.d.ts +6 -0
- package/{lib → dist/esm}/plugins/context-sort.js +4 -9
- package/dist/esm/plugins/create-special-use.d.ts +7 -0
- package/{lib → dist/esm}/plugins/create-special-use.js +33 -55
- package/dist/esm/plugins/enable.d.ts +23 -0
- package/dist/esm/plugins/enable.js +111 -0
- package/dist/esm/plugins/esearch.d.ts +6 -0
- package/{lib → dist/esm}/plugins/esearch.js +13 -42
- package/dist/esm/plugins/esort.d.ts +6 -0
- package/{lib → dist/esm}/plugins/esort.js +7 -25
- package/dist/esm/plugins/id.d.ts +5 -0
- package/dist/esm/plugins/id.js +108 -0
- package/dist/esm/plugins/idle.d.ts +5 -0
- package/dist/esm/plugins/idle.js +70 -0
- package/dist/esm/plugins/imap4rev2.d.ts +6 -0
- package/{lib → dist/esm}/plugins/imap4rev2.js +24 -60
- package/dist/esm/plugins/index.d.ts +2 -0
- package/dist/esm/plugins/index.js +113 -0
- package/dist/esm/plugins/list-extended.d.ts +2 -0
- package/{lib → dist/esm}/plugins/list-extended.js +28 -66
- package/dist/esm/plugins/list-status.d.ts +6 -0
- package/{lib → dist/esm}/plugins/list-status.js +5 -12
- package/dist/esm/plugins/literalminus.d.ts +8 -0
- package/{lib → dist/esm}/plugins/literalminus.js +2 -5
- package/dist/esm/plugins/literalplus.d.ts +6 -0
- package/{lib → dist/esm}/plugins/literalplus.js +2 -5
- package/dist/esm/plugins/logindisabled.d.ts +5 -0
- package/dist/esm/plugins/logindisabled.js +39 -0
- package/dist/esm/plugins/messagelimit.d.ts +19 -0
- package/{lib → dist/esm}/plugins/messagelimit.js +18 -41
- package/dist/esm/plugins/metadata-server.d.ts +7 -0
- package/{lib → dist/esm}/plugins/metadata-server.js +3 -7
- package/dist/esm/plugins/metadata.d.ts +11 -0
- package/dist/esm/plugins/metadata.js +421 -0
- package/dist/esm/plugins/move.d.ts +11 -0
- package/{lib → dist/esm}/plugins/move.js +37 -66
- package/dist/esm/plugins/multiappend.d.ts +12 -0
- package/{lib → dist/esm}/plugins/multiappend.js +2 -5
- package/dist/esm/plugins/multisearch.d.ts +6 -0
- package/{lib → dist/esm}/plugins/multisearch.js +24 -48
- package/dist/esm/plugins/namespace.d.ts +5 -0
- package/dist/esm/plugins/namespace.js +42 -0
- package/dist/esm/plugins/notify.d.ts +2 -0
- package/{lib → dist/esm}/plugins/notify.js +90 -134
- package/dist/esm/plugins/oauthbearer.d.ts +32 -0
- package/{lib → dist/esm}/plugins/oauthbearer.js +18 -48
- package/dist/esm/plugins/objectid.d.ts +23 -0
- package/{lib → dist/esm}/plugins/objectid.js +21 -58
- package/dist/esm/plugins/partial.d.ts +6 -0
- package/{lib → dist/esm}/plugins/partial.js +7 -15
- package/dist/esm/plugins/preview.d.ts +31 -0
- package/{lib → dist/esm}/plugins/preview.js +28 -66
- package/dist/esm/plugins/qresync.d.ts +11 -0
- package/{lib → dist/esm}/plugins/qresync.js +64 -134
- package/dist/esm/plugins/quota.d.ts +18 -0
- package/{lib → dist/esm}/plugins/quota.js +89 -135
- package/dist/esm/plugins/replace.d.ts +14 -0
- package/{lib → dist/esm}/plugins/replace.js +21 -51
- package/dist/esm/plugins/sasl-ir.d.ts +5 -0
- package/{lib → dist/esm}/plugins/sasl-ir.js +3 -6
- package/dist/esm/plugins/savedate.d.ts +17 -0
- package/{lib → dist/esm}/plugins/savedate.js +5 -15
- package/dist/esm/plugins/savelimit.d.ts +10 -0
- package/{lib → dist/esm}/plugins/savelimit.js +3 -6
- package/dist/esm/plugins/searchres.d.ts +6 -0
- package/{lib → dist/esm}/plugins/searchres.js +13 -22
- package/dist/esm/plugins/sort-display.d.ts +11 -0
- package/{lib → dist/esm}/plugins/sort-display.js +6 -10
- package/dist/esm/plugins/sort.d.ts +11 -0
- package/{lib → dist/esm}/plugins/sort.js +24 -46
- package/dist/esm/plugins/special-use.d.ts +10 -0
- package/{lib → dist/esm}/plugins/special-use.js +9 -24
- package/dist/esm/plugins/starttls.d.ts +5 -0
- package/{lib → dist/esm}/plugins/starttls.js +20 -37
- package/dist/esm/plugins/status-size.d.ts +7 -0
- package/{lib → dist/esm}/plugins/status-size.js +4 -11
- package/dist/esm/plugins/thread-orderedsubject.d.ts +11 -0
- package/{lib → dist/esm}/plugins/thread-orderedsubject.js +3 -6
- package/dist/esm/plugins/thread-references.d.ts +11 -0
- package/{lib → dist/esm}/plugins/thread-references.js +3 -6
- package/dist/esm/plugins/uidonly.d.ts +15 -0
- package/{lib → dist/esm}/plugins/uidonly.js +11 -35
- package/dist/esm/plugins/uidplus.d.ts +15 -0
- package/{lib → dist/esm}/plugins/uidplus.js +25 -45
- package/dist/esm/plugins/unauthenticate.d.ts +7 -0
- package/dist/esm/plugins/unauthenticate.js +17 -0
- package/dist/esm/plugins/unselect.d.ts +5 -0
- package/dist/esm/plugins/unselect.js +21 -0
- package/dist/esm/plugins/utf8-accept.d.ts +6 -0
- package/{lib → dist/esm}/plugins/utf8-accept.js +10 -21
- package/dist/esm/plugins/x-gm-ext-1.d.ts +26 -0
- package/{lib → dist/esm}/plugins/x-gm-ext-1.js +48 -100
- package/dist/esm/plugins/xoauth2.d.ts +2 -0
- package/dist/esm/plugins/xoauth2.js +140 -0
- package/dist/esm/plugins/xtoybird.d.ts +2 -0
- package/dist/esm/plugins/xtoybird.js +218 -0
- package/dist/esm/server.d.ts +876 -0
- package/dist/esm/server.js +2543 -0
- package/dist/esm/smtp-listener.d.ts +13 -0
- package/{lib → dist/esm}/smtp-listener.js +5 -12
- package/dist/esm/sorting.d.ts +77 -0
- package/{lib → dist/esm}/sorting.js +40 -53
- package/dist/esm/threading.d.ts +30 -0
- package/{lib → dist/esm}/threading.js +29 -70
- package/dist/esm/types.d.ts +327 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/utf8-session.d.ts +17 -0
- package/{lib → dist/esm}/utf8-session.js +15 -25
- package/dist/esm/vanished.d.ts +21 -0
- package/{lib → dist/esm}/vanished.js +16 -21
- package/dist/plugin-help.json +298 -0
- package/package.json +54 -11
- package/lib/commands/capability.js +0 -47
- package/lib/commands/check.js +0 -21
- package/lib/commands/close.js +0 -30
- package/lib/commands/examine.js +0 -7
- package/lib/commands/expunge.js +0 -27
- package/lib/commands/list.js +0 -100
- package/lib/commands/logout.js +0 -41
- package/lib/commands/lsub.js +0 -87
- package/lib/commands/noop.js +0 -21
- package/lib/commands/search.js +0 -76
- package/lib/commands/select.js +0 -289
- package/lib/commands/uid copy.js +0 -7
- package/lib/commands/uid fetch.js +0 -5
- package/lib/commands/uid search.js +0 -5
- package/lib/commands/uid store.js +0 -5
- package/lib/plugins/compress.js +0 -76
- package/lib/plugins/enable.js +0 -155
- package/lib/plugins/id.js +0 -138
- package/lib/plugins/idle.js +0 -105
- package/lib/plugins/logindisabled.js +0 -50
- package/lib/plugins/metadata.js +0 -475
- package/lib/plugins/namespace.js +0 -67
- package/lib/plugins/unauthenticate.js +0 -28
- package/lib/plugins/unselect.js +0 -36
- package/lib/plugins/xoauth2.js +0 -188
- package/lib/plugins/xtoybird.js +0 -282
- package/lib/server.js +0 -2880
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const { parsePartialRange } = require('../esearch');
|
|
5
|
-
const { MAX_NUMBER, isNzNumber } = require('../numbers');
|
|
6
|
-
|
|
1
|
+
import { badError } from '../commands/handlers/search.js';
|
|
2
|
+
import { parsePartialRange } from '../esearch.js';
|
|
3
|
+
import { MAX_NUMBER, isNzNumber } from '../numbers.js';
|
|
7
4
|
/**
|
|
8
5
|
* @help Adds MESSAGELIMIT [RFC9738] capability, advertised as MESSAGELIMIT=<n>
|
|
9
6
|
* @help Server option "messageLimit" sets n (default 1000). FETCH, STORE, SEARCH, MOVE, UID EXPUNGE
|
|
@@ -14,17 +11,14 @@ const { MAX_NUMBER, isNzNumber } = require('../numbers');
|
|
|
14
11
|
*
|
|
15
12
|
* MESSAGELIMIT: https://www.rfc-editor.org/rfc/rfc9738
|
|
16
13
|
*/
|
|
17
|
-
|
|
18
14
|
// RFC 9738 section 3: the advertised limit SHOULD NOT be lower than 1000
|
|
19
15
|
const DEFAULT_LIMIT = 1000;
|
|
20
|
-
|
|
21
16
|
// RFC 9738 section 3.1: commands that operate on the messages with the highest UIDs and return the MESSAGELIMIT response code.
|
|
22
17
|
// EXPUNGE, CLOSE and STATUS UNSEEN MUST NOT be limited
|
|
23
18
|
const PARTIAL_COMMANDS = new Set(['FETCH', 'UID FETCH', 'STORE', 'UID STORE', 'MOVE', 'UID MOVE', 'UID EXPUNGE']);
|
|
24
19
|
const SEARCH_COMMANDS = new Set(['SEARCH', 'UID SEARCH']);
|
|
25
20
|
// RFC 9738 section 3.3: SORT and THREAD can not be run on more messages than the limit
|
|
26
21
|
const SORT_COMMANDS = new Set(['SORT', 'UID SORT', 'THREAD', 'UID THREAD']);
|
|
27
|
-
|
|
28
22
|
/**
|
|
29
23
|
* Reads the limit from the "messageLimit" server option
|
|
30
24
|
*
|
|
@@ -45,7 +39,6 @@ function getLimit(server, name) {
|
|
|
45
39
|
server.messageLimit = { name, limit };
|
|
46
40
|
return limit;
|
|
47
41
|
}
|
|
48
|
-
|
|
49
42
|
/**
|
|
50
43
|
* Refuses COPY and APPEND of more messages than the limit, shared with the SAVELIMIT plugin. COPY and
|
|
51
44
|
* MULTIAPPEND APPEND are atomic, so nothing is copied or appended (RFC 9738 section 3.1)
|
|
@@ -70,14 +63,11 @@ function addSaveLimit(server, limit) {
|
|
|
70
63
|
return false;
|
|
71
64
|
});
|
|
72
65
|
}
|
|
73
|
-
|
|
74
|
-
module.exports = function (server) {
|
|
66
|
+
export default function messagelimitPlugin(server) {
|
|
75
67
|
const limit = getLimit(server, 'MESSAGELIMIT');
|
|
76
68
|
server.registerCapability('MESSAGELIMIT=' + limit);
|
|
77
69
|
addSaveLimit(server, limit);
|
|
78
|
-
|
|
79
|
-
const commandName = parsed => String(parsed.command || '').toUpperCase();
|
|
80
|
-
|
|
70
|
+
const commandName = (parsed) => String(parsed.command || '').toUpperCase();
|
|
81
71
|
// FETCH, STORE, MOVE and UID EXPUNGE only operate on the messages with the highest UIDs, "the server is REQUIRED
|
|
82
72
|
// to process messages from highest to lowest UID". COPY is refused by addSaveLimit instead
|
|
83
73
|
server.rangeLimits.push((connection, parsed, range) => {
|
|
@@ -98,7 +88,6 @@ module.exports = function (server) {
|
|
|
98
88
|
parsed.highestFirst = true;
|
|
99
89
|
return range.filter(entry => !dropped.has(entry)).reverse();
|
|
100
90
|
});
|
|
101
|
-
|
|
102
91
|
// RFC 9738 section 3.1: with the PARTIAL FETCH modifier (RFC 9394 section 3.3), the PARTIAL range is the message
|
|
103
92
|
// count, a larger range is refused without doing any work. The PARTIAL plugin answers an invalid modifier with BAD
|
|
104
93
|
server.commandChecks.push((connection, parsed) => {
|
|
@@ -111,7 +100,8 @@ module.exports = function (server) {
|
|
|
111
100
|
let range;
|
|
112
101
|
try {
|
|
113
102
|
range = position >= 0 && parsePartialRange(modifiers[position + 1], true);
|
|
114
|
-
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
115
105
|
return false;
|
|
116
106
|
}
|
|
117
107
|
if (range && range.to - range.from + 1 > limit) {
|
|
@@ -119,13 +109,12 @@ module.exports = function (server) {
|
|
|
119
109
|
}
|
|
120
110
|
return false;
|
|
121
111
|
});
|
|
122
|
-
|
|
123
112
|
// RFC 9738 section 3.1: SEARCH counts the searched messages, not the matching ones. The messages a search
|
|
124
113
|
// looks at are the ones its top level sequence set, UID, UIDAFTER and UIDBEFORE keys allow
|
|
125
114
|
server.searchLimits.push((connection, messages, query) => {
|
|
126
115
|
// the running command, SEARCH and SORT do not pass it to the search
|
|
127
116
|
const parsed = connection._runningCommand && connection._runningCommand.parsed;
|
|
128
|
-
const command = parsed
|
|
117
|
+
const command = parsed ? commandName(parsed) : '';
|
|
129
118
|
if (messages.length <= limit || (!SEARCH_COMMANDS.has(command) && !SORT_COMMANDS.has(command))) {
|
|
130
119
|
return false;
|
|
131
120
|
}
|
|
@@ -143,9 +132,8 @@ module.exports = function (server) {
|
|
|
143
132
|
parsed.messageLimitUid = candidates[candidates.length - limit].uid;
|
|
144
133
|
return candidates.slice(-limit);
|
|
145
134
|
});
|
|
146
|
-
|
|
147
135
|
// RFC 9738 section 3.2: UIDAFTER <uid> is "UID <uid>+1:*", UIDBEFORE <uid> is "UID 1:<uid>-1"
|
|
148
|
-
const parseUniqueId = value => {
|
|
136
|
+
const parseUniqueId = (value) => {
|
|
149
137
|
// uniqueid = nz-number
|
|
150
138
|
if (!isNzNumber(value)) {
|
|
151
139
|
throw badError('UIDAFTER and UIDBEFORE expect a UID');
|
|
@@ -158,7 +146,6 @@ module.exports = function (server) {
|
|
|
158
146
|
uidBefore.argumentTypes = () => [parseUniqueId];
|
|
159
147
|
server.searchHandlers.UIDAFTER = uidAfter;
|
|
160
148
|
server.searchHandlers.UIDBEFORE = uidBefore;
|
|
161
|
-
|
|
162
149
|
const outputHandler = (connection, response, description, parsed, data) => {
|
|
163
150
|
// section 3.1 UID SEARCH example: the results go from the highest UID down, (MODSEQ n) of CONDSTORE stays last
|
|
164
151
|
if (parsed && parsed.messageLimitUid && response.tag === '*' && response.command === 'SEARCH' && Array.isArray(response.attributes)) {
|
|
@@ -177,43 +164,35 @@ module.exports = function (server) {
|
|
|
177
164
|
if (response.command !== 'OK') {
|
|
178
165
|
return;
|
|
179
166
|
}
|
|
180
|
-
|
|
181
167
|
// resp-text-code =/ "MESSAGELIMIT" SP message-limit [SP uniqueid]
|
|
182
168
|
const code = { type: 'SECTION', section: [{ type: 'ATOM', value: 'MESSAGELIMIT' }, limit, lastUid] };
|
|
183
169
|
const attributes = response.attributes || [];
|
|
184
170
|
if (attributes[0] && attributes[0].type === 'SECTION') {
|
|
185
171
|
// RFC 9738 section 3.1: when the tagged OK carries another response code (EXPUNGEISSUED there, and here
|
|
186
172
|
// also HIGHESTMODSEQ, MODIFIED), MESSAGELIMIT is sent in an untagged NO
|
|
187
|
-
connection.send(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
},
|
|
193
|
-
'MESSAGELIMIT',
|
|
194
|
-
parsed,
|
|
195
|
-
data
|
|
196
|
-
);
|
|
173
|
+
connection.send({
|
|
174
|
+
tag: '*',
|
|
175
|
+
command: 'NO',
|
|
176
|
+
attributes: [code, { type: 'TEXT', value: 'Only the last ' + limit + ' messages were processed' }]
|
|
177
|
+
}, 'MESSAGELIMIT', parsed, data);
|
|
197
178
|
return;
|
|
198
179
|
}
|
|
199
180
|
response.attributes = [code].concat(attributes);
|
|
200
181
|
};
|
|
201
|
-
|
|
202
182
|
// registered once every plugin is loaded, so that the response codes other plugins add to the tagged OK
|
|
203
183
|
// are already there
|
|
204
184
|
server.once('pluginsLoaded', () => server.outputHandlers.push(outputHandler));
|
|
205
|
-
}
|
|
206
|
-
|
|
185
|
+
}
|
|
207
186
|
/**
|
|
208
187
|
* Checks if a message passes the keys of a search query that every match must pass: sequence sets,
|
|
209
188
|
* UID, UIDAFTER and UIDBEFORE at the top level, or in parenthesized lists at the top level
|
|
210
189
|
*
|
|
211
|
-
* @param {Object} node AND node of the query tree, see commands/handlers/search.
|
|
190
|
+
* @param {Object} node AND node of the query tree, see commands/handlers/search.ts
|
|
212
191
|
* @param {Object} message Message
|
|
213
192
|
* @return {Boolean} true if the message has to be searched
|
|
214
193
|
*/
|
|
215
194
|
function isCandidate(node, message) {
|
|
216
|
-
return node.args.every(arg => {
|
|
195
|
+
return node.args.every((arg) => {
|
|
217
196
|
switch (arg.key) {
|
|
218
197
|
case 'AND':
|
|
219
198
|
return isCandidate(arg, message);
|
|
@@ -229,6 +208,4 @@ function isCandidate(node, message) {
|
|
|
229
208
|
}
|
|
230
209
|
});
|
|
231
210
|
}
|
|
232
|
-
|
|
233
|
-
module.exports.getLimit = getLimit;
|
|
234
|
-
module.exports.addSaveLimit = addSaveLimit;
|
|
211
|
+
export { getLimit, addSaveLimit };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IMAPServer } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* @help Adds METADATA-SERVER [RFC5464] capability, like METADATA but
|
|
4
|
+
* @help only for server annotations (mailbox name ""). With METADATA
|
|
5
|
+
* @help also loaded, only METADATA is advertised
|
|
6
|
+
*/
|
|
7
|
+
export default function metadataServerPlugin(server: IMAPServer): void;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { setup } = require('./metadata');
|
|
4
|
-
|
|
1
|
+
import { setup } from './metadata.js';
|
|
5
2
|
/**
|
|
6
3
|
* @help Adds METADATA-SERVER [RFC5464] capability, like METADATA but
|
|
7
4
|
* @help only for server annotations (mailbox name ""). With METADATA
|
|
8
5
|
* @help also loaded, only METADATA is advertised
|
|
9
6
|
*/
|
|
10
|
-
|
|
11
|
-
module.exports = function (server) {
|
|
7
|
+
export default function metadataServerPlugin(server) {
|
|
12
8
|
setup(server, false);
|
|
13
|
-
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IMAPServer } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sets up METADATA or METADATA-SERVER. With both plugins loaded, METADATA wins (RFC 5464 section 1:
|
|
4
|
+
* a server with mailbox annotations advertises METADATA)
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} server IMAPServer instance
|
|
7
|
+
* @param {Boolean} mailboxes If true, mailbox annotations are supported, otherwise only server annotations
|
|
8
|
+
*/
|
|
9
|
+
declare function setup(server: IMAPServer, mailboxes: boolean): void;
|
|
10
|
+
export default function metadataPlugin(server: IMAPServer): void;
|
|
11
|
+
export { setup };
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { states } from '../command-states.js';
|
|
2
|
+
import { isNumber } from '../numbers.js';
|
|
3
|
+
import { isAstring } from '../arguments.js';
|
|
4
|
+
import { registerEnable, isEnabled } from './enable.js';
|
|
5
|
+
/**
|
|
6
|
+
* @help Adds METADATA [RFC5464] capability (GETMETADATA and SETMETADATA)
|
|
7
|
+
* @help for server and mailbox annotations. Mailboxes take initial entries
|
|
8
|
+
* @help from a "metadata" object in storage, server entries come from the
|
|
9
|
+
* @help "metadata" option. Limits: "metadataMaxSize" (octets per value,
|
|
10
|
+
* @help default 65536), "metadataMaxEntries" (per mailbox and for the
|
|
11
|
+
* @help server, default 100). "metadataPrivate": false turns /private
|
|
12
|
+
* @help entries off. ENABLE METADATA turns on unsolicited METADATA responses
|
|
13
|
+
*/
|
|
14
|
+
// Default limits, RFC 5464 section 4.1 requires at least 1024 octets and 10 entries
|
|
15
|
+
const DEFAULT_MAX_SIZE = 64 * 1024;
|
|
16
|
+
const DEFAULT_MAX_ENTRIES = 100;
|
|
17
|
+
// RFC 5464 section 3.2.1.1: /shared/admin is always read-only
|
|
18
|
+
const READ_ONLY_SERVER_ENTRIES = new Set(['/shared/admin']);
|
|
19
|
+
// RFC 6154 section 4: special-use attributes are tied to this mailbox entry
|
|
20
|
+
const SPECIAL_USE_ENTRY = '/private/specialuse';
|
|
21
|
+
/**
|
|
22
|
+
* Checks an entry name against RFC 5464 section 3.2
|
|
23
|
+
*
|
|
24
|
+
* @param {String} name Entry name as a binary string
|
|
25
|
+
* @param {Boolean} [isSet] If true, the name must be one that can hold a value (at least 2 components,
|
|
26
|
+
* at least 4 under /shared/vendor and /private/vendor). GETMETADATA also accepts the bare "/shared"
|
|
27
|
+
* and "/private" scopes, so a client can ask for everything with DEPTH infinity
|
|
28
|
+
* @return {String|Boolean} Description of the problem, or false if the name is valid
|
|
29
|
+
*/
|
|
30
|
+
function checkEntryName(name, isSet) {
|
|
31
|
+
if (typeof name !== 'string' || name.charAt(0) !== '/') {
|
|
32
|
+
return 'Entry name must begin with "/"';
|
|
33
|
+
}
|
|
34
|
+
// "MUST NOT contain non-ASCII characters or characters with octet values in the range 0x00 to 0x19"
|
|
35
|
+
if (/[^\x20-\x7f]/.test(name)) {
|
|
36
|
+
return 'Entry name must not contain non-ASCII or control characters';
|
|
37
|
+
}
|
|
38
|
+
if (/[*%]/.test(name)) {
|
|
39
|
+
return 'Entry name must not contain "*" or "%"';
|
|
40
|
+
}
|
|
41
|
+
if (name.indexOf('//') >= 0) {
|
|
42
|
+
return 'Entry name must not contain consecutive "/" characters';
|
|
43
|
+
}
|
|
44
|
+
if (name.charAt(name.length - 1) === '/') {
|
|
45
|
+
return 'Entry name must not end with "/"';
|
|
46
|
+
}
|
|
47
|
+
const components = name.toLowerCase().substr(1).split('/');
|
|
48
|
+
if (components[0] !== 'private' && components[0] !== 'shared') {
|
|
49
|
+
return 'Entry name must begin with /private or /shared';
|
|
50
|
+
}
|
|
51
|
+
if (isSet && components.length < 2) {
|
|
52
|
+
return 'Entry name must have at least 2 components';
|
|
53
|
+
}
|
|
54
|
+
if (isSet && components[1] === 'vendor' && components.length < 4) {
|
|
55
|
+
return 'Vendor entry names must have at least 4 components';
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Converts a value from storage or options to a binary string. A value with NUL is sent as a literal8
|
|
61
|
+
*
|
|
62
|
+
* @param {String} name Entry name, for error messages
|
|
63
|
+
* @param {*} value Entry value
|
|
64
|
+
* @return {String|null} Binary string, or null for no value
|
|
65
|
+
*/
|
|
66
|
+
function normalizeValue(name, value) {
|
|
67
|
+
if (value === null || value === undefined) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof Uint8Array) {
|
|
71
|
+
value = Buffer.from(value).toString('binary');
|
|
72
|
+
}
|
|
73
|
+
else if (typeof value === 'string') {
|
|
74
|
+
if (/[Ā-]/.test(value)) {
|
|
75
|
+
// characters outside Latin-1 can only come from a unicode string, so encode it as UTF-8
|
|
76
|
+
value = Buffer.from(value, 'utf-8').toString('binary');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw new Error('Invalid value for metadata entry ' + name + ', expecting a string');
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Formats a mailbox name for a METADATA response. IMAPConnection#send converts the storage name for
|
|
86
|
+
* each session (UTF-8 with UTF8=ACCEPT) and quotes it when it is not an atom
|
|
87
|
+
*
|
|
88
|
+
* @param {String} name Mailbox name, "" for the server
|
|
89
|
+
* @return {Object} Response attribute
|
|
90
|
+
*/
|
|
91
|
+
function mailboxAttribute(name) {
|
|
92
|
+
return { type: 'MAILBOX', value: name };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sets up METADATA or METADATA-SERVER. With both plugins loaded, METADATA wins (RFC 5464 section 1:
|
|
96
|
+
* a server with mailbox annotations advertises METADATA)
|
|
97
|
+
*
|
|
98
|
+
* @param {Object} server IMAPServer instance
|
|
99
|
+
* @param {Boolean} mailboxes If true, mailbox annotations are supported, otherwise only server annotations
|
|
100
|
+
*/
|
|
101
|
+
function setup(server, mailboxes) {
|
|
102
|
+
if (server.metadataState) {
|
|
103
|
+
// the other variant is already loaded, upgrade it if needed
|
|
104
|
+
if (mailboxes && !server.metadataState.mailboxes) {
|
|
105
|
+
server.metadataState.mailboxes = true;
|
|
106
|
+
delete server.capabilities['METADATA-SERVER'];
|
|
107
|
+
server.registerCapability('METADATA');
|
|
108
|
+
server.enableAvailable.splice(server.enableAvailable.indexOf('METADATA-SERVER'), 1, 'METADATA');
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// RFC 5464 section 5: SETMETADATA values can be literal8
|
|
113
|
+
server.parserOptions.literal8 = true;
|
|
114
|
+
const capability = mailboxes ? 'METADATA' : 'METADATA-SERVER';
|
|
115
|
+
server.registerCapability(capability);
|
|
116
|
+
// RFC 5464 section 4.1: unsolicited METADATA responses are only sent after ENABLE
|
|
117
|
+
registerEnable(server, capability);
|
|
118
|
+
const options = server.options;
|
|
119
|
+
const state = (server.metadataState = {
|
|
120
|
+
mailboxes,
|
|
121
|
+
maxSize: Number(options.metadataMaxSize) || DEFAULT_MAX_SIZE,
|
|
122
|
+
maxEntries: Number(options.metadataMaxEntries) || DEFAULT_MAX_ENTRIES,
|
|
123
|
+
allowPrivate: options.metadataPrivate !== false,
|
|
124
|
+
// server annotations, normalized on first use like mailbox annotations
|
|
125
|
+
server: { path: '', metadata: options.metadata }
|
|
126
|
+
});
|
|
127
|
+
// metadata objects that are already normalized
|
|
128
|
+
const normalized = new WeakSet();
|
|
129
|
+
/**
|
|
130
|
+
* Returns the stored entries of a mailbox or the server, as an object of lower case entry
|
|
131
|
+
* names (entry names are case-insensitive, RFC 5464 section 3.2) and binary string values
|
|
132
|
+
*
|
|
133
|
+
* @param {Object} holder Mailbox object or state.server
|
|
134
|
+
* @return {Object} Entries, replace them with setEntries instead of changing them
|
|
135
|
+
*/
|
|
136
|
+
const getEntries = (holder) => {
|
|
137
|
+
if (!holder.metadata) {
|
|
138
|
+
return Object.create(null);
|
|
139
|
+
}
|
|
140
|
+
if (normalized.has(holder.metadata)) {
|
|
141
|
+
return holder.metadata;
|
|
142
|
+
}
|
|
143
|
+
// initial values from storage or options, never modify the caller's objects
|
|
144
|
+
const entries = Object.create(null);
|
|
145
|
+
const metadata = holder.metadata;
|
|
146
|
+
Object.keys(metadata).forEach(name => {
|
|
147
|
+
const error = checkEntryName(name, true);
|
|
148
|
+
if (error) {
|
|
149
|
+
throw new Error('Invalid metadata entry name ' + JSON.stringify(name) + ': ' + error);
|
|
150
|
+
}
|
|
151
|
+
const value = normalizeValue(name, metadata[name]);
|
|
152
|
+
if (value !== null) {
|
|
153
|
+
entries[name.toLowerCase()] = value;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return setEntries(holder, entries);
|
|
157
|
+
};
|
|
158
|
+
const setEntries = (holder, entries) => {
|
|
159
|
+
normalized.add(entries);
|
|
160
|
+
holder.metadata = entries;
|
|
161
|
+
return entries;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Returns entries that the server computes instead of storing them
|
|
165
|
+
*
|
|
166
|
+
* @param {Object} holder Mailbox object or state.server
|
|
167
|
+
* @return {Object} Entries, a null value means no value
|
|
168
|
+
*/
|
|
169
|
+
const getComputedEntries = (holder) => {
|
|
170
|
+
const computed = Object.create(null);
|
|
171
|
+
if (holder.path && server.capabilities['SPECIAL-USE']) {
|
|
172
|
+
// RFC 6154 section 4: NIL or the special-use attributes separated by spaces, as in LIST responses
|
|
173
|
+
const specialUse = [].concat(holder['special-use'] || []);
|
|
174
|
+
computed[SPECIAL_USE_ENTRY] = specialUse.length ? specialUse.join(' ') : null;
|
|
175
|
+
}
|
|
176
|
+
return computed;
|
|
177
|
+
};
|
|
178
|
+
const isMetadataEnabled = (connection) => isEnabled(connection, state.mailboxes ? 'METADATA' : 'METADATA-SERVER');
|
|
179
|
+
/**
|
|
180
|
+
* Finds the annotation holder for a mailbox name argument, sends a NO if there is none
|
|
181
|
+
*
|
|
182
|
+
* @return {Object|Boolean} Mailbox object, state.server or false
|
|
183
|
+
*/
|
|
184
|
+
const getHolder = (connection, parsed, data, name) => {
|
|
185
|
+
if (name === '') {
|
|
186
|
+
return state.server;
|
|
187
|
+
}
|
|
188
|
+
if (!state.mailboxes) {
|
|
189
|
+
connection.sendStatus(parsed, data, 'NO', 'Mailbox annotations are not supported, only server annotations');
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// RFC 5464 section 3.3: only mailboxes that exist and are returned by LIST. Annotations
|
|
193
|
+
// on \Noselect mailboxes are allowed (section 4.1)
|
|
194
|
+
const mailbox = connection.server.getMailbox(name);
|
|
195
|
+
if (!mailbox || mailbox.flags.indexOf('\\NonExistent') >= 0) {
|
|
196
|
+
connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT');
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return mailbox;
|
|
200
|
+
};
|
|
201
|
+
const bad = (connection, parsed, data, text, callback) => {
|
|
202
|
+
connection.sendStatus(parsed, data, 'BAD', text);
|
|
203
|
+
return callback();
|
|
204
|
+
};
|
|
205
|
+
// RFC 5464 section 4.2, options before the mailbox name as corrected by errata 2785 and 2786:
|
|
206
|
+
// getmetadata = "GETMETADATA" [SP getmetadata-options] SP mailbox SP entries
|
|
207
|
+
server.setCommandHandler('GETMETADATA', (connection, parsed, data, callback) => {
|
|
208
|
+
const attrs = parsed.attributes || [];
|
|
209
|
+
const offset = Array.isArray(attrs[0]) ? 1 : 0;
|
|
210
|
+
let maxSize = false;
|
|
211
|
+
let depth = 0;
|
|
212
|
+
if (attrs.length !== offset + 2 || !isAstring(attrs[offset])) {
|
|
213
|
+
return bad(connection, parsed, data, 'GETMETADATA expects options, a mailbox name and entries', callback);
|
|
214
|
+
}
|
|
215
|
+
if (offset) {
|
|
216
|
+
// getmetadata-options = "(" getmetadata-option *(SP getmetadata-option) ")"
|
|
217
|
+
const list = attrs[0];
|
|
218
|
+
if (!list.length) {
|
|
219
|
+
return bad(connection, parsed, data, 'Empty option list', callback);
|
|
220
|
+
}
|
|
221
|
+
for (let i = 0; i < list.length; i += 2) {
|
|
222
|
+
const label = list[i] && list[i].type === 'ATOM' ? list[i].value.toUpperCase() : false;
|
|
223
|
+
const value = list[i + 1] && list[i + 1].type === 'ATOM' ? list[i + 1].value : false;
|
|
224
|
+
if (label === 'MAXSIZE') {
|
|
225
|
+
// maxsize-opt = "MAXSIZE" SP number
|
|
226
|
+
if (!isNumber(value)) {
|
|
227
|
+
return bad(connection, parsed, data, 'Invalid value for MAXSIZE', callback);
|
|
228
|
+
}
|
|
229
|
+
maxSize = Number(value);
|
|
230
|
+
}
|
|
231
|
+
else if (label === 'DEPTH') {
|
|
232
|
+
// scope-opt = "DEPTH" SP ("0" / "1" / "infinity")
|
|
233
|
+
if (!value || !/^(0|1|infinity)$/i.test(value)) {
|
|
234
|
+
return bad(connection, parsed, data, 'Invalid value for DEPTH', callback);
|
|
235
|
+
}
|
|
236
|
+
depth = value.toLowerCase() === 'infinity' ? Infinity : Number(value);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
return bad(connection, parsed, data, 'Unknown GETMETADATA option', callback);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// the mailbox position depends on the options, so the name is converted here, not by mailboxArguments
|
|
244
|
+
let mailboxName;
|
|
245
|
+
try {
|
|
246
|
+
mailboxName = connection.importMailboxName(attrs[offset].value);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
return bad(connection, parsed, data, err.message, callback);
|
|
250
|
+
}
|
|
251
|
+
// entries = entry / "(" entry *(SP entry) ")"
|
|
252
|
+
const entryAttrs = [].concat(attrs[offset + 1]);
|
|
253
|
+
if (!entryAttrs.length || !entryAttrs.every(isAstring)) {
|
|
254
|
+
return bad(connection, parsed, data, 'GETMETADATA expects an entry name or a list of entry names', callback);
|
|
255
|
+
}
|
|
256
|
+
for (const attr of entryAttrs) {
|
|
257
|
+
const error = checkEntryName(attr.value);
|
|
258
|
+
if (error) {
|
|
259
|
+
// RFC 5464 section 3.2: "Invalid entry names result in a BAD response"
|
|
260
|
+
return bad(connection, parsed, data, error, callback);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const holder = getHolder(connection, parsed, data, mailboxName);
|
|
264
|
+
if (!holder) {
|
|
265
|
+
return callback();
|
|
266
|
+
}
|
|
267
|
+
const all = Object.assign(Object.create(null), getEntries(holder), getComputedEntries(holder));
|
|
268
|
+
const names = depth
|
|
269
|
+
? Object.keys(all)
|
|
270
|
+
.filter(name => all[name] !== null)
|
|
271
|
+
.sort()
|
|
272
|
+
: [];
|
|
273
|
+
// RFC 5464 section 4.2.2: the entry itself and the entries below it up to DEPTH
|
|
274
|
+
const found = new Map();
|
|
275
|
+
for (const attr of entryAttrs) {
|
|
276
|
+
const entry = attr.value.toLowerCase();
|
|
277
|
+
const value = all[entry] ?? null;
|
|
278
|
+
if (!depth || value !== null) {
|
|
279
|
+
// a requested entry without a value is returned as NIL
|
|
280
|
+
found.set(entry, value);
|
|
281
|
+
}
|
|
282
|
+
const prefix = entry + '/';
|
|
283
|
+
names.forEach(name => {
|
|
284
|
+
if (name.startsWith(prefix) && (depth === Infinity || name.indexOf('/', prefix.length) < 0)) {
|
|
285
|
+
found.set(name, all[name]);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// RFC 5464 section 4.2.1: larger values are left out, LONGENTRIES reports the biggest one
|
|
290
|
+
let longest = -1;
|
|
291
|
+
const pairs = [];
|
|
292
|
+
found.forEach((value, name) => {
|
|
293
|
+
if (maxSize !== false && value !== null && value.length > maxSize) {
|
|
294
|
+
longest = Math.max(longest, value.length);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
// RFC 5464 section 5: value = nstring / literal8, only binary data with NUL needs a literal8
|
|
298
|
+
pairs.push({ type: 'ATOM', value: name }, value === null ? null : { type: value.indexOf('\x00') >= 0 ? 'LITERAL8' : 'STRING', value });
|
|
299
|
+
});
|
|
300
|
+
if (pairs.length) {
|
|
301
|
+
connection.send({
|
|
302
|
+
tag: '*',
|
|
303
|
+
command: 'METADATA',
|
|
304
|
+
attributes: [mailboxAttribute(holder.path), pairs]
|
|
305
|
+
}, 'METADATA', parsed, data, holder);
|
|
306
|
+
}
|
|
307
|
+
connection.sendStatus(parsed, data, 'OK', 'GETMETADATA completed', longest >= 0 && ['METADATA', 'LONGENTRIES', longest]);
|
|
308
|
+
return callback();
|
|
309
|
+
}, { states: states.AUTHENTICATED });
|
|
310
|
+
// RFC 5464 section 4.3: setmetadata = "SETMETADATA" SP mailbox SP entry-values
|
|
311
|
+
server.setCommandHandler('SETMETADATA', (connection, parsed, data, callback) => {
|
|
312
|
+
const attrs = parsed.attributes || [];
|
|
313
|
+
if (attrs.length !== 2 || !isAstring(attrs[0]) || !Array.isArray(attrs[1])) {
|
|
314
|
+
return bad(connection, parsed, data, 'SETMETADATA expects a mailbox name and a list of entries and values', callback);
|
|
315
|
+
}
|
|
316
|
+
// entry-values = "(" entry-value *(SP entry-value) ")", entry-value = entry SP value
|
|
317
|
+
const list = attrs[1];
|
|
318
|
+
if (!list.length || list.length % 2) {
|
|
319
|
+
return bad(connection, parsed, data, 'SETMETADATA expects entry and value pairs', callback);
|
|
320
|
+
}
|
|
321
|
+
const changes = new Map();
|
|
322
|
+
for (let i = 0; i < list.length; i += 2) {
|
|
323
|
+
if (!isAstring(list[i])) {
|
|
324
|
+
return bad(connection, parsed, data, 'Entry name must be a string', callback);
|
|
325
|
+
}
|
|
326
|
+
const error = checkEntryName(list[i].value, true);
|
|
327
|
+
if (error) {
|
|
328
|
+
return bad(connection, parsed, data, error, callback);
|
|
329
|
+
}
|
|
330
|
+
// value = nstring / literal8, an atom is not a value
|
|
331
|
+
const value = list[i + 1];
|
|
332
|
+
if (value !== null && (!value || ['STRING', 'LITERAL', 'LITERAL8'].indexOf(value.type) < 0)) {
|
|
333
|
+
return bad(connection, parsed, data, 'Entry value must be a string, a literal, a literal8 or NIL', callback);
|
|
334
|
+
}
|
|
335
|
+
// RFC 5464 section 3.2: "Clients MUST use the CRLF (0x0D 0x0A) character octet sequence
|
|
336
|
+
// to represent line ends in a multi-line string value", binary data in a literal8 is not a string
|
|
337
|
+
if (value && value.type !== 'LITERAL8' && /\r(?!\n)|(?:^|[^\r])\n/.test(value.value)) {
|
|
338
|
+
return bad(connection, parsed, data, 'Line ends in a value must be CRLF', callback);
|
|
339
|
+
}
|
|
340
|
+
changes.set(list[i].value.toLowerCase(), value ? value.value : null);
|
|
341
|
+
}
|
|
342
|
+
const holder = getHolder(connection, parsed, data, attrs[0].value);
|
|
343
|
+
if (!holder) {
|
|
344
|
+
return callback();
|
|
345
|
+
}
|
|
346
|
+
const entries = getEntries(holder);
|
|
347
|
+
const computed = getComputedEntries(holder);
|
|
348
|
+
const fail = (text, code) => {
|
|
349
|
+
// RFC 5464 section 4.3: when one entry fails, no entry is changed
|
|
350
|
+
connection.sendStatus(parsed, data, 'NO', text, code);
|
|
351
|
+
return callback();
|
|
352
|
+
};
|
|
353
|
+
// changes go to a copy that replaces the entries once every check passed
|
|
354
|
+
const next = Object.assign(Object.create(null), entries);
|
|
355
|
+
const changed = [];
|
|
356
|
+
for (const [name, value] of changes) {
|
|
357
|
+
if ((!holder.path && READ_ONLY_SERVER_ENTRIES.has(name)) || name in computed) {
|
|
358
|
+
return fail('The ' + name + ' entry is read-only', 'CANNOT');
|
|
359
|
+
}
|
|
360
|
+
if (!state.allowPrivate && name.startsWith('/private/')) {
|
|
361
|
+
return fail('Private annotations are not supported', ['METADATA', 'NOPRIVATE']);
|
|
362
|
+
}
|
|
363
|
+
if (value !== null && value.length > state.maxSize) {
|
|
364
|
+
return fail('Value of ' + name + ' is too large', ['METADATA', 'MAXSIZE', state.maxSize]);
|
|
365
|
+
}
|
|
366
|
+
if ((entries[name] ?? null) !== value) {
|
|
367
|
+
changed.push(name);
|
|
368
|
+
}
|
|
369
|
+
if (value === null) {
|
|
370
|
+
delete next[name];
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
next[name] = value;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// only adding entries can fail, even when the storage already holds more than the limit
|
|
377
|
+
const count = Object.keys(next).length;
|
|
378
|
+
if (count > state.maxEntries && count > Object.keys(entries).length) {
|
|
379
|
+
return fail('Too many annotations', ['METADATA', 'TOOMANY']);
|
|
380
|
+
}
|
|
381
|
+
setEntries(holder, next);
|
|
382
|
+
if (changed.length) {
|
|
383
|
+
// RFC 5464 section 4.4.2: unsolicited responses only list the entry names. Section 4.1:
|
|
384
|
+
// only sessions that used ENABLE METADATA (or METADATA-SERVER) get them
|
|
385
|
+
server.notify({
|
|
386
|
+
tag: '*',
|
|
387
|
+
command: 'METADATA',
|
|
388
|
+
attributes: [mailboxAttribute(holder.path)].concat(changed.map(name => ({ type: 'ATOM', value: name })))
|
|
389
|
+
}, false, connection, isMetadataEnabled);
|
|
390
|
+
}
|
|
391
|
+
connection.sendStatus(parsed, data, 'OK', 'SETMETADATA completed');
|
|
392
|
+
return callback();
|
|
393
|
+
},
|
|
394
|
+
// RFC 5464 section 5: values can be literal8, with or without BINARY
|
|
395
|
+
{ states: states.AUTHENTICATED, mailboxArguments: [0], literal8: true });
|
|
396
|
+
server.outputHandlers.push((connection, response, description, parsed, data, extra) => {
|
|
397
|
+
if (response.command !== 'OK' || !parsed || !parsed.attributes) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (description === 'RENAME' && extra && extra.path === 'INBOX') {
|
|
401
|
+
// RFC 5464 section 4.1: renaming INBOX copies its annotations, INBOX keeps them. Other
|
|
402
|
+
// mailboxes are moved as objects, so their annotations follow without any help
|
|
403
|
+
const target = connection.server.getMailbox(parsed.attributes[1].value);
|
|
404
|
+
if (target && target !== extra && extra.metadata) {
|
|
405
|
+
setEntries(target, Object.assign(Object.create(null), getEntries(extra)));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else if (description === 'DELETE') {
|
|
409
|
+
// RFC 5464 section 4.1: a mailbox created later with the same name must not inherit the
|
|
410
|
+
// annotations. A mailbox with children stays as a \Noselect placeholder
|
|
411
|
+
const placeholder = connection.server.getMailbox(parsed.attributes[0].value);
|
|
412
|
+
if (placeholder) {
|
|
413
|
+
delete placeholder.metadata;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
export default function metadataPlugin(server) {
|
|
419
|
+
setup(server, true);
|
|
420
|
+
}
|
|
421
|
+
export { setup };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IMAPServer } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* @help Adds MOVE [RFC6851] capability
|
|
4
|
+
*
|
|
5
|
+
* MOVE: http://tools.ietf.org/html/rfc6851
|
|
6
|
+
*
|
|
7
|
+
* Additional commands:
|
|
8
|
+
* - MOVE
|
|
9
|
+
* - UID MOVE
|
|
10
|
+
*/
|
|
11
|
+
export default function movePlugin(server: IMAPServer): void;
|