@mavogel/awscdk-rootmail 0.1.25 → 0.1.26

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 (170) hide show
  1. package/.jsii +9 -9
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/lib/ses-receive.ops-santa-handler.js +2 -2
  5. package/node_modules/@aws-sdk/checksums/package.json +3 -3
  6. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +9 -7
  7. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/schemas/schemas_0.js +11 -6
  8. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveriesCommand.d.ts +3 -0
  9. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveryDestinationsCommand.d.ts +3 -0
  10. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliverySourcesCommand.d.ts +3 -0
  11. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/PutResourcePolicyCommand.d.ts +1 -1
  12. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/models_0.d.ts +27 -1
  13. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/models_0.d.ts +3 -0
  14. package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +4 -4
  15. package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +1 -1
  16. package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
  17. package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +1 -1
  18. package/node_modules/@aws-sdk/client-s3/package.json +7 -7
  19. package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +1 -1
  20. package/node_modules/@aws-sdk/client-ses/package.json +4 -4
  21. package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +1 -1
  22. package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
  23. package/node_modules/@aws-sdk/core/dist-cjs/submodules/protocols/index.js +94 -1589
  24. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/index.js +1 -7
  25. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsJsonRpcProtocol.js +1 -3
  26. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsRestJsonProtocol.js +1 -3
  27. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/index.d.ts +2 -8
  28. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
  29. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
  30. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
  31. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
  32. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/index.d.ts +10 -10
  33. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
  34. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
  35. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
  36. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
  37. package/node_modules/@aws-sdk/core/package.json +2 -2
  38. package/node_modules/@aws-sdk/credential-provider-env/package.json +3 -3
  39. package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -3
  40. package/node_modules/@aws-sdk/credential-provider-ini/package.json +10 -10
  41. package/node_modules/@aws-sdk/credential-provider-login/package.json +4 -4
  42. package/node_modules/@aws-sdk/credential-provider-node/package.json +8 -8
  43. package/node_modules/@aws-sdk/credential-provider-process/package.json +3 -3
  44. package/node_modules/@aws-sdk/credential-provider-sso/package.json +5 -5
  45. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +4 -4
  46. package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +3 -3
  47. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  48. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  49. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  50. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  51. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  52. package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
  53. package/node_modules/@aws-sdk/token-providers/dist-cjs/index.js +37 -19
  54. package/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js +37 -19
  55. package/node_modules/@aws-sdk/token-providers/package.json +4 -4
  56. package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +1695 -3
  57. package/node_modules/@smithy/core/dist-es/submodules/protocols/ConfigurableSerdeContext.js +6 -0
  58. package/node_modules/@smithy/core/dist-es/submodules/protocols/UnionSerde.js +26 -0
  59. package/node_modules/@smithy/core/dist-es/submodules/protocols/common.js +3 -0
  60. package/node_modules/@smithy/core/dist-es/submodules/protocols/index.js +8 -0
  61. package/node_modules/@smithy/core/dist-es/submodules/protocols/json/SmithyRpcV2JsonProtocol.js +102 -0
  62. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeDeserializer.js +8 -1
  63. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeSerializer.js +8 -1
  64. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.js +13 -2
  65. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeSerializer2.js +11 -1
  66. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/parseJsonBody.js +16 -1
  67. package/node_modules/@smithy/core/dist-es/submodules/protocols/writeKey.js +3 -0
  68. package/node_modules/@smithy/core/dist-types/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
  69. package/node_modules/@smithy/core/dist-types/submodules/protocols/UnionSerde.d.ts +24 -0
  70. package/node_modules/@smithy/core/dist-types/submodules/protocols/common.d.ts +2 -0
  71. package/node_modules/@smithy/core/dist-types/submodules/protocols/index.d.ts +9 -0
  72. package/node_modules/@smithy/core/dist-types/submodules/protocols/json/JsonSettings.d.ts +13 -0
  73. package/node_modules/@smithy/core/dist-types/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
  74. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/parseJsonBody.d.ts +16 -0
  75. package/node_modules/@smithy/core/dist-types/submodules/protocols/writeKey.d.ts +4 -0
  76. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
  77. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/UnionSerde.d.ts +24 -0
  78. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/common.d.ts +2 -0
  79. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/index.d.ts +9 -0
  80. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/JsonSettings.d.ts +13 -0
  81. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
  82. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonCodec.d.ts +15 -11
  83. package/node_modules/{@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json → @smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1}/JsonSettings.d.ts +7 -4
  84. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +14 -0
  85. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +29 -0
  86. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +51 -0
  87. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +13 -9
  88. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +30 -0
  89. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +60 -0
  90. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/detectBufferParsing.d.ts +1 -1
  91. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +21 -0
  92. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +15 -0
  93. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +11 -0
  94. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +38 -0
  95. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/writeKey.d.ts +4 -0
  96. package/node_modules/@smithy/core/package.json +2 -1
  97. package/node_modules/@zone-eu/mailsplit/README.md +43 -54
  98. package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +7 -3
  99. package/node_modules/@zone-eu/mailsplit/package.json +20 -15
  100. package/node_modules/libmime/CHANGELOG.md +7 -0
  101. package/node_modules/libmime/dist/cjs/libmime.js +24 -10
  102. package/node_modules/libmime/dist/esm/libmime.js +24 -10
  103. package/node_modules/libmime/package.json +3 -1
  104. package/node_modules/mailparser/CHANGELOG.md +14 -0
  105. package/node_modules/mailparser/README.md +6 -0
  106. package/node_modules/mailparser/dist/cjs/index.d.ts +10 -0
  107. package/node_modules/mailparser/dist/cjs/index.js +12 -0
  108. package/node_modules/mailparser/dist/cjs/mail-parser.d.ts +98 -0
  109. package/node_modules/mailparser/dist/cjs/mail-parser.js +1419 -0
  110. package/node_modules/mailparser/dist/cjs/package.json +3 -0
  111. package/node_modules/mailparser/dist/cjs/simple-parser.d.ts +17 -0
  112. package/node_modules/mailparser/dist/cjs/simple-parser.js +133 -0
  113. package/node_modules/mailparser/dist/cjs/stream-hash.d.ts +24 -0
  114. package/node_modules/mailparser/dist/cjs/stream-hash.js +33 -0
  115. package/node_modules/mailparser/dist/cjs/tlds.cjs +8 -0
  116. package/node_modules/mailparser/dist/cjs/types.d.ts +309 -0
  117. package/node_modules/mailparser/dist/cjs/types.js +2 -0
  118. package/node_modules/mailparser/dist/esm/index.d.ts +10 -0
  119. package/node_modules/mailparser/dist/esm/index.js +4 -0
  120. package/node_modules/mailparser/dist/esm/mail-parser.d.ts +98 -0
  121. package/node_modules/mailparser/{lib → dist/esm}/mail-parser.js +302 -368
  122. package/node_modules/mailparser/dist/esm/package.json +3 -0
  123. package/node_modules/mailparser/dist/esm/simple-parser.d.ts +17 -0
  124. package/node_modules/mailparser/{lib → dist/esm}/simple-parser.js +45 -68
  125. package/node_modules/mailparser/dist/esm/stream-hash.d.ts +24 -0
  126. package/node_modules/mailparser/{lib → dist/esm}/stream-hash.js +7 -9
  127. package/node_modules/mailparser/dist/esm/tlds.cjs +8 -0
  128. package/node_modules/mailparser/dist/esm/types.d.ts +309 -0
  129. package/node_modules/mailparser/dist/esm/types.js +1 -0
  130. package/node_modules/mailparser/package.json +86 -15
  131. package/package.json +9 -9
  132. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -7
  133. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -4
  134. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -13
  135. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -18
  136. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -27
  137. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -15
  138. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -39
  139. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +0 -7
  140. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +0 -7
  141. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +0 -2
  142. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +0 -14
  143. package/node_modules/mailparser/.gitattributes +0 -1
  144. package/node_modules/mailparser/.prettierignore +0 -3
  145. package/node_modules/mailparser/.prettierrc.js +0 -10
  146. package/node_modules/mailparser/.release-please-manifest.json +0 -3
  147. package/node_modules/mailparser/eslint.config.js +0 -33
  148. package/node_modules/mailparser/index.js +0 -9
  149. package/node_modules/mailparser/release-please-config.json +0 -10
  150. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/JsonSettings.js +0 -0
  151. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonCodec.js +0 -0
  152. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonSettings.js +0 -0
  153. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.js +0 -0
  154. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonCodec2.js +0 -0
  155. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/detectBufferParsing.js +0 -0
  156. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReplacer.js +0 -0
  157. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReviver.js +0 -0
  158. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/needsReviver.js +0 -0
  159. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonCodec.d.ts +0 -0
  160. /package/node_modules/{@aws-sdk/core/dist-types/submodules/protocols/json → @smithy/core/dist-types/submodules/protocols/json/codec-v1}/JsonSettings.d.ts +0 -0
  161. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -0
  162. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -0
  163. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -0
  164. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +0 -0
  165. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -0
  166. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -0
  167. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/detectBufferParsing.d.ts +0 -0
  168. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReplacer.d.ts +0 -0
  169. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReviver.d.ts +0 -0
  170. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/needsReviver.d.ts +0 -0
@@ -1,21 +1,17 @@
1
- 'use strict';
2
-
3
- const mailsplit = require('@zone-eu/mailsplit');
4
- const libmime = require('libmime');
5
- const addressparser = require('nodemailer/lib/addressparser');
6
- const Transform = require('stream').Transform;
7
- const Splitter = mailsplit.Splitter;
8
- const ChunkedPassthrough = mailsplit.ChunkedPassthrough;
9
- const punycode = require('punycode.js');
10
- const FlowedDecoder = require('@zone-eu/mailsplit/lib/flowed-decoder');
11
- const StreamHash = require('./stream-hash');
12
- const iconv = require('iconv-lite');
13
- const { htmlToText } = require('html-to-text');
14
- const he = require('he');
15
- const linkify = require('linkify-it')();
16
- const tlds = require('tlds');
17
- const encodingJapanese = require('encoding-japanese');
18
-
1
+ import mailsplit from '@zone-eu/mailsplit';
2
+ import FlowedDecoder from '@zone-eu/mailsplit/lib/flowed-decoder.js';
3
+ import encodingJapanese from 'encoding-japanese';
4
+ import he from 'he';
5
+ import { htmlToText } from 'html-to-text';
6
+ import iconv from 'iconv-lite';
7
+ import libmime from 'libmime';
8
+ import linkifyIt from 'linkify-it';
9
+ import addressparser from 'nodemailer/lib/addressparser';
10
+ import punycode from 'punycode.js';
11
+ import { Transform } from 'node:stream';
12
+ import tlds from './tlds.cjs';
13
+ import StreamHash from './stream-hash.js';
14
+ const { Splitter, ChunkedPassthrough } = mailsplit;
19
15
  // Bounds for link detection in text bodies. Message content is untrusted and
20
16
  // linkify-it backtracks heavily on crafted input: matching a host is quadratic
21
17
  // in the length of a single whitespace-free run and exponential in the number
@@ -33,37 +29,32 @@ const encodingJapanese = require('encoding-japanese');
33
29
  const MAX_LINKIFY_SEGMENT_LENGTH = 4096;
34
30
  const MAX_LINKIFY_HOST_LABELS = 6;
35
31
  const MAX_LINKIFY_WORK = 768 * 1024 * 1024; // shared by all text parts of a message
36
-
37
32
  // Default upper bound for the total length of the URLs updateImageLinks() writes
38
33
  // into the HTML (options.maxInlinedImagesSize). simpleParser inlines every cid:
39
34
  // reference as a full data: URL, so a small message repeating a reference to
40
35
  // one image thousands of times would otherwise grow the HTML past the maximum
41
36
  // string length. References beyond the budget are left as cid: links.
42
37
  const MAX_INLINED_IMAGES_SIZE = 20 * 1024 * 1024;
43
-
44
38
  // List-* header names that must not become keys of the plain list object
45
39
  const UNSAFE_LIST_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
46
-
40
+ const linkify = linkifyIt();
47
41
  linkify
48
42
  .tlds(tlds) // Reload with full tlds list
49
43
  .tlds('onion', true) // Add unofficial `.onion` domain
50
44
  .add('git:', 'http:') // Add `git:` ptotocol as "alias"
51
45
  .add('ftp:', null) // Disable `ftp:` ptotocol
52
46
  .set({ fuzzyIP: true, fuzzyLink: true, fuzzyEmail: true });
53
-
54
47
  // Characters linkify-it accepts inside a host name: letters, digits, a dash,
55
48
  // and anything non-ASCII (its label pattern takes any character that is not
56
49
  // punctuation, whitespace or a control character).
57
50
  function isHostChar(code) {
58
51
  return (code >= 0x61 && code <= 0x7a) || (code >= 0x41 && code <= 0x5a) || (code >= 0x30 && code <= 0x39) || code === 0x2d || code >= 0x80;
59
52
  }
60
-
61
53
  // twitter linkifier from
62
54
  // https://github.com/markdown-it/linkify-it#example-2-add-twitter-mentions-handler
63
55
  linkify.add('@', {
64
56
  validate(text, pos, self) {
65
57
  let tail = text.slice(pos);
66
-
67
58
  if (!self.re.twitter) {
68
59
  self.re.twitter = new RegExp('^([a-zA-Z0-9_]){1,15}(?!_)(?=$|' + self.re.src_ZPCc + ')');
69
60
  }
@@ -81,7 +72,6 @@ linkify.add('@', {
81
72
  match.url = 'https://twitter.com/' + match.url.replace(/^@/, '');
82
73
  }
83
74
  });
84
-
85
75
  // Lists the parameters a header value gives in RFC 2231 form (name*=, name*0= or
86
76
  // name*0*=). libmime decodes those while parsing the header, so the result must not go
87
77
  // through decodeWords as well: filename*=utf-8''a%3D%3Futf-8%3FQ%3F%3D2Eexe%3F%3D would
@@ -91,8 +81,7 @@ function rfc2231ParamNames(value) {
91
81
  let quote = false;
92
82
  let escaped = false;
93
83
  let start = 0;
94
-
95
- let addSegment = end => {
84
+ let addSegment = (end) => {
96
85
  let segment = value.substring(start, end);
97
86
  let eq = segment.indexOf('=');
98
87
  if (eq < 0) {
@@ -107,46 +96,40 @@ function rfc2231ParamNames(value) {
107
96
  names.add(match[1]);
108
97
  }
109
98
  };
110
-
111
99
  for (let i = 0; i < value.length; i++) {
112
100
  let chr = value.charAt(i);
113
101
  if (escaped) {
114
102
  escaped = false;
115
- } else if (chr === '\\') {
103
+ }
104
+ else if (chr === '\\') {
116
105
  escaped = true;
117
- } else if (chr === '"') {
106
+ }
107
+ else if (chr === '"') {
118
108
  quote = !quote;
119
- } else if (chr === ';' && !quote) {
109
+ }
110
+ else if (chr === ';' && !quote) {
120
111
  addSegment(i);
121
112
  start = i + 1;
122
113
  }
123
114
  }
124
115
  addSegment(value.length);
125
-
126
116
  return names;
127
117
  }
128
-
129
118
  // Deepest element nesting html-to-text is asked to convert. htmlparser2 keeps its open
130
119
  // element stack at the front of an array, so parsing alone is quadratic in the nesting
131
120
  // depth (200k nested <b> took 9 s), and html-to-text's recursive walk overflows the call
132
121
  // stack at about 2000 levels. Real markup stays far below this: Chrome's own parser
133
122
  // flattens anything nested deeper than 512 levels.
134
123
  const MAX_HTML_NESTING_DEPTH = 1000;
135
-
136
124
  // Elements that never hold content, so a start tag does not open a level
137
125
  const HTML_VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
138
-
139
126
  // Elements the HTML parser closes implicitly when a sibling opens (li after li, td after
140
127
  // td, p after p), so an unclosed run of them does not nest. They are left out of the depth
141
128
  // count: a report with thousands of unclosed table cells is sloppy but common markup
142
129
  const HTML_IMPLIED_END_ELEMENTS = new Set(['p', 'li', 'dt', 'dd', 'option', 'optgroup', 'tr', 'td', 'th', 'thead', 'tbody', 'tfoot', 'rt', 'rp']);
143
-
144
130
  function isTagNameChar(code) {
145
- return (
146
- (code >= 0x61 && code <= 0x7a) || (code >= 0x41 && code <= 0x5a) || (code >= 0x30 && code <= 0x39) || code === 0x2d || code === 0x3a || code === 0x5f
147
- );
131
+ return ((code >= 0x61 && code <= 0x7a) || (code >= 0x41 && code <= 0x5a) || (code >= 0x30 && code <= 0x39) || code === 0x2d || code === 0x3a || code === 0x5f);
148
132
  }
149
-
150
133
  // Reports whether `html` nests elements deeper than `maxDepth`, in one linear pass that
151
134
  // follows what htmlparser2 builds: a start tag opens an element (a trailing "/" does not
152
135
  // close it in HTML mode), an end tag closes down to the nearest open element of that name
@@ -188,7 +171,8 @@ function exceedsHtmlNestingDepth(html, maxDepth) {
188
171
  if (stack.length > maxDepth) {
189
172
  return true;
190
173
  }
191
- } else if (open.get(name)) {
174
+ }
175
+ else if (open.get(name)) {
192
176
  let popped;
193
177
  do {
194
178
  popped = stack.pop();
@@ -200,21 +184,17 @@ function exceedsHtmlNestingDepth(html, maxDepth) {
200
184
  }
201
185
  return false;
202
186
  }
203
-
204
187
  class IconvDecoder extends Transform {
205
188
  constructor(Iconv, charset) {
206
189
  super();
207
-
208
190
  // Iconv throws error on ks_c_5601-1987 when it is mapped to EUC-KR
209
191
  // https://github.com/bnoordhuis/node-iconv/issues/169
210
192
  if (charset.toLowerCase() === 'ks_c_5601-1987') {
211
193
  charset = 'CP949';
212
194
  }
213
195
  this.stream = new Iconv(charset, 'UTF-8//TRANSLIT//IGNORE');
214
-
215
196
  this.inputEnded = false;
216
197
  this.endCb = false;
217
-
218
198
  this.stream.on('error', err => this.emit('error', err));
219
199
  this.stream.on('data', chunk => this.push(chunk));
220
200
  this.stream.on('end', () => {
@@ -224,37 +204,30 @@ class IconvDecoder extends Transform {
224
204
  }
225
205
  });
226
206
  }
227
-
228
207
  _transform(chunk, encoding, done) {
229
208
  this.stream.write(chunk);
230
209
  done();
231
210
  }
232
-
233
211
  _flush(done) {
234
212
  this.endCb = done;
235
213
  this.stream.end();
236
214
  }
237
215
  }
238
-
239
216
  class JPDecoder extends Transform {
240
217
  constructor(charset) {
241
218
  super();
242
-
243
219
  this.charset = charset;
244
220
  this.chunks = [];
245
221
  this.chunklen = 0;
246
222
  }
247
-
248
223
  _transform(chunk, encoding, done) {
249
224
  if (typeof chunk === 'string') {
250
225
  chunk = Buffer.from(chunk, encoding);
251
226
  }
252
-
253
227
  this.chunks.push(chunk);
254
228
  this.chunklen += chunk.length;
255
229
  done();
256
230
  }
257
-
258
231
  _flush(done) {
259
232
  let input = Buffer.concat(this.chunks, this.chunklen);
260
233
  try {
@@ -267,75 +240,73 @@ class JPDecoder extends Transform {
267
240
  output = Buffer.from(output);
268
241
  }
269
242
  this.push(output);
270
- } catch (err) {
243
+ }
244
+ catch {
271
245
  // keep as is on errors
272
246
  this.push(input);
273
247
  }
274
-
275
248
  done();
276
249
  }
277
250
  }
278
-
251
+ /**
252
+ * A lower level, streaming e-mail parser. It is a Transform stream: write the raw message in and
253
+ * read the parsed content out. It emits:
254
+ *
255
+ * - a `headers` event with the parsed headers of the message (see {@link Headers}),
256
+ * - a `headerLines` event with the raw header lines,
257
+ * - an {@link AttachmentStream} object for every attachment, whose content stream has to be read
258
+ * and `release()` called before the parser continues,
259
+ * - one {@link MessageText} object with the text content at the end.
260
+ *
261
+ * For one-shot parsing into a single object, use `simpleParser` instead.
262
+ */
263
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -- the interface below only narrows inherited stream methods
279
264
  class MailParser extends Transform {
280
265
  constructor(config) {
281
266
  super({
282
267
  readableObjectMode: true,
283
268
  writableObjectMode: false
284
269
  });
285
-
286
270
  this.options = config || {};
271
+ // mailsplit declares its streams with only a 'data' event, the parser uses the other Transform events too
287
272
  this.chunkedPassthrough = new ChunkedPassthrough();
288
273
  this.splitter = new Splitter(config);
289
-
290
274
  this.chunkedPassthrough.pipe(this.splitter);
291
-
292
275
  this.finished = false;
293
276
  this.waitingEnd = false;
294
-
295
277
  this.headers = false;
296
278
  this.headerLines = false;
297
-
298
279
  this.endReceived = false;
299
280
  this.reading = false;
300
281
  this.hasFailed = false;
301
-
302
282
  this.tree = false;
303
283
  this.curnode = false;
304
284
  this.waitUntilAttachmentEnd = false;
305
285
  this.attachmentCallback = false;
306
-
307
286
  this.hasHtml = false;
308
287
  this.hasText = false;
309
-
310
288
  this.text = false;
311
289
  this.html = false;
312
290
  this.textAsHtml = false;
313
291
  this.linkifyWork = 0;
314
-
315
292
  this.attachmentList = [];
316
-
317
293
  this.textTypes = ['text/plain', 'text/html'].concat(!this.options.keepDeliveryStatus ? 'message/delivery-status' : []);
318
-
319
294
  this.decoder = this.getDecoder();
320
-
321
295
  this.chunkedPassthrough.on('error', err => {
322
296
  this.splitter.emit('error', err);
323
297
  });
324
-
325
298
  this.splitter.on('readable', () => {
326
299
  if (this.reading) {
327
300
  return false;
328
301
  }
329
302
  this.readData();
330
303
  });
331
-
332
304
  this.splitter.on('end', () => {
333
305
  this.endReceived = true;
334
306
  if (!this.reading) {
335
307
  this.endStream();
336
308
  }
337
309
  });
338
-
339
310
  this.splitter.on('error', err => {
340
311
  this.hasFailed = true;
341
312
  if (typeof this.waitingEnd === 'function') {
@@ -343,10 +314,9 @@ class MailParser extends Transform {
343
314
  }
344
315
  this.emit('error', err);
345
316
  });
346
-
347
317
  this.libmime = new libmime.Libmime({ Iconv: this.options.Iconv });
348
318
  }
349
-
319
+ /** @internal */
350
320
  getDecoder() {
351
321
  if (this.options.Iconv) {
352
322
  const Iconv = this.options.Iconv;
@@ -358,7 +328,8 @@ class MailParser extends Transform {
358
328
  return new IconvDecoder(Iconv, charset);
359
329
  }
360
330
  };
361
- } else {
331
+ }
332
+ else {
362
333
  return {
363
334
  decodeStream(charset) {
364
335
  // resolve charset aliases (eg. iso-8859-8-i -> ISO-8859-8) via libmime's canonical table
@@ -367,13 +338,12 @@ class MailParser extends Transform {
367
338
  // special case not supported by iconv-lite
368
339
  return new JPDecoder(charset);
369
340
  }
370
-
371
341
  return iconv.decodeStream(charset);
372
342
  }
373
343
  };
374
344
  }
375
345
  }
376
-
346
+ /** @internal */
377
347
  readData() {
378
348
  if (this.hasFailed) {
379
349
  return false;
@@ -387,7 +357,6 @@ class MailParser extends Transform {
387
357
  }
388
358
  return;
389
359
  }
390
-
391
360
  this.processChunk(data, err => {
392
361
  if (err) {
393
362
  if (typeof this.waitingEnd === 'function') {
@@ -398,10 +367,9 @@ class MailParser extends Transform {
398
367
  setImmediate(() => this.readData());
399
368
  });
400
369
  }
401
-
370
+ /** @internal */
402
371
  endStream() {
403
372
  this.finished = true;
404
-
405
373
  if (this.curnode && this.curnode.decoder) {
406
374
  this.curnode.decoder.end();
407
375
  }
@@ -409,21 +377,20 @@ class MailParser extends Transform {
409
377
  this.waitingEnd();
410
378
  }
411
379
  }
412
-
413
380
  _transform(chunk, encoding, done) {
414
381
  if (!chunk || !chunk.length) {
415
382
  return done();
416
383
  }
417
-
418
384
  if (this.chunkedPassthrough.write(chunk) === false) {
419
- return this.chunkedPassthrough.once('drain', () => {
385
+ this.chunkedPassthrough.once('drain', () => {
420
386
  done();
421
387
  });
422
- } else {
388
+ return;
389
+ }
390
+ else {
423
391
  return done();
424
392
  }
425
393
  }
426
-
427
394
  _flush(done) {
428
395
  setImmediate(() => this.chunkedPassthrough.end());
429
396
  if (this.finished) {
@@ -438,32 +405,32 @@ class MailParser extends Transform {
438
405
  this.cleanup(done);
439
406
  };
440
407
  }
441
-
408
+ /** @internal */
442
409
  cleanup(done) {
443
410
  let finish = () => {
444
411
  try {
445
412
  let t = this.getTextContent();
446
413
  this.push(t);
447
- } catch (err) {
414
+ }
415
+ catch (err) {
448
416
  // report through the stream instead of leaving _flush hanging
449
417
  return done(err);
450
418
  }
451
-
452
419
  done();
453
420
  };
454
-
455
421
  if (this.curnode && this.curnode.decoder && this.curnode.decoder.readable && !this.decoderEnded) {
456
422
  (this.curnode.contentStream || this.curnode.decoder).once('end', () => {
457
423
  finish();
458
424
  });
459
425
  this.curnode.decoder.end();
460
- } else {
426
+ }
427
+ else {
461
428
  setImmediate(() => {
462
429
  finish();
463
430
  });
464
431
  }
465
432
  }
466
-
433
+ /** @internal */
467
434
  processHeaders(lines) {
468
435
  let headers = new Map();
469
436
  (lines || []).forEach(line => {
@@ -486,7 +453,8 @@ class MailParser extends Transform {
486
453
  }
487
454
  try {
488
455
  value.params[key] = this.libmime.decodeWords(value.params[key]);
489
- } catch (E) {
456
+ }
457
+ catch {
490
458
  // ignore, keep as is
491
459
  }
492
460
  });
@@ -494,7 +462,7 @@ class MailParser extends Transform {
494
462
  }
495
463
  case 'date': {
496
464
  let dateValue = new Date(value);
497
- if (isNaN(dateValue)) {
465
+ if (isNaN(dateValue.getTime())) {
498
466
  // date parsing failed :S
499
467
  dateValue = new Date();
500
468
  }
@@ -504,26 +472,29 @@ class MailParser extends Transform {
504
472
  case 'subject':
505
473
  try {
506
474
  value = this.libmime.decodeWords(value);
507
- } catch (E) {
475
+ }
476
+ catch {
508
477
  // ignore, keep as is
509
478
  }
510
479
  break;
511
480
  case 'references':
512
481
  try {
513
482
  value = this.libmime.decodeWords(value);
514
- } catch (E) {
483
+ }
484
+ catch {
515
485
  // ignore
516
486
  }
517
487
  value = value
518
488
  .split(/\s+/)
519
489
  .map(this.ensureMessageIDFormat)
520
- .filter(val => val);
490
+ .filter((val) => val);
521
491
  break;
522
492
  case 'message-id':
523
493
  case 'in-reply-to':
524
494
  try {
525
495
  value = this.libmime.decodeWords(value);
526
- } catch (E) {
496
+ }
497
+ catch {
527
498
  // ignore
528
499
  }
529
500
  value = this.ensureMessageIDFormat(value);
@@ -553,29 +524,28 @@ class MailParser extends Transform {
553
524
  };
554
525
  break;
555
526
  }
556
-
557
527
  // handle list-* keys
558
- if (key.substr(0, 5) === 'list-') {
559
- value = this.parseListHeader(key.substr(5), value);
528
+ if (key.slice(0, 5) === 'list-') {
529
+ value = this.parseListHeader(key.slice(5), value);
560
530
  key = 'list';
561
531
  }
562
-
563
532
  if (value) {
564
533
  if (!headers.has(key)) {
565
534
  headers.set(key, [].concat(value || []));
566
- } else if (Array.isArray(value)) {
535
+ }
536
+ else if (Array.isArray(value)) {
567
537
  // appended in place: rebuilding the list with concat for every repeated
568
538
  // header was quadratic in the number of References lines
569
539
  let list = headers.get(key);
570
540
  for (let entry of value) {
571
541
  list.push(entry);
572
542
  }
573
- } else {
543
+ }
544
+ else {
574
545
  headers.get(key).push(value);
575
546
  }
576
547
  }
577
548
  });
578
-
579
549
  // keep only the first value
580
550
  let singleKeys = [
581
551
  'message-id',
@@ -596,22 +566,21 @@ class MailParser extends Transform {
596
566
  'errors-to',
597
567
  'disposition-notification-to'
598
568
  ];
599
-
600
569
  headers.forEach((value, key) => {
601
570
  if (Array.isArray(value)) {
602
571
  if (singleKeys.includes(key) && value.length) {
603
572
  // the first occurrence: the one mailsplit built the MIME structure from,
604
573
  // and the one mail clients display when the header is duplicated
605
574
  headers.set(key, value[0]);
606
- } else if (value.length === 1) {
575
+ }
576
+ else if (value.length === 1) {
607
577
  headers.set(key, value[0]);
608
578
  }
609
579
  }
610
-
611
580
  if (key === 'list') {
612
581
  // normalize List-* headers
613
582
  let listValue = {};
614
- [].concat(value || []).forEach(val => {
583
+ [].concat(value || []).forEach((val) => {
615
584
  Object.keys(val || {}).forEach(listKey => {
616
585
  listValue[listKey] = val[listKey];
617
586
  });
@@ -619,10 +588,9 @@ class MailParser extends Transform {
619
588
  headers.set(key, listValue);
620
589
  }
621
590
  });
622
-
623
591
  return headers;
624
592
  }
625
-
593
+ /** @internal */
626
594
  parseListHeader(key, value) {
627
595
  if (UNSAFE_LIST_KEYS.has(key)) {
628
596
  // would replace the prototype of (or shadow) the normalized list object
@@ -632,23 +600,26 @@ class MailParser extends Transform {
632
600
  let response = {};
633
601
  let data = addresses
634
602
  .map(address => {
635
- if (/^https?:/i.test(address.name)) {
636
- response.url = address.name;
637
- } else if (address.name) {
638
- response.name = address.name;
639
- }
640
- if (/^mailto:/.test(address.address)) {
641
- response.mail = address.address.substr(7);
642
- } else if (address.address && address.address.indexOf('@') < 0) {
643
- response.id = address.address;
644
- } else if (address.address) {
645
- response.mail = address.address;
646
- }
647
- if (Object.keys(response).length) {
648
- return response;
649
- }
650
- return false;
651
- })
603
+ if (/^https?:/i.test(address.name)) {
604
+ response.url = address.name;
605
+ }
606
+ else if (address.name) {
607
+ response.name = address.name;
608
+ }
609
+ if (/^mailto:/.test(address.address || '')) {
610
+ response.mail = address.address.slice(7);
611
+ }
612
+ else if (address.address && address.address.indexOf('@') < 0) {
613
+ response.id = address.address;
614
+ }
615
+ else if (address.address) {
616
+ response.mail = address.address;
617
+ }
618
+ if (Object.keys(response).length) {
619
+ return response;
620
+ }
621
+ return false;
622
+ })
652
623
  .filter(address => address);
653
624
  if (data.length) {
654
625
  return {
@@ -657,20 +628,26 @@ class MailParser extends Transform {
657
628
  }
658
629
  return false;
659
630
  }
660
-
631
+ /**
632
+ * Turns a `Priority`, `X-Priority`, `X-MSMail-Priority` or `Importance` header value into
633
+ * `'high'`, `'normal'` or `'low'`.
634
+ */
661
635
  parsePriority(value) {
662
636
  value = value.toLowerCase().trim();
663
637
  if (!isNaN(parseInt(value, 10))) {
664
638
  // support "X-Priority: 1 (Highest)"
665
- value = parseInt(value, 10) || 0;
666
- if (value === 3) {
639
+ let level = parseInt(value, 10) || 0;
640
+ if (level === 3) {
667
641
  return 'normal';
668
- } else if (value > 3) {
642
+ }
643
+ else if (level > 3) {
669
644
  return 'low';
670
- } else {
645
+ }
646
+ else {
671
647
  return 'high';
672
648
  }
673
- } else {
649
+ }
650
+ else {
674
651
  switch (value) {
675
652
  case 'non-urgent':
676
653
  case 'low':
@@ -682,28 +659,26 @@ class MailParser extends Transform {
682
659
  }
683
660
  return 'normal';
684
661
  }
685
-
662
+ /**
663
+ * Wraps a Message-ID in angle brackets where they are missing. Returns `false` for an empty value.
664
+ */
686
665
  ensureMessageIDFormat(value) {
687
666
  if (!value.length) {
688
667
  return false;
689
668
  }
690
-
691
669
  if (value.charAt(0) !== '<') {
692
670
  value = '<' + value;
693
671
  }
694
-
695
672
  if (value.charAt(value.length - 1) !== '>') {
696
673
  value += '>';
697
674
  }
698
-
699
675
  return value;
700
676
  }
701
-
677
+ /** @internal */
702
678
  decodeAddresses(addresses) {
703
679
  for (let i = 0; i < addresses.length; i++) {
704
680
  let address = addresses[i];
705
681
  address.name = (address.name || '').toString().trim();
706
-
707
682
  // An encoded word decodes into the display name and nothing else, whatever it
708
683
  // decodes to. RFC 2047 section 5 keeps encoded words out of the addr-spec, so a
709
684
  // name that decodes to "CEO" <ceo@bank.com> is still only a name: parsing it
@@ -711,7 +686,8 @@ class MailParser extends Transform {
711
686
  if (address.name) {
712
687
  try {
713
688
  address.name = this.libmime.decodeWords(address.name);
714
- } catch (E) {
689
+ }
690
+ catch {
715
691
  //ignore, keep as is
716
692
  }
717
693
  }
@@ -723,19 +699,22 @@ class MailParser extends Transform {
723
699
  let decodedAddr = this.libmime.decodeWords(address.address);
724
700
  if (/^[^\s@]+@[^\s@]+$/.test(decodedAddr) && !/[=]\?/.test(decodedAddr)) {
725
701
  address.address = decodedAddr;
726
- } else {
702
+ }
703
+ else {
727
704
  address.address = '';
728
705
  }
729
- } catch (E) {
706
+ }
707
+ catch {
730
708
  address.address = '';
731
709
  }
732
710
  }
733
- if (/@xn--/.test(address.address)) {
711
+ if (address.address && /@xn--/.test(address.address)) {
734
712
  try {
735
713
  address.address =
736
- address.address.substr(0, address.address.lastIndexOf('@') + 1) +
737
- punycode.toUnicode(address.address.substr(address.address.lastIndexOf('@') + 1));
738
- } catch (E) {
714
+ address.address.slice(0, address.address.lastIndexOf('@') + 1) +
715
+ punycode.toUnicode(address.address.slice(address.address.lastIndexOf('@') + 1));
716
+ }
717
+ catch {
739
718
  // Not a valid punycode string; keep as is
740
719
  }
741
720
  }
@@ -744,17 +723,15 @@ class MailParser extends Transform {
744
723
  }
745
724
  }
746
725
  }
747
-
726
+ /** @internal */
748
727
  createNode(node) {
749
728
  let contentType = node.contentType;
750
729
  let disposition = node.disposition;
751
730
  let encoding = node.encoding;
752
731
  let charset = node.charset;
753
-
754
732
  if (!contentType && node.root) {
755
733
  contentType = 'text/plain';
756
734
  }
757
-
758
735
  let newNode = {
759
736
  node,
760
737
  headerLines: node.headers.lines,
@@ -762,46 +739,37 @@ class MailParser extends Transform {
762
739
  contentType,
763
740
  children: []
764
741
  };
765
-
766
742
  if (!/^multipart\//i.test(contentType)) {
767
743
  if (disposition && !['attachment', 'inline'].includes(disposition)) {
768
744
  disposition = 'attachment';
769
745
  }
770
-
771
746
  if (!disposition && !this.textTypes.includes(contentType)) {
772
747
  newNode.disposition = 'attachment';
773
- } else {
748
+ }
749
+ else {
774
750
  newNode.disposition = disposition || 'inline';
775
751
  }
776
-
777
752
  newNode.isAttachment = !this.textTypes.includes(contentType) || newNode.disposition !== 'inline';
778
-
779
753
  newNode.encoding = ['quoted-printable', 'base64'].includes(encoding) ? encoding : 'binary';
780
-
781
754
  if (charset) {
782
755
  newNode.charset = charset;
783
756
  }
784
-
785
757
  let decoder = node.getDecoder();
786
758
  decoder.on('end', () => {
787
759
  this.decoderEnded = true;
788
760
  });
789
761
  newNode.decoder = decoder;
790
762
  }
791
-
792
763
  if (node.root) {
793
764
  this.headers = newNode.headers;
794
765
  this.headerLines = newNode.headerLines;
795
766
  }
796
-
797
767
  // find location in tree
798
-
799
- if (!this.tree) {
768
+ if (!this.tree || !this.curnode) {
800
769
  newNode.root = true;
801
770
  this.curnode = this.tree = newNode;
802
771
  return newNode;
803
772
  }
804
-
805
773
  // immediate child of root node
806
774
  if (!this.curnode.parent) {
807
775
  newNode.parent = this.curnode;
@@ -809,7 +777,6 @@ class MailParser extends Transform {
809
777
  this.curnode = newNode;
810
778
  return newNode;
811
779
  }
812
-
813
780
  // siblings
814
781
  if (this.curnode.parent.node === node.parentNode) {
815
782
  newNode.parent = this.curnode.parent;
@@ -817,7 +784,6 @@ class MailParser extends Transform {
817
784
  this.curnode = newNode;
818
785
  return newNode;
819
786
  }
820
-
821
787
  // first child
822
788
  if (this.curnode.node === node.parentNode) {
823
789
  newNode.parent = this.curnode;
@@ -825,7 +791,6 @@ class MailParser extends Transform {
825
791
  this.curnode = newNode;
826
792
  return newNode;
827
793
  }
828
-
829
794
  // move up
830
795
  let parentNode = this.curnode;
831
796
  while ((parentNode = parentNode.parent)) {
@@ -836,12 +801,11 @@ class MailParser extends Transform {
836
801
  return newNode;
837
802
  }
838
803
  }
839
-
840
804
  // should never happen, can't detect parent
841
805
  this.curnode = newNode;
842
806
  return newNode;
843
807
  }
844
-
808
+ /** @internal */
845
809
  getTextContent() {
846
810
  let text = [];
847
811
  let html = [];
@@ -850,71 +814,66 @@ class MailParser extends Transform {
850
814
  if (node.showMeta) {
851
815
  let meta = ['From', 'Subject', 'Date', 'To', 'Cc', 'Bcc']
852
816
  .map(fkey => {
853
- let key = fkey.toLowerCase();
854
- if (!node.headers.has(key)) {
855
- return false;
856
- }
857
- let value = node.headers.get(key);
858
- if (!value) {
859
- return false;
860
- }
861
- return {
862
- key: fkey,
863
- value: Array.isArray(value) ? value[value.length - 1] : value
864
- };
865
- })
817
+ let key = fkey.toLowerCase();
818
+ if (!node.headers.has(key)) {
819
+ return false;
820
+ }
821
+ let value = node.headers.get(key);
822
+ if (!value) {
823
+ return false;
824
+ }
825
+ return {
826
+ key: fkey,
827
+ value: (Array.isArray(value) ? value[value.length - 1] : value)
828
+ };
829
+ })
866
830
  .filter(entry => entry);
867
831
  if (this.hasHtml) {
868
- html.push(
869
- '<table class="mp_head">' +
870
- meta
871
- .map(entry => {
872
- let value = entry.value;
873
- switch (entry.key) {
874
- case 'From':
875
- case 'To':
876
- case 'Cc':
877
- case 'Bcc':
878
- value = value.html;
879
- break;
880
- case 'Date':
881
- value = this.options.formatDateString ? this.options.formatDateString(value) : value.toUTCString();
882
- break;
883
- case 'Subject':
884
- value = '<strong>' + he.encode(value) + '</strong>';
885
- break;
886
- default:
887
- value = he.encode(value);
888
- }
889
-
890
- return '<tr><td class="mp_head_key">' + he.encode(entry.key) + ':</td><td class="mp_head_value">' + value + '<td></tr>';
891
- })
892
- .join('\n') +
893
- '<table>'
894
- );
832
+ html.push('<table class="mp_head">' +
833
+ meta
834
+ .map(entry => {
835
+ let value = entry.value;
836
+ switch (entry.key) {
837
+ case 'From':
838
+ case 'To':
839
+ case 'Cc':
840
+ case 'Bcc':
841
+ value = value.html;
842
+ break;
843
+ case 'Date':
844
+ value = this.options.formatDateString ? this.options.formatDateString(value) : value.toUTCString();
845
+ break;
846
+ case 'Subject':
847
+ value = '<strong>' + he.encode(value) + '</strong>';
848
+ break;
849
+ default:
850
+ value = he.encode(value);
851
+ }
852
+ return '<tr><td class="mp_head_key">' + he.encode(entry.key) + ':</td><td class="mp_head_value">' + value + '<td></tr>';
853
+ })
854
+ .join('\n') +
855
+ '<table>');
895
856
  }
896
857
  if (this.hasText) {
897
- text.push(
898
- '\n' +
899
- meta
900
- .map(entry => {
901
- let value = entry.value;
902
- switch (entry.key) {
903
- case 'From':
904
- case 'To':
905
- case 'Cc':
906
- case 'Bcc':
907
- value = value.text;
908
- break;
909
- case 'Date':
910
- value = this.options.formatDateString ? this.options.formatDateString(value) : value.toUTCString();
911
- break;
912
- }
913
- return entry.key + ': ' + value;
914
- })
915
- .join('\n') +
916
- '\n'
917
- );
858
+ text.push('\n' +
859
+ meta
860
+ .map(entry => {
861
+ let value = entry.value;
862
+ switch (entry.key) {
863
+ case 'From':
864
+ case 'To':
865
+ case 'Cc':
866
+ case 'Bcc':
867
+ value = value.text;
868
+ break;
869
+ case 'Date':
870
+ value = this.options.formatDateString ? this.options.formatDateString(value) : value.toUTCString();
871
+ break;
872
+ }
873
+ return entry.key + ': ' + value;
874
+ })
875
+ .join('\n') +
876
+ '\n');
918
877
  }
919
878
  }
920
879
  if (node.textContent) {
@@ -923,16 +882,19 @@ class MailParser extends Transform {
923
882
  if (!alternative && this.hasHtml) {
924
883
  html.push(this.textToHtml(node.textContent));
925
884
  }
926
- } else if (node.contentType === 'message/delivery-status' && !this.options.keepDeliveryStatus) {
885
+ }
886
+ else if (node.contentType === 'message/delivery-status' && !this.options.keepDeliveryStatus) {
927
887
  text.push(node.textContent);
928
888
  if (!alternative && this.hasHtml) {
929
889
  html.push(this.textToHtml(node.textContent));
930
890
  }
931
- } else if (node.contentType === 'text/html') {
891
+ }
892
+ else if (node.contentType === 'text/html') {
932
893
  if ((!alternative && this.hasText) || (node.root && !this.hasText)) {
933
894
  if (this.options.skipHtmlToText) {
934
895
  text.push('');
935
- } else {
896
+ }
897
+ else {
936
898
  // HTML that can not be converted is no reason to lose the message:
937
899
  // the text stays empty, the HTML is kept and the reason travels with
938
900
  // the result as textConversionError. An 'error' event here used to
@@ -941,12 +903,15 @@ class MailParser extends Transform {
941
903
  let err = null;
942
904
  if (node.textContent.length > this.options.maxHtmlLengthToParse) {
943
905
  err = new Error(`HTML too long for parsing ${node.textContent.length} bytes`);
944
- } else if (exceedsHtmlNestingDepth(node.textContent, MAX_HTML_NESTING_DEPTH)) {
906
+ }
907
+ else if (exceedsHtmlNestingDepth(node.textContent, MAX_HTML_NESTING_DEPTH)) {
945
908
  err = new Error(`HTML nested deeper than ${MAX_HTML_NESTING_DEPTH} levels`);
946
- } else {
909
+ }
910
+ else {
947
911
  try {
948
912
  text.push(htmlToText(node.textContent, { limits: { maxDepth: MAX_HTML_NESTING_DEPTH } }));
949
- } catch (cause) {
913
+ }
914
+ catch (cause) {
950
915
  err = new Error('Failed to parse HTML', { cause });
951
916
  }
952
917
  }
@@ -966,9 +931,7 @@ class MailParser extends Transform {
966
931
  });
967
932
  }
968
933
  };
969
-
970
934
  processNode(false, 0, this.tree);
971
-
972
935
  let response = {
973
936
  type: 'text'
974
937
  };
@@ -984,7 +947,7 @@ class MailParser extends Transform {
984
947
  }
985
948
  return response;
986
949
  }
987
-
950
+ /** @internal */
988
951
  processChunk(data, done) {
989
952
  let partId = null;
990
953
  if (data._parentBoundary) {
@@ -1001,31 +964,26 @@ class MailParser extends Transform {
1001
964
  }
1002
965
  });
1003
966
  this.emit('headers', node.headers);
1004
-
1005
967
  if (node.headerLines) {
1006
968
  this.emit('headerLines', node.headerLines);
1007
969
  }
1008
970
  }
1009
-
1010
971
  if (data.contentType === 'message/rfc822' && data.messageNode) {
1011
972
  break;
1012
973
  }
1013
-
1014
974
  if (data.parentNode && data.parentNode.contentType === 'message/rfc822') {
1015
975
  node.showMeta = true;
1016
976
  }
1017
-
1018
977
  if (node.isAttachment) {
978
+ let decoder = node.decoder;
1019
979
  let contentType = node.contentType;
1020
980
  if (node.contentType === 'application/octet-stream' && data.filename) {
1021
981
  contentType = this.libmime.detectMimeType(data.filename) || 'application/octet-stream';
1022
982
  }
1023
-
1024
983
  // backpressure state for the attachment stream, see pump() below
1025
984
  let released = false;
1026
985
  let waitingDrain = false;
1027
- let pump = null;
1028
-
986
+ let pump;
1029
987
  let attachment = {
1030
988
  type: 'attachment',
1031
989
  content: null,
@@ -1045,59 +1003,53 @@ class MailParser extends Transform {
1045
1003
  this.waitUntilAttachmentEnd = false;
1046
1004
  }
1047
1005
  };
1048
-
1049
1006
  let algo = this.options.checksumAlgo || 'md5';
1050
1007
  let hasher;
1051
1008
  try {
1052
1009
  hasher = new StreamHash(attachment, algo);
1053
- } catch (err) {
1010
+ }
1011
+ catch (err) {
1054
1012
  // an algorithm this Node does not offer (a typo, or MD5 on a host whose
1055
1013
  // OpenSSL runs in FIPS mode) is a parser error, not a process-ending throw
1056
1014
  err.message = `Checksum algorithm "${algo}" is not available: ${err.message}`;
1057
1015
  return done(err);
1058
1016
  }
1059
- node.decoder.on('error', err => {
1017
+ decoder.on('error', err => {
1060
1018
  hasher.emit('error', err);
1061
1019
  });
1062
-
1063
1020
  // Honour the consumer's backpressure while it reads the attachment. Once the
1064
1021
  // attachment is released it is drained regardless, so that a caller that
1065
1022
  // releases without reading the content does not stall the parser.
1066
1023
  pump = () => {
1067
1024
  let chunk;
1068
- while ((chunk = node.decoder.read()) !== null) {
1025
+ while ((chunk = decoder.read()) !== null) {
1069
1026
  if (hasher.write(chunk) === false && !released) {
1070
1027
  waitingDrain = true;
1071
- return hasher.once('drain', () => {
1028
+ hasher.once('drain', () => {
1072
1029
  waitingDrain = false;
1073
1030
  pump();
1074
1031
  });
1032
+ return;
1075
1033
  }
1076
1034
  }
1077
1035
  };
1078
-
1079
- node.decoder.on('readable', () => {
1036
+ decoder.on('readable', () => {
1080
1037
  if (!waitingDrain) {
1081
1038
  pump();
1082
1039
  }
1083
1040
  });
1084
-
1085
- node.decoder.once('end', () => {
1041
+ decoder.once('end', () => {
1086
1042
  hasher.end();
1087
1043
  });
1088
-
1089
1044
  //node.decoder.pipe(hasher);
1090
1045
  attachment.content = hasher;
1091
-
1092
1046
  this.waitUntilAttachmentEnd = true;
1093
1047
  if (data.disposition) {
1094
1048
  attachment.contentDisposition = data.disposition;
1095
1049
  }
1096
-
1097
1050
  if (data.filename) {
1098
1051
  attachment.filename = data.filename;
1099
1052
  }
1100
-
1101
1053
  if (node.headers.has('content-id')) {
1102
1054
  attachment.contentId = [].concat(node.headers.get('content-id') || []).shift();
1103
1055
  attachment.cid = attachment.contentId.trim().replace(/^<|>$/g, '').trim();
@@ -1109,23 +1061,23 @@ class MailParser extends Transform {
1109
1061
  }
1110
1062
  }
1111
1063
  }
1112
-
1113
1064
  attachment.headers = node.headers;
1114
1065
  this.push(attachment);
1115
1066
  this.attachmentList.push(attachment);
1116
- } else if (node.disposition === 'inline') {
1067
+ }
1068
+ else if (node.disposition === 'inline') {
1117
1069
  let chunks = [];
1118
1070
  let chunklen = 0;
1119
1071
  node.contentStream = node.decoder;
1120
-
1121
1072
  if (node.contentType === 'text/plain') {
1122
1073
  this.hasText = true;
1123
- } else if (node.contentType === 'text/html') {
1074
+ }
1075
+ else if (node.contentType === 'text/html') {
1124
1076
  this.hasHtml = true;
1125
- } else if (node.contentType === 'message/delivery-status' && !this.options.keepDeliveryStatus) {
1077
+ }
1078
+ else if (node.contentType === 'message/delivery-status' && !this.options.keepDeliveryStatus) {
1126
1079
  this.hasText = true;
1127
1080
  }
1128
-
1129
1081
  if (node.node.flowed) {
1130
1082
  let contentStream = node.contentStream;
1131
1083
  let flowDecoder = new FlowedDecoder({
@@ -1137,10 +1089,8 @@ class MailParser extends Transform {
1137
1089
  contentStream.pipe(flowDecoder);
1138
1090
  node.contentStream = flowDecoder;
1139
1091
  }
1140
-
1141
1092
  let charset = node.charset || 'utf-8';
1142
1093
  //charset = charset || 'windows-1257';
1143
-
1144
1094
  if (!['ascii', 'usascii', 'utf8'].includes(charset.toLowerCase().replace(/[^a-z0-9]+/g, ''))) {
1145
1095
  try {
1146
1096
  let contentStream = node.contentStream;
@@ -1150,14 +1100,15 @@ class MailParser extends Transform {
1150
1100
  });
1151
1101
  contentStream.pipe(decodeStream);
1152
1102
  node.contentStream = decodeStream;
1153
- } catch (E) {
1103
+ }
1104
+ catch {
1154
1105
  // do not decode charset
1155
1106
  }
1156
1107
  }
1157
-
1158
- node.contentStream.on('readable', () => {
1108
+ let contentStream = node.contentStream;
1109
+ contentStream.on('readable', () => {
1159
1110
  let chunk;
1160
- while ((chunk = node.contentStream.read()) !== null) {
1111
+ while ((chunk = contentStream.read()) !== null) {
1161
1112
  if (typeof chunk === 'string') {
1162
1113
  chunk = Buffer.from(chunk);
1163
1114
  }
@@ -1165,114 +1116,113 @@ class MailParser extends Transform {
1165
1116
  chunklen += chunk.length;
1166
1117
  }
1167
1118
  });
1168
-
1169
- node.contentStream.once('end', () => {
1119
+ contentStream.once('end', () => {
1170
1120
  node.textContent = Buffer.concat(chunks, chunklen).toString().replace(/\r?\n/g, '\n');
1171
1121
  });
1172
-
1173
- node.contentStream.once('error', err => {
1122
+ contentStream.once('error', err => {
1174
1123
  this.emit('error', err);
1175
1124
  });
1176
1125
  }
1177
-
1178
1126
  break;
1179
1127
  }
1180
-
1181
1128
  case 'data':
1182
1129
  if (this.curnode && this.curnode.decoder) {
1183
1130
  this.curnode.decoder.end();
1184
1131
  }
1185
-
1186
1132
  if (this.waitUntilAttachmentEnd) {
1187
1133
  this.attachmentCallback = done;
1188
1134
  return;
1189
1135
  }
1190
-
1191
1136
  // multipart message structure
1192
1137
  // this is not related to any specific 'node' block as it includes
1193
1138
  // everything between the end of some node body and between the next header
1194
1139
  //process.stdout.write(data.value);
1195
1140
  break;
1196
-
1197
1141
  case 'body':
1198
1142
  if (this.curnode && this.curnode.decoder && this.curnode.decoder.writable) {
1199
1143
  if (this.curnode.decoder.write(data.value) === false) {
1200
- return this.curnode.decoder.once('drain', done);
1144
+ this.curnode.decoder.once('drain', done);
1145
+ return;
1201
1146
  }
1202
1147
  }
1203
-
1204
1148
  // Leaf element body. Includes the body for the last 'node' block. You might
1205
1149
  // have several 'body' calls for a single 'node' block
1206
1150
  //process.stdout.write(data.value);
1207
1151
  break;
1208
1152
  }
1209
-
1210
1153
  setImmediate(done);
1211
1154
  }
1212
-
1155
+ /**
1156
+ * Formats parsed addresses for an HTML context, every value HTML-escaped.
1157
+ */
1213
1158
  getAddressesHTML(value) {
1214
- let formatSingleLevel = addresses =>
1215
- addresses
1216
- .map(address => {
1217
- let str = '<span class="mp_address_group">';
1218
- if (address.name) {
1219
- str += '<span class="mp_address_name">' + he.encode(address.name) + (address.group ? ': ' : '') + '</span>';
1220
- }
1221
- if (address.address) {
1222
- let link = '<a href="mailto:' + he.encode(address.address) + '" class="mp_address_email">' + he.encode(address.address) + '</a>';
1223
- if (address.name) {
1224
- str += ' &lt;' + link + '&gt;';
1225
- } else {
1226
- str += link;
1227
- }
1228
- }
1229
- if (address.group) {
1230
- str += formatSingleLevel(address.group) + ';';
1231
- }
1232
- return str + '</span>';
1233
- })
1234
- .join(', ');
1159
+ let formatSingleLevel = (addresses) => addresses
1160
+ .map(address => {
1161
+ let str = '<span class="mp_address_group">';
1162
+ if (address.name) {
1163
+ str += '<span class="mp_address_name">' + he.encode(address.name) + (address.group ? ': ' : '') + '</span>';
1164
+ }
1165
+ if (address.address) {
1166
+ let link = '<a href="mailto:' + he.encode(address.address) + '" class="mp_address_email">' + he.encode(address.address) + '</a>';
1167
+ if (address.name) {
1168
+ str += ' &lt;' + link + '&gt;';
1169
+ }
1170
+ else {
1171
+ str += link;
1172
+ }
1173
+ }
1174
+ if (address.group) {
1175
+ str += formatSingleLevel(address.group) + ';';
1176
+ }
1177
+ return str + '</span>';
1178
+ })
1179
+ .join(', ');
1235
1180
  return formatSingleLevel([].concat(value || []));
1236
1181
  }
1237
-
1182
+ /**
1183
+ * Formats parsed addresses as plain text, display names quoted.
1184
+ */
1238
1185
  getAddressesText(value) {
1239
- let formatSingleLevel = addresses =>
1240
- addresses
1241
- .map(address => {
1242
- let str = '';
1243
- if (address.name) {
1244
- // a quoted-string, so a quote or backslash inside the name is escaped
1245
- str += `"${address.name.replace(/["\\]/g, '\\$&')}"` + (address.group ? ': ' : '');
1246
- }
1247
- if (address.address) {
1248
- let link = address.address;
1249
- if (address.name) {
1250
- str += ' <' + link + '>';
1251
- } else {
1252
- str += link;
1253
- }
1254
- }
1255
- if (address.group) {
1256
- str += formatSingleLevel(address.group) + ';';
1257
- }
1258
- return str;
1259
- })
1260
- .join(', ');
1186
+ let formatSingleLevel = (addresses) => addresses
1187
+ .map(address => {
1188
+ let str = '';
1189
+ if (address.name) {
1190
+ // a quoted-string, so a quote or backslash inside the name is escaped
1191
+ str += `"${address.name.replace(/["\\]/g, '\\$&')}"` + (address.group ? ': ' : '');
1192
+ }
1193
+ if (address.address) {
1194
+ let link = address.address;
1195
+ if (address.name) {
1196
+ str += ' <' + link + '>';
1197
+ }
1198
+ else {
1199
+ str += link;
1200
+ }
1201
+ }
1202
+ if (address.group) {
1203
+ str += formatSingleLevel(address.group) + ';';
1204
+ }
1205
+ return str;
1206
+ })
1207
+ .join(', ');
1261
1208
  return formatSingleLevel([].concat(value || []));
1262
1209
  }
1263
-
1210
+ /**
1211
+ * Replaces `cid:` links to image attachments in the HTML content. Call it once the parser has
1212
+ * ended. `replaceCallback` is called once for every referenced image attachment and answers with
1213
+ * the URL to use instead; `done` gets the updated HTML, or `false` when the message has no HTML.
1214
+ * The total length of the URLs written is bounded by `maxInlinedImagesSize`.
1215
+ */
1264
1216
  updateImageLinks(replaceCallback, done) {
1265
1217
  if (!this.html) {
1266
- return setImmediate(() => done(null, false));
1218
+ setImmediate(() => done(null, false));
1219
+ return;
1267
1220
  }
1268
-
1269
1221
  let cids = new Map();
1270
1222
  let html = (this.html || '').toString();
1271
-
1272
1223
  if (this.options.skipImageLinks) {
1273
1224
  return done(null, html);
1274
1225
  }
1275
-
1276
1226
  html.replace(/\bcid:([^'"\s]{1,256})/g, (match, cid) => {
1277
1227
  for (let i = 0, len = this.attachmentList.length; i < len; i++) {
1278
1228
  if (this.attachmentList[i].cid === cid && /^image\/[\w]+$/i.test(this.attachmentList[i].contentType)) {
@@ -1284,12 +1234,10 @@ class MailParser extends Transform {
1284
1234
  }
1285
1235
  return match;
1286
1236
  });
1287
-
1288
1237
  let cidList = [];
1289
1238
  cids.forEach(entry => {
1290
1239
  cidList.push(entry);
1291
1240
  });
1292
-
1293
1241
  let pos = 0;
1294
1242
  let processNext = () => {
1295
1243
  if (pos >= cidList.length) {
@@ -1311,36 +1259,35 @@ class MailParser extends Transform {
1311
1259
  inlinedSize += url.length;
1312
1260
  return url;
1313
1261
  });
1314
- } catch (err) {
1262
+ }
1263
+ catch (err) {
1315
1264
  // runs from setImmediate, a throw here would be an uncaught exception
1316
1265
  return done(err);
1317
1266
  }
1318
-
1319
1267
  return done(null, html);
1320
1268
  }
1321
1269
  let entry = cidList[pos++];
1322
1270
  replaceCallback(entry.attachment, (err, url) => {
1323
1271
  if (err) {
1324
- return setImmediate(() => done(err));
1272
+ setImmediate(() => done(err));
1273
+ return;
1325
1274
  }
1326
1275
  entry.url = url;
1327
1276
  setImmediate(processNext);
1328
1277
  });
1329
1278
  };
1330
-
1331
1279
  setImmediate(processNext);
1332
1280
  }
1333
-
1334
1281
  // Tells whether a text segment is worth handing to linkify-it, see
1335
1282
  // MAX_LINKIFY_* above. Note that linkify.pretest() can not be used for this
1336
1283
  // - it is a quick deny that also denies links its own matcher finds, eg. a
1337
1284
  // host followed by a fullwidth vertical bar.
1285
+ /** @internal */
1338
1286
  canScanSegment(segment) {
1339
1287
  // every link linkify-it can find contains at least one of these
1340
1288
  if (segment.length > MAX_LINKIFY_SEGMENT_LENGTH || !/[.:@/]/.test(segment)) {
1341
1289
  return false;
1342
1290
  }
1343
-
1344
1291
  // Dots only chain up into one host candidate while the characters
1345
1292
  // around them could be part of a host, so a slash, comma or colon ends
1346
1293
  // the chain and dots in a path or query do not add to it.
@@ -1351,21 +1298,19 @@ class MailParser extends Transform {
1351
1298
  if (++labels > MAX_LINKIFY_HOST_LABELS) {
1352
1299
  return false;
1353
1300
  }
1354
- } else if (!isHostChar(code)) {
1301
+ }
1302
+ else if (!isHostChar(code)) {
1355
1303
  labels = 0;
1356
1304
  }
1357
1305
  }
1358
-
1359
1306
  return true;
1360
1307
  }
1361
-
1308
+ /** @internal */
1362
1309
  findLinks(str) {
1363
1310
  let links = [];
1364
-
1365
1311
  if (this.linkifyWork >= MAX_LINKIFY_WORK || !linkify.pretest(str)) {
1366
1312
  return links;
1367
1313
  }
1368
-
1369
1314
  // Whitespace can not be part of a link and linkify-it treats the end of
1370
1315
  // a segment exactly like whitespace, so scanning segment by segment
1371
1316
  // finds the same links as scanning the entire string would. U+FEFF is
@@ -1373,89 +1318,80 @@ class MailParser extends Transform {
1373
1318
  // linkify-it, so it must not end a segment.
1374
1319
  let segmentRegex = /[\S\uFEFF]+/g;
1375
1320
  let segmentMatch;
1376
-
1377
1321
  while ((segmentMatch = segmentRegex.exec(str)) !== null) {
1378
1322
  let segment = segmentMatch[0];
1379
-
1380
1323
  if (!this.canScanSegment(segment)) {
1381
1324
  // too expensive to scan or can not match at all, keep as plain text
1382
1325
  continue;
1383
1326
  }
1384
-
1385
1327
  let matches = linkify.match(segment);
1386
1328
  for (let i = 0; matches && i < matches.length; i++) {
1387
1329
  matches[i].index += segmentMatch.index;
1388
1330
  matches[i].lastIndex += segmentMatch.index;
1389
1331
  links.push(matches[i]);
1390
1332
  }
1391
-
1392
1333
  this.linkifyWork += segment.length * (segment.length + 1024);
1393
1334
  if (this.linkifyWork >= MAX_LINKIFY_WORK) {
1394
1335
  break;
1395
1336
  }
1396
1337
  }
1397
-
1398
1338
  return links;
1399
1339
  }
1400
-
1340
+ /**
1341
+ * Converts plain text to HTML the way `textAsHtml` is built: HTML-escaped, paragraphs and line
1342
+ * breaks kept, and links turned into anchors unless `skipTextLinks` is set. Returns an empty
1343
+ * string when `skipTextToHtml` is set.
1344
+ */
1401
1345
  textToHtml(str) {
1402
1346
  if (this.options.skipTextToHtml) {
1403
1347
  return '';
1404
1348
  }
1405
1349
  str = (str || '').toString();
1406
1350
  let encoded;
1407
-
1408
1351
  if (!this.options.skipTextLinks) {
1409
1352
  try {
1410
1353
  let links = this.findLinks(str);
1411
1354
  if (links.length) {
1412
1355
  let result = [];
1413
1356
  let last = 0;
1414
-
1415
1357
  links.forEach(link => {
1416
1358
  if (last < link.index) {
1417
1359
  let textPart = he
1418
1360
  // encode special chars
1419
1361
  .encode(str.slice(last, link.index), {
1420
- useNamedReferences: true
1421
- });
1362
+ useNamedReferences: true
1363
+ });
1422
1364
  result.push(textPart);
1423
1365
  }
1424
-
1425
1366
  // Escape quotes in URL to prevent XSS
1426
1367
  let safeUrl = link.url.replace(/"/g, '&quot;');
1427
1368
  // Escape HTML entities in link text
1428
1369
  let safeText = he.encode(link.text, { useNamedReferences: true });
1429
1370
  result.push(`<a href="${safeUrl}">${safeText}</a>`);
1430
-
1431
1371
  last = link.lastIndex;
1432
1372
  });
1433
-
1434
1373
  let textPart = he
1435
1374
  // encode special chars
1436
1375
  .encode(str.slice(last), {
1437
- useNamedReferences: true
1438
- });
1376
+ useNamedReferences: true
1377
+ });
1439
1378
  result.push(textPart);
1440
-
1441
1379
  encoded = result.join('');
1442
1380
  }
1443
- } catch (E) {
1381
+ }
1382
+ catch {
1444
1383
  // failed, don't linkify
1445
1384
  }
1446
1385
  }
1447
-
1448
1386
  // nothing linkified, or linkifying threw halfway through
1449
1387
  if (!encoded) {
1450
1388
  encoded = he
1451
1389
  // encode special chars
1452
1390
  .encode(str, {
1453
- useNamedReferences: true
1454
- });
1391
+ useNamedReferences: true
1392
+ });
1455
1393
  }
1456
-
1457
- let text =
1458
- '<p>' +
1394
+ let text = '<p>' +
1459
1395
  encoded
1460
1396
  .replace(/\r?\n/g, '\n')
1461
1397
  .trim() // normalize line endings
@@ -1470,9 +1406,7 @@ class MailParser extends Transform {
1470
1406
  .trim() // insert <p> to multiple linebreaks
1471
1407
  .replace(/\n/g, '<br/>') + // insert <br> to single linebreaks
1472
1408
  '</p>';
1473
-
1474
1409
  return text;
1475
1410
  }
1476
1411
  }
1477
-
1478
- module.exports = MailParser;
1412
+ export default MailParser;