@mavogel/awscdk-rootmail 0.1.26 → 0.1.28

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 (175) hide show
  1. package/.jsii +8 -8
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/node_modules/@aws-sdk/checksums/package.json +4 -4
  5. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +1 -1
  6. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/CloudWatchLogsClient.d.ts +2 -1
  7. package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +5 -5
  8. package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +1 -1
  9. package/node_modules/@aws-sdk/client-route-53/dist-types/Route53Client.d.ts +2 -1
  10. package/node_modules/@aws-sdk/client-route-53/package.json +6 -6
  11. package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +1 -1
  12. package/node_modules/@aws-sdk/client-s3/dist-types/S3Client.d.ts +2 -1
  13. package/node_modules/@aws-sdk/client-s3/package.json +9 -9
  14. package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +1 -1
  15. package/node_modules/@aws-sdk/client-ses/dist-types/SESClient.d.ts +2 -1
  16. package/node_modules/@aws-sdk/client-ses/package.json +5 -5
  17. package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +1 -1
  18. package/node_modules/@aws-sdk/client-ssm/dist-types/SSMClient.d.ts +2 -1
  19. package/node_modules/@aws-sdk/client-ssm/package.json +5 -5
  20. package/node_modules/@aws-sdk/core/package.json +3 -3
  21. package/node_modules/@aws-sdk/credential-provider-env/package.json +4 -4
  22. package/node_modules/@aws-sdk/credential-provider-http/package.json +4 -4
  23. package/node_modules/@aws-sdk/credential-provider-ini/package.json +11 -11
  24. package/node_modules/@aws-sdk/credential-provider-login/package.json +5 -5
  25. package/node_modules/@aws-sdk/credential-provider-node/package.json +9 -9
  26. package/node_modules/@aws-sdk/credential-provider-process/package.json +4 -4
  27. package/node_modules/@aws-sdk/credential-provider-sso/package.json +6 -6
  28. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +5 -5
  29. package/node_modules/@aws-sdk/middleware-sdk-route53/package.json +2 -2
  30. package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +5 -5
  31. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  32. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  33. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  34. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  35. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  36. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/CognitoIdentityClient.d.ts +2 -1
  37. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/SigninClient.d.ts +2 -1
  38. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/SSOClient.d.ts +2 -1
  39. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/SSOOIDCClient.d.ts +2 -1
  40. package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/STSClient.d.ts +2 -1
  41. package/node_modules/@aws-sdk/nested-clients/package.json +5 -5
  42. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
  43. package/node_modules/@aws-sdk/token-providers/package.json +5 -5
  44. package/node_modules/@aws-sdk/types/dist-types/feature-ids.d.ts +3 -0
  45. package/node_modules/@aws-sdk/types/dist-types/ts3.4/feature-ids.d.ts +3 -0
  46. package/node_modules/@aws-sdk/types/package.json +1 -1
  47. package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +15 -2
  48. package/node_modules/@smithy/core/dist-cjs/submodules/event-streams/index.browser.js +10 -0
  49. package/node_modules/@smithy/core/dist-cjs/submodules/event-streams/index.js +10 -0
  50. package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +5 -5
  51. package/node_modules/@smithy/core/dist-es/submodules/client/smithy-client/command.js +16 -2
  52. package/node_modules/@smithy/core/dist-es/submodules/event-streams/eventstream-codec/HeaderMarshaller.js +10 -0
  53. package/node_modules/@smithy/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeSerializer2.js +5 -5
  54. package/node_modules/@smithy/core/package.json +1 -1
  55. package/node_modules/@zone-eu/mailsplit/lib/message-splitter.js +39 -27
  56. package/node_modules/@zone-eu/mailsplit/lib/mime-node.d.ts +1 -1
  57. package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +38 -61
  58. package/node_modules/@zone-eu/mailsplit/lib/types.d.ts +1 -1
  59. package/node_modules/@zone-eu/mailsplit/package.json +2 -2
  60. package/node_modules/libmime/CHANGELOG.md +36 -0
  61. package/node_modules/libmime/README.md +40 -3
  62. package/node_modules/libmime/dist/cjs/charset.d.ts +36 -0
  63. package/node_modules/libmime/dist/cjs/charset.js +165 -2
  64. package/node_modules/libmime/dist/cjs/charsets.js +8 -2
  65. package/node_modules/libmime/dist/cjs/libmime.d.ts +20 -3
  66. package/node_modules/libmime/dist/cjs/libmime.js +100 -6
  67. package/node_modules/libmime/dist/cjs/types.d.ts +28 -0
  68. package/node_modules/libmime/dist/esm/charset.d.ts +36 -0
  69. package/node_modules/libmime/dist/esm/charset.js +162 -2
  70. package/node_modules/libmime/dist/esm/charsets.js +8 -2
  71. package/node_modules/libmime/dist/esm/libmime.d.ts +20 -3
  72. package/node_modules/libmime/dist/esm/libmime.js +100 -6
  73. package/node_modules/libmime/dist/esm/types.d.ts +28 -0
  74. package/node_modules/libmime/package.json +3 -3
  75. package/node_modules/mailparser/CHANGELOG.md +51 -0
  76. package/node_modules/mailparser/README.md +32 -1
  77. package/node_modules/mailparser/dist/cjs/address-header.d.ts +19 -0
  78. package/node_modules/mailparser/dist/cjs/address-header.js +185 -0
  79. package/node_modules/mailparser/dist/cjs/index.d.ts +3 -1
  80. package/node_modules/mailparser/dist/cjs/index.js +4 -2
  81. package/node_modules/mailparser/dist/cjs/mail-parser.js +36 -183
  82. package/node_modules/mailparser/dist/cjs/stream-hash.d.ts +5 -4
  83. package/node_modules/mailparser/dist/cjs/stream-hash.js +8 -5
  84. package/node_modules/mailparser/dist/cjs/types.d.ts +33 -6
  85. package/node_modules/mailparser/dist/esm/address-header.d.ts +19 -0
  86. package/node_modules/mailparser/dist/esm/address-header.js +176 -0
  87. package/node_modules/mailparser/dist/esm/index.d.ts +3 -1
  88. package/node_modules/mailparser/dist/esm/index.js +3 -2
  89. package/node_modules/mailparser/dist/esm/mail-parser.js +36 -183
  90. package/node_modules/mailparser/dist/esm/stream-hash.d.ts +5 -4
  91. package/node_modules/mailparser/dist/esm/stream-hash.js +8 -5
  92. package/node_modules/mailparser/dist/esm/types.d.ts +33 -6
  93. package/node_modules/mailparser/package.json +6 -7
  94. package/node_modules/nodemailer/CHANGELOG.md +23 -0
  95. package/node_modules/nodemailer/dist/cjs/addressparser/index.js +2 -5
  96. package/node_modules/nodemailer/dist/cjs/dkim/index.js +1 -3
  97. package/node_modules/nodemailer/dist/cjs/errors.d.ts +19 -0
  98. package/node_modules/nodemailer/dist/cjs/errors.js +14 -1
  99. package/node_modules/nodemailer/dist/cjs/fetch/cookies.js +2 -4
  100. package/node_modules/nodemailer/dist/cjs/fetch/index.js +2 -8
  101. package/node_modules/nodemailer/dist/cjs/mail-composer/index.js +42 -9
  102. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +5 -6
  103. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.d.ts +18 -1
  104. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +43 -1
  105. package/node_modules/nodemailer/dist/cjs/mime-funcs/mime-types.js +3 -12
  106. package/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +34 -1
  107. package/node_modules/nodemailer/dist/cjs/mime-node/index.js +40 -15
  108. package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
  109. package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
  110. package/node_modules/nodemailer/dist/cjs/qp/index.js +3 -12
  111. package/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +1 -3
  112. package/node_modules/nodemailer/dist/cjs/shared/index.js +2 -6
  113. package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +1 -1
  114. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +6 -0
  115. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +15 -12
  116. package/node_modules/nodemailer/dist/cjs/smtp-connection/server-message-id.d.ts +14 -0
  117. package/node_modules/nodemailer/dist/cjs/smtp-connection/server-message-id.js +53 -0
  118. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +11 -0
  119. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +25 -1
  120. package/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +2 -5
  121. package/node_modules/nodemailer/dist/esm/addressparser/index.js +2 -5
  122. package/node_modules/nodemailer/dist/esm/dkim/index.js +1 -3
  123. package/node_modules/nodemailer/dist/esm/errors.d.ts +19 -0
  124. package/node_modules/nodemailer/dist/esm/errors.js +12 -0
  125. package/node_modules/nodemailer/dist/esm/fetch/cookies.js +2 -4
  126. package/node_modules/nodemailer/dist/esm/fetch/index.js +2 -8
  127. package/node_modules/nodemailer/dist/esm/mail-composer/index.js +42 -9
  128. package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +5 -6
  129. package/node_modules/nodemailer/dist/esm/mime-funcs/index.d.ts +18 -1
  130. package/node_modules/nodemailer/dist/esm/mime-funcs/index.js +43 -1
  131. package/node_modules/nodemailer/dist/esm/mime-funcs/mime-types.js +3 -12
  132. package/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +34 -1
  133. package/node_modules/nodemailer/dist/esm/mime-node/index.js +40 -15
  134. package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
  135. package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
  136. package/node_modules/nodemailer/dist/esm/qp/index.js +3 -12
  137. package/node_modules/nodemailer/dist/esm/sendmail-transport/index.js +1 -3
  138. package/node_modules/nodemailer/dist/esm/shared/index.js +2 -6
  139. package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +1 -1
  140. package/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +6 -0
  141. package/node_modules/nodemailer/dist/esm/smtp-connection/index.js +16 -13
  142. package/node_modules/nodemailer/dist/esm/smtp-connection/server-message-id.d.ts +14 -0
  143. package/node_modules/nodemailer/dist/esm/smtp-connection/server-message-id.js +50 -0
  144. package/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +11 -0
  145. package/node_modules/nodemailer/dist/esm/smtp-pool/index.js +25 -1
  146. package/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.js +2 -5
  147. package/node_modules/nodemailer/package.json +11 -9
  148. package/package.json +9 -9
  149. package/node_modules/mailparser/node_modules/iconv-lite/LICENSE +0 -21
  150. package/node_modules/mailparser/node_modules/iconv-lite/README.md +0 -138
  151. package/node_modules/mailparser/node_modules/iconv-lite/encodings/dbcs-codec.js +0 -532
  152. package/node_modules/mailparser/node_modules/iconv-lite/encodings/dbcs-data.js +0 -185
  153. package/node_modules/mailparser/node_modules/iconv-lite/encodings/index.js +0 -23
  154. package/node_modules/mailparser/node_modules/iconv-lite/encodings/internal.js +0 -218
  155. package/node_modules/mailparser/node_modules/iconv-lite/encodings/sbcs-codec.js +0 -75
  156. package/node_modules/mailparser/node_modules/iconv-lite/encodings/sbcs-data-generated.js +0 -451
  157. package/node_modules/mailparser/node_modules/iconv-lite/encodings/sbcs-data.js +0 -180
  158. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/big5-added.json +0 -122
  159. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/cp936.json +0 -264
  160. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/cp949.json +0 -273
  161. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/cp950.json +0 -177
  162. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/eucjp.json +0 -182
  163. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +0 -1
  164. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/gbk-added.json +0 -56
  165. package/node_modules/mailparser/node_modules/iconv-lite/encodings/tables/shiftjis.json +0 -125
  166. package/node_modules/mailparser/node_modules/iconv-lite/encodings/utf16.js +0 -187
  167. package/node_modules/mailparser/node_modules/iconv-lite/encodings/utf32.js +0 -314
  168. package/node_modules/mailparser/node_modules/iconv-lite/encodings/utf7.js +0 -283
  169. package/node_modules/mailparser/node_modules/iconv-lite/lib/bom-handling.js +0 -48
  170. package/node_modules/mailparser/node_modules/iconv-lite/lib/helpers/merge-exports.js +0 -13
  171. package/node_modules/mailparser/node_modules/iconv-lite/lib/index.d.ts +0 -129
  172. package/node_modules/mailparser/node_modules/iconv-lite/lib/index.js +0 -182
  173. package/node_modules/mailparser/node_modules/iconv-lite/lib/streams.js +0 -105
  174. package/node_modules/mailparser/node_modules/iconv-lite/package.json +0 -70
  175. package/node_modules/mailparser/node_modules/iconv-lite/types/encodings.d.ts +0 -425
@@ -5,17 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const mailsplit_1 = __importDefault(require("@zone-eu/mailsplit"));
7
7
  const flowed_decoder_js_1 = __importDefault(require("@zone-eu/mailsplit/lib/flowed-decoder.js"));
8
- const encoding_japanese_1 = __importDefault(require("encoding-japanese"));
9
8
  const he_1 = __importDefault(require("he"));
10
9
  const html_to_text_1 = require("html-to-text");
11
- const iconv_lite_1 = __importDefault(require("iconv-lite"));
12
10
  const libmime_1 = __importDefault(require("libmime"));
13
11
  const linkify_it_1 = __importDefault(require("linkify-it"));
14
12
  const addressparser_1 = __importDefault(require("nodemailer/lib/addressparser"));
15
- const punycode_js_1 = __importDefault(require("punycode.js"));
16
13
  const node_stream_1 = require("node:stream");
17
14
  const tlds_cjs_1 = __importDefault(require("./tlds.cjs"));
18
15
  const stream_hash_js_1 = __importDefault(require("./stream-hash.js"));
16
+ const address_header_js_1 = require("./address-header.js");
19
17
  const { Splitter, ChunkedPassthrough } = mailsplit_1.default;
20
18
  // Bounds for link detection in text bodies. Message content is untrusted and
21
19
  // linkify-it backtracks heavily on crafted input: matching a host is quadratic
@@ -77,49 +75,6 @@ linkify.add('@', {
77
75
  match.url = 'https://twitter.com/' + match.url.replace(/^@/, '');
78
76
  }
79
77
  });
80
- // Lists the parameters a header value gives in RFC 2231 form (name*=, name*0= or
81
- // name*0*=). libmime decodes those while parsing the header, so the result must not go
82
- // through decodeWords as well: filename*=utf-8''a%3D%3Futf-8%3FQ%3F%3D2Eexe%3F%3D would
83
- // otherwise come out as "a.exe" while a parser that decodes once sees the encoded word.
84
- function rfc2231ParamNames(value) {
85
- let names = new Set();
86
- let quote = false;
87
- let escaped = false;
88
- let start = 0;
89
- let addSegment = (end) => {
90
- let segment = value.substring(start, end);
91
- let eq = segment.indexOf('=');
92
- if (eq < 0) {
93
- return;
94
- }
95
- let match = segment
96
- .substring(0, eq)
97
- .trim()
98
- .toLowerCase()
99
- .match(/^(.+?)\*(\d+\*?)?$/);
100
- if (match) {
101
- names.add(match[1]);
102
- }
103
- };
104
- for (let i = 0; i < value.length; i++) {
105
- let chr = value.charAt(i);
106
- if (escaped) {
107
- escaped = false;
108
- }
109
- else if (chr === '\\') {
110
- escaped = true;
111
- }
112
- else if (chr === '"') {
113
- quote = !quote;
114
- }
115
- else if (chr === ';' && !quote) {
116
- addSegment(i);
117
- start = i + 1;
118
- }
119
- }
120
- addSegment(value.length);
121
- return names;
122
- }
123
78
  // Deepest element nesting html-to-text is asked to convert. htmlparser2 keeps its open
124
79
  // element stack at the front of an array, so parsing alone is quadratic in the nesting
125
80
  // depth (200k nested <b> took 9 s), and html-to-text's recursive walk overflows the call
@@ -189,6 +144,14 @@ function exceedsHtmlNestingDepth(html, maxDepth) {
189
144
  }
190
145
  return false;
191
146
  }
147
+ // Charsets a body is never decoded from, matched the way iconv-lite and node-iconv compare names
148
+ // (case and punctuation ignored). UTF-7 can carry markup past a filter that looks for "<" (a body
149
+ // of +ADw-script+AD4- decodes to <script>), base64 and hex are transfer encodings. libmime's decode
150
+ // stream refuses the same names, this check applies the refusal to the native Iconv option as well
151
+ const REFUSED_CHARSET = /^(?:(?:cs)?unicode11)?utf7(?:imap)?$|^base64$|^hex$/;
152
+ function isRefusedCharset(charset) {
153
+ return REFUSED_CHARSET.test((charset || '').toLowerCase().replace(/[^a-z0-9]/g, ''));
154
+ }
192
155
  class IconvDecoder extends node_stream_1.Transform {
193
156
  constructor(Iconv, charset) {
194
157
  super();
@@ -218,41 +181,6 @@ class IconvDecoder extends node_stream_1.Transform {
218
181
  this.stream.end();
219
182
  }
220
183
  }
221
- class JPDecoder extends node_stream_1.Transform {
222
- constructor(charset) {
223
- super();
224
- this.charset = charset;
225
- this.chunks = [];
226
- this.chunklen = 0;
227
- }
228
- _transform(chunk, encoding, done) {
229
- if (typeof chunk === 'string') {
230
- chunk = Buffer.from(chunk, encoding);
231
- }
232
- this.chunks.push(chunk);
233
- this.chunklen += chunk.length;
234
- done();
235
- }
236
- _flush(done) {
237
- let input = Buffer.concat(this.chunks, this.chunklen);
238
- try {
239
- let output = encoding_japanese_1.default.convert(input, {
240
- to: 'UNICODE', // to_encoding
241
- from: this.charset, // from_encoding
242
- type: 'string'
243
- });
244
- if (typeof output === 'string') {
245
- output = Buffer.from(output);
246
- }
247
- this.push(output);
248
- }
249
- catch {
250
- // keep as is on errors
251
- this.push(input);
252
- }
253
- done();
254
- }
255
- }
256
184
  /**
257
185
  * A lower level, streaming e-mail parser. It is a Transform stream: write the raw message in and
258
186
  * read the parsed content out. It emits:
@@ -328,6 +256,10 @@ class MailParser extends node_stream_1.Transform {
328
256
  // create wrapper
329
257
  return {
330
258
  decodeStream(charset) {
259
+ if (isRefusedCharset(charset)) {
260
+ // the same refusal as without the Iconv option, see below
261
+ return libmime_1.default.createDecodeStream(charset);
262
+ }
331
263
  // node-iconv resolves its own charset aliases and needs charset-specific
332
264
  // fixups (see IconvDecoder, eg. ks_c_5601-1987), so pass the charset through as is
333
265
  return new IconvDecoder(Iconv, charset);
@@ -337,13 +269,12 @@ class MailParser extends node_stream_1.Transform {
337
269
  else {
338
270
  return {
339
271
  decodeStream(charset) {
340
- // resolve charset aliases (eg. iso-8859-8-i -> ISO-8859-8) via libmime's canonical table
341
- charset = libmime_1.default.normalizeCharset((charset || 'ascii').toString());
342
- if (/^jis|^iso-?2022-?jp/i.test(charset)) {
343
- // special case not supported by iconv-lite
344
- return new JPDecoder(charset);
345
- }
346
- return iconv_lite_1.default.decodeStream(charset);
272
+ // libmime resolves charset aliases (eg. iso-8859-8-i -> ISO-8859-8), decodes the
273
+ // Japanese charsets iconv-lite lacks and keeps a multibyte character split across
274
+ // chunks intact. A charset it does not know or refuses (UTF-7, which would turn
275
+ // +ADw-script+AD4- into <script>, and the base64 and hex transfer encodings) is
276
+ // decoded as UTF-8 with invalid bytes replaced, the same as encoded words get
277
+ return libmime_1.default.createDecodeStream(charset || 'ascii');
347
278
  }
348
279
  };
349
280
  }
@@ -446,9 +377,18 @@ class MailParser extends node_stream_1.Transform {
446
377
  case 'content-type':
447
378
  case 'content-disposition':
448
379
  case 'dkim-signature': {
449
- // a parameter given in RFC 2231 form is decoded by parseHeaderValue already
450
- let rfc2231 = rfc2231ParamNames(value);
451
- value = this.libmime.parseHeaderValue(value);
380
+ // The comments of a MIME header are not part of its value, so they are
381
+ // dropped as mailsplit drops them for the structure: `boundary=b (comment)`
382
+ // names the boundary `b`. A DKIM-Signature is a tag list without comment
383
+ // syntax, so a parenthesis in a tag value is data
384
+ value = this.libmime.parseHeaderValue(value, { stripComments: key !== 'dkim-signature' });
385
+ // A parameter given in RFC 2231 form was decoded by parseHeaderValue already,
386
+ // which names it in decodedParams, and must not go through decodeWords as
387
+ // well: filename*=utf-8''a%3D%3Futf-8%3FQ%3F%3D2Eexe%3F%3D would otherwise come
388
+ // out as a.exe while a parser that decodes once sees the encoded word. A
389
+ // second scan of the raw header for the name used to decide this, and read
390
+ // `(c)filename*=` as `(c)filename` once the comment was dropped from the real one
391
+ let rfc2231 = new Set(value.decodedParams);
452
392
  if (value.value) {
453
393
  value.value = this.libmime.decodeWords(value.value);
454
394
  }
@@ -681,52 +621,7 @@ class MailParser extends node_stream_1.Transform {
681
621
  }
682
622
  /** @internal */
683
623
  decodeAddresses(addresses) {
684
- for (let i = 0; i < addresses.length; i++) {
685
- let address = addresses[i];
686
- address.name = (address.name || '').toString().trim();
687
- // An encoded word decodes into the display name and nothing else, whatever it
688
- // decodes to. RFC 2047 section 5 keeps encoded words out of the addr-spec, so a
689
- // name that decodes to "CEO" <ceo@bank.com> is still only a name: parsing it
690
- // again would report an address the header never carried
691
- if (address.name) {
692
- try {
693
- address.name = this.libmime.decodeWords(address.name);
694
- }
695
- catch {
696
- //ignore, keep as is
697
- }
698
- }
699
- // Security fix: detect RFC 2047 encoded-words in the address field.
700
- // Per RFC 2047 Section 5, encoded-words MUST NOT appear in addr-spec.
701
- // If found, decode and validate; reject if result is not a simple local@domain.
702
- if (address.address && /[=]\?[^?]+\?[BbQq]\?[^?]*\?[=]/.test(address.address)) {
703
- try {
704
- let decodedAddr = this.libmime.decodeWords(address.address);
705
- if (/^[^\s@]+@[^\s@]+$/.test(decodedAddr) && !/[=]\?/.test(decodedAddr)) {
706
- address.address = decodedAddr;
707
- }
708
- else {
709
- address.address = '';
710
- }
711
- }
712
- catch {
713
- address.address = '';
714
- }
715
- }
716
- if (address.address && /@xn--/.test(address.address)) {
717
- try {
718
- address.address =
719
- address.address.slice(0, address.address.lastIndexOf('@') + 1) +
720
- punycode_js_1.default.toUnicode(address.address.slice(address.address.lastIndexOf('@') + 1));
721
- }
722
- catch {
723
- // Not a valid punycode string; keep as is
724
- }
725
- }
726
- if (address.group) {
727
- this.decodeAddresses(address.group);
728
- }
729
- }
624
+ (0, address_header_js_1.decodeAddresses)(addresses, this.libmime);
730
625
  }
731
626
  /** @internal */
732
627
  createNode(node) {
@@ -1008,7 +903,8 @@ class MailParser extends node_stream_1.Transform {
1008
903
  this.waitUntilAttachmentEnd = false;
1009
904
  }
1010
905
  };
1011
- let algo = this.options.checksumAlgo || 'md5';
906
+ // false skips hashing, checksum is then left unset
907
+ let algo = this.options.checksumAlgo === false ? false : this.options.checksumAlgo || 'md5';
1012
908
  let hasher;
1013
909
  try {
1014
910
  hasher = new stream_hash_js_1.default(attachment, algo);
@@ -1161,56 +1057,13 @@ class MailParser extends node_stream_1.Transform {
1161
1057
  * Formats parsed addresses for an HTML context, every value HTML-escaped.
1162
1058
  */
1163
1059
  getAddressesHTML(value) {
1164
- let formatSingleLevel = (addresses) => addresses
1165
- .map(address => {
1166
- let str = '<span class="mp_address_group">';
1167
- if (address.name) {
1168
- str += '<span class="mp_address_name">' + he_1.default.encode(address.name) + (address.group ? ': ' : '') + '</span>';
1169
- }
1170
- if (address.address) {
1171
- let link = '<a href="mailto:' + he_1.default.encode(address.address) + '" class="mp_address_email">' + he_1.default.encode(address.address) + '</a>';
1172
- if (address.name) {
1173
- str += ' &lt;' + link + '&gt;';
1174
- }
1175
- else {
1176
- str += link;
1177
- }
1178
- }
1179
- if (address.group) {
1180
- str += formatSingleLevel(address.group) + ';';
1181
- }
1182
- return str + '</span>';
1183
- })
1184
- .join(', ');
1185
- return formatSingleLevel([].concat(value || []));
1060
+ return (0, address_header_js_1.formatAddressesHTML)(value);
1186
1061
  }
1187
1062
  /**
1188
1063
  * Formats parsed addresses as plain text, display names quoted.
1189
1064
  */
1190
1065
  getAddressesText(value) {
1191
- let formatSingleLevel = (addresses) => addresses
1192
- .map(address => {
1193
- let str = '';
1194
- if (address.name) {
1195
- // a quoted-string, so a quote or backslash inside the name is escaped
1196
- str += `"${address.name.replace(/["\\]/g, '\\$&')}"` + (address.group ? ': ' : '');
1197
- }
1198
- if (address.address) {
1199
- let link = address.address;
1200
- if (address.name) {
1201
- str += ' <' + link + '>';
1202
- }
1203
- else {
1204
- str += link;
1205
- }
1206
- }
1207
- if (address.group) {
1208
- str += formatSingleLevel(address.group) + ';';
1209
- }
1210
- return str;
1211
- })
1212
- .join(', ');
1213
- return formatSingleLevel([].concat(value || []));
1066
+ return (0, address_header_js_1.formatAddressesText)(value);
1214
1067
  }
1215
1068
  /**
1216
1069
  * Replaces `cid:` links to image attachments in the HTML content. Call it once the parser has
@@ -10,14 +10,15 @@ export interface HashTarget {
10
10
  }
11
11
  /**
12
12
  * A pass-through stream that records the checksum and byte size of what flows through it on the
13
- * given object once the stream ends. Throws when the hash algorithm is not available.
13
+ * given object once the stream ends. Throws when the hash algorithm is not available. With `algo`
14
+ * set to `false` nothing is hashed and only the size is recorded.
14
15
  */
15
16
  declare class StreamHash extends Transform {
16
17
  attachment: HashTarget;
17
- algo: string;
18
- hash: crypto.Hash;
18
+ algo: string | false;
19
+ hash: crypto.Hash | null;
19
20
  byteCount: number;
20
- constructor(attachment: HashTarget, algo?: string);
21
+ constructor(attachment: HashTarget, algo?: string | false);
21
22
  _transform(chunk: Buffer, encoding: BufferEncoding, done: TransformCallback): void;
22
23
  _flush(done: TransformCallback): void;
23
24
  }
@@ -7,23 +7,26 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
7
7
  const node_stream_1 = require("node:stream");
8
8
  /**
9
9
  * A pass-through stream that records the checksum and byte size of what flows through it on the
10
- * given object once the stream ends. Throws when the hash algorithm is not available.
10
+ * given object once the stream ends. Throws when the hash algorithm is not available. With `algo`
11
+ * set to `false` nothing is hashed and only the size is recorded.
11
12
  */
12
13
  class StreamHash extends node_stream_1.Transform {
13
14
  constructor(attachment, algo) {
14
15
  super();
15
16
  this.attachment = attachment;
16
- this.algo = (algo || 'md5').toLowerCase();
17
- this.hash = node_crypto_1.default.createHash(this.algo);
17
+ this.algo = algo === false ? false : (algo || 'md5').toLowerCase();
18
+ this.hash = this.algo === false ? null : node_crypto_1.default.createHash(this.algo);
18
19
  this.byteCount = 0;
19
20
  }
20
21
  _transform(chunk, encoding, done) {
21
- this.hash.update(chunk);
22
+ this.hash?.update(chunk);
22
23
  this.byteCount += chunk.length;
23
24
  done(null, chunk);
24
25
  }
25
26
  _flush(done) {
26
- this.attachment.checksum = this.hash.digest('hex');
27
+ if (this.hash) {
28
+ this.attachment.checksum = this.hash.digest('hex');
29
+ }
27
30
  this.attachment.size = this.byteCount;
28
31
  done();
29
32
  }
@@ -37,6 +37,24 @@ export interface AddressObject {
37
37
  /** The addresses formatted as plain text, display names quoted. */
38
38
  text: string;
39
39
  }
40
+ /**
41
+ * Options for `parseAddressHeader`.
42
+ */
43
+ export interface ParseAddressHeaderOptions {
44
+ /**
45
+ * Replace every group with its members, so the result lists mailboxes only and the group names
46
+ * are dropped.
47
+ * @default false
48
+ */
49
+ flatten?: boolean;
50
+ /**
51
+ * Decode an internationalized (punycode) domain to Unicode. Set to false to keep the domain as
52
+ * it appears in the header, for an address that is going to be used to send mail, such as an
53
+ * SMTP envelope, where the ASCII form is the one every server accepts.
54
+ * @default true
55
+ */
56
+ decodePunycode?: boolean;
57
+ }
40
58
  /**
41
59
  * One entry of the combined `list` header, built from a `List-*` header such as `List-Unsubscribe`.
42
60
  */
@@ -122,7 +140,10 @@ export interface AttachmentStream extends AttachmentCommon {
122
140
  * message. Must be called for every attachment. Set to `null` once called.
123
141
  */
124
142
  release: (() => void) | null;
125
- /** The hex encoded checksum of the content (md5 unless `checksumAlgo` says otherwise). Set once `content` has ended. */
143
+ /**
144
+ * The hex encoded checksum of the content (md5 unless `checksumAlgo` says otherwise). Set once
145
+ * `content` has ended, never when `checksumAlgo` is `false`.
146
+ */
126
147
  checksum?: string;
127
148
  /** The size of the decoded content in bytes. Set once `content` has ended. */
128
149
  size?: number;
@@ -135,7 +156,11 @@ export interface Attachment extends AttachmentCommon {
135
156
  content: Buffer;
136
157
  /** Always `null`, `simpleParser` has released the attachment already. */
137
158
  release: null;
138
- /** The hex encoded checksum of the content (md5 unless `checksumAlgo` says otherwise). */
159
+ /**
160
+ * The hex encoded checksum of the content (md5 unless `checksumAlgo` says otherwise). Not set when
161
+ * `checksumAlgo` is `false`: the type keeps it a string so code written for the default does not
162
+ * have to check it, so a caller that turns hashing off must not read it.
163
+ */
139
164
  checksum: string;
140
165
  /** The size of the decoded content in bytes. */
141
166
  size: number;
@@ -211,16 +236,18 @@ export interface MailParserOptions {
211
236
  */
212
237
  keepDeliveryStatus?: boolean;
213
238
  /**
214
- * Decode character sets with the native `iconv` module instead of `iconv-lite`. Pass the
215
- * `Iconv` class: `{ Iconv: require('iconv').Iconv }`.
239
+ * Decode the character sets of text bodies with the native `iconv` module instead of libmime's
240
+ * decoder (`iconv-lite`). Pass the `Iconv` class: `{ Iconv: require('iconv').Iconv }`. Bodies
241
+ * declared as UTF-7 are never decoded from it either way, see the note on charsets in the README.
216
242
  */
217
243
  Iconv?: IconvConstructor;
218
244
  /**
219
245
  * The hash algorithm for attachment checksums, any name `crypto.createHash()` accepts. An algorithm
220
- * that is not available makes the parser emit an error.
246
+ * that is not available makes the parser emit an error. `false` skips hashing attachments
247
+ * altogether, and their `checksum` is then not set (`size` still is).
221
248
  * @default 'md5'
222
249
  */
223
- checksumAlgo?: string;
250
+ checksumAlgo?: string | false;
224
251
  /**
225
252
  * The longest the URLs `updateImageLinks()` writes into the HTML may be in total, in characters.
226
253
  * References past the limit are left as `cid:` links.
@@ -0,0 +1,19 @@
1
+ import type { AddressObject, ParseAddressHeaderOptions } from './types.js';
2
+ /**
3
+ * Parses the value of an address header (`From`, `To`, `Cc`, `Reply-To` and the like) into the
4
+ * {@link AddressObject} `MailParser` and `simpleParser` produce for one: display names have their
5
+ * encoded words decoded, an internationalized (punycode) domain is decoded to Unicode (unless
6
+ * `decodePunycode` is false), and an
7
+ * address part that hides an encoded word is dropped unless it decodes to a plain `local@domain`
8
+ * (RFC 2047 keeps encoded words out of the address, so `=?utf-8?B?...?=@example.com` naming
9
+ * another address is not taken at its word).
10
+ *
11
+ * The value is the header value without the `Name:` prefix, as a string. Line folding may be left
12
+ * in. Raw 8-bit bytes have to be decoded into the string first, eg. `buffer.toString('utf-8')`.
13
+ * Several values (a header that occurs more than once) can be given as an array: each is parsed
14
+ * on its own and the addresses are listed in order.
15
+ *
16
+ * @param value The header value, or several of them
17
+ * @param options See {@link ParseAddressHeaderOptions}
18
+ */
19
+ export declare function parseAddressHeader(value: string | string[] | null | undefined, options?: ParseAddressHeaderOptions): AddressObject;
@@ -0,0 +1,176 @@
1
+ import he from 'he';
2
+ import libmime from 'libmime';
3
+ import addressparser from 'nodemailer/lib/addressparser';
4
+ import punycode from 'punycode.js';
5
+ // An RFC 2047 encoded word
6
+ const ENCODED_WORD = /[=]\?[^?]+\?[BbQq]\?[^?]*\?[=]/;
7
+ /**
8
+ * Decodes the addresses addressparser produced, in place: encoded words in display names, an
9
+ * internationalized (punycode) domain, and the members of a group.
10
+ *
11
+ * @internal
12
+ */
13
+ export function decodeAddresses(addresses, decoder = libmime, decodePunycode = true) {
14
+ for (let address of addresses) {
15
+ address.name = (address.name || '').toString().trim();
16
+ // An encoded word decodes into the display name and nothing else, whatever it
17
+ // decodes to. RFC 2047 section 5 keeps encoded words out of the addr-spec, so a
18
+ // name that decodes to "CEO" <ceo@bank.com> is still only a name: parsing it
19
+ // again would report an address the header never carried
20
+ if (address.name) {
21
+ try {
22
+ address.name = decoder.decodeWords(address.name);
23
+ }
24
+ catch {
25
+ //ignore, keep as is
26
+ }
27
+ }
28
+ // Security fix: detect RFC 2047 encoded-words in the address field.
29
+ // Per RFC 2047 Section 5, encoded-words MUST NOT appear in addr-spec.
30
+ // If found, decode and validate; reject if result is not a simple local@domain.
31
+ if (address.address && ENCODED_WORD.test(address.address)) {
32
+ try {
33
+ let decodedAddr = decoder.decodeWords(address.address);
34
+ if (/^[^\s@]+@[^\s@]+$/.test(decodedAddr) && !/[=]\?/.test(decodedAddr)) {
35
+ address.address = decodedAddr;
36
+ }
37
+ else {
38
+ address.address = '';
39
+ }
40
+ }
41
+ catch {
42
+ address.address = '';
43
+ }
44
+ }
45
+ if (decodePunycode && address.address && /@xn--/.test(address.address)) {
46
+ try {
47
+ address.address =
48
+ address.address.slice(0, address.address.lastIndexOf('@') + 1) +
49
+ punycode.toUnicode(address.address.slice(address.address.lastIndexOf('@') + 1));
50
+ }
51
+ catch {
52
+ // Not a valid punycode string; keep as is
53
+ }
54
+ }
55
+ if (address.group) {
56
+ decodeAddresses(address.group, decoder, decodePunycode);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Formats parsed addresses for an HTML context, every value HTML-escaped.
62
+ *
63
+ * @internal
64
+ */
65
+ export function formatAddressesHTML(value) {
66
+ let formatSingleLevel = (addresses) => addresses
67
+ .map(address => {
68
+ let str = '<span class="mp_address_group">';
69
+ if (address.name) {
70
+ str += '<span class="mp_address_name">' + he.encode(address.name) + (address.group ? ': ' : '') + '</span>';
71
+ }
72
+ if (address.address) {
73
+ let link = '<a href="mailto:' + he.encode(address.address) + '" class="mp_address_email">' + he.encode(address.address) + '</a>';
74
+ if (address.name) {
75
+ str += ' &lt;' + link + '&gt;';
76
+ }
77
+ else {
78
+ str += link;
79
+ }
80
+ }
81
+ if (address.group) {
82
+ str += formatSingleLevel(address.group) + ';';
83
+ }
84
+ return str + '</span>';
85
+ })
86
+ .join(', ');
87
+ return formatSingleLevel([].concat(value || []));
88
+ }
89
+ /**
90
+ * Formats parsed addresses as plain text, display names quoted.
91
+ *
92
+ * @internal
93
+ */
94
+ export function formatAddressesText(value) {
95
+ let formatSingleLevel = (addresses) => addresses
96
+ .map(address => {
97
+ let str = '';
98
+ if (address.name) {
99
+ // a quoted-string, so a quote or backslash inside the name is escaped
100
+ str += `"${address.name.replace(/["\\]/g, '\\$&')}"` + (address.group ? ': ' : '');
101
+ }
102
+ if (address.address) {
103
+ let link = address.address;
104
+ if (address.name) {
105
+ str += ' <' + link + '>';
106
+ }
107
+ else {
108
+ str += link;
109
+ }
110
+ }
111
+ if (address.group) {
112
+ str += formatSingleLevel(address.group) + ';';
113
+ }
114
+ return str;
115
+ })
116
+ .join(', ');
117
+ return formatSingleLevel([].concat(value || []));
118
+ }
119
+ /**
120
+ * Parses the value of an address header (`From`, `To`, `Cc`, `Reply-To` and the like) into the
121
+ * {@link AddressObject} `MailParser` and `simpleParser` produce for one: display names have their
122
+ * encoded words decoded, an internationalized (punycode) domain is decoded to Unicode (unless
123
+ * `decodePunycode` is false), and an
124
+ * address part that hides an encoded word is dropped unless it decodes to a plain `local@domain`
125
+ * (RFC 2047 keeps encoded words out of the address, so `=?utf-8?B?...?=@example.com` naming
126
+ * another address is not taken at its word).
127
+ *
128
+ * The value is the header value without the `Name:` prefix, as a string. Line folding may be left
129
+ * in. Raw 8-bit bytes have to be decoded into the string first, eg. `buffer.toString('utf-8')`.
130
+ * Several values (a header that occurs more than once) can be given as an array: each is parsed
131
+ * on its own and the addresses are listed in order.
132
+ *
133
+ * @param value The header value, or several of them
134
+ * @param options See {@link ParseAddressHeaderOptions}
135
+ */
136
+ export function parseAddressHeader(value, options) {
137
+ let addresses = [];
138
+ for (let entry of [].concat(value)) {
139
+ if (typeof entry !== 'string' || !entry) {
140
+ continue;
141
+ }
142
+ let parsed = addressparser(entry, { flatten: !!options?.flatten });
143
+ decodeAddresses(parsed, libmime, options?.decodePunycode !== false);
144
+ for (let address of parsed) {
145
+ addresses.push(address);
146
+ }
147
+ }
148
+ return withLazyFormats(addresses);
149
+ }
150
+ /**
151
+ * The AddressObject for parsed addresses, with `html` and `text` built on first read: a caller
152
+ * that only wants the addresses (eg. for an SMTP envelope) pays for neither. Both stay enumerable
153
+ * and assignable, to read, compare, serialize and overwrite like the plain values they were.
154
+ */
155
+ function withLazyFormats(addresses) {
156
+ let html;
157
+ let text;
158
+ const result = { value: addresses };
159
+ Object.defineProperties(result, {
160
+ html: {
161
+ enumerable: true,
162
+ get: () => (html ??= formatAddressesHTML(addresses)),
163
+ set: value => {
164
+ html = value;
165
+ }
166
+ },
167
+ text: {
168
+ enumerable: true,
169
+ get: () => (text ??= formatAddressesText(addresses)),
170
+ set: value => {
171
+ text = value;
172
+ }
173
+ }
174
+ });
175
+ return result;
176
+ }
@@ -1,10 +1,12 @@
1
1
  import MailParser from './mail-parser.js';
2
2
  import simpleParser from './simple-parser.js';
3
- export { MailParser, simpleParser };
3
+ import { parseAddressHeader } from './address-header.js';
4
+ export { MailParser, simpleParser, parseAddressHeader };
4
5
  export type { SimpleParserCallback } from './simple-parser.js';
5
6
  export type * from './types.js';
6
7
  declare const _default: {
7
8
  MailParser: typeof MailParser;
8
9
  simpleParser: typeof simpleParser;
10
+ parseAddressHeader: typeof parseAddressHeader;
9
11
  };
10
12
  export default _default;
@@ -1,4 +1,5 @@
1
1
  import MailParser from './mail-parser.js';
2
2
  import simpleParser from './simple-parser.js';
3
- export { MailParser, simpleParser };
4
- export default { MailParser, simpleParser };
3
+ import { parseAddressHeader } from './address-header.js';
4
+ export { MailParser, simpleParser, parseAddressHeader };
5
+ export default { MailParser, simpleParser, parseAddressHeader };