@nocobase/plugin-workflow-mailer 2.3.0-beta.9 → 2.4.0-alpha.10

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 (187) hide show
  1. package/dist/externalVersion.js +6 -6
  2. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -1
  3. package/dist/node_modules/joi/lib/index.js +1 -1
  4. package/dist/node_modules/joi/lib/messages.js +35 -6
  5. package/dist/node_modules/joi/lib/types/keys.js +18 -1
  6. package/dist/node_modules/joi/lib/types/link.js +18 -2
  7. package/dist/node_modules/joi/lib/types/string.js +2 -2
  8. package/dist/node_modules/joi/package.json +1 -1
  9. package/dist/node_modules/nodemailer/dist/cjs/addressparser/index.d.ts +59 -0
  10. package/dist/node_modules/nodemailer/dist/cjs/addressparser/index.js +790 -0
  11. package/dist/node_modules/nodemailer/dist/cjs/base64/index.d.ts +36 -0
  12. package/dist/node_modules/nodemailer/dist/cjs/base64/index.js +119 -0
  13. package/dist/node_modules/nodemailer/dist/cjs/dkim/index.d.ts +52 -0
  14. package/dist/node_modules/nodemailer/dist/cjs/dkim/index.js +228 -0
  15. package/dist/node_modules/nodemailer/dist/cjs/dkim/message-parser.d.ts +48 -0
  16. package/dist/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +180 -0
  17. package/dist/node_modules/nodemailer/dist/cjs/dkim/relaxed-body.d.ts +36 -0
  18. package/dist/node_modules/nodemailer/dist/cjs/dkim/relaxed-body.js +148 -0
  19. package/dist/node_modules/nodemailer/dist/cjs/dkim/sign.d.ts +52 -0
  20. package/dist/node_modules/nodemailer/dist/cjs/dkim/sign.js +165 -0
  21. package/dist/node_modules/nodemailer/dist/cjs/errors.d.ts +96 -0
  22. package/dist/node_modules/nodemailer/dist/cjs/errors.js +70 -0
  23. package/dist/node_modules/nodemailer/dist/cjs/fetch/cookies.d.ts +107 -0
  24. package/dist/node_modules/nodemailer/dist/cjs/fetch/cookies.js +282 -0
  25. package/dist/node_modules/nodemailer/dist/cjs/fetch/index.d.ts +65 -0
  26. package/dist/node_modules/nodemailer/dist/cjs/fetch/index.js +384 -0
  27. package/dist/node_modules/nodemailer/dist/cjs/json-transport/index.d.ts +64 -0
  28. package/dist/node_modules/nodemailer/dist/cjs/json-transport/index.js +99 -0
  29. package/dist/node_modules/nodemailer/dist/cjs/mail-composer/index.d.ts +174 -0
  30. package/dist/node_modules/nodemailer/dist/cjs/mail-composer/index.js +613 -0
  31. package/dist/node_modules/nodemailer/dist/cjs/mailer/index.d.ts +237 -0
  32. package/dist/node_modules/nodemailer/dist/cjs/mailer/index.js +415 -0
  33. package/dist/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +97 -0
  34. package/dist/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +342 -0
  35. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/index.d.ts +161 -0
  36. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +787 -0
  37. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/mime-types.d.ts +2 -0
  38. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/mime-types.js +2103 -0
  39. package/dist/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +333 -0
  40. package/dist/node_modules/nodemailer/dist/cjs/mime-node/index.js +1493 -0
  41. package/dist/node_modules/nodemailer/dist/cjs/mime-node/last-newline.d.ts +5 -0
  42. package/dist/node_modules/nodemailer/dist/cjs/mime-node/last-newline.js +32 -0
  43. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-unix.d.ts +9 -0
  44. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-unix.js +40 -0
  45. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-windows.d.ts +10 -0
  46. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-windows.js +47 -0
  47. package/dist/node_modules/nodemailer/dist/cjs/nodemailer.d.ts +124 -0
  48. package/dist/node_modules/nodemailer/dist/cjs/nodemailer.js +1 -0
  49. package/dist/node_modules/nodemailer/dist/cjs/package-info.d.ts +3 -0
  50. package/dist/node_modules/nodemailer/dist/cjs/package-info.js +7 -0
  51. package/dist/node_modules/nodemailer/dist/cjs/package.json +3 -0
  52. package/dist/node_modules/nodemailer/dist/cjs/punycode/index.d.ts +73 -0
  53. package/dist/node_modules/nodemailer/{lib → dist/cjs}/punycode/index.js +63 -112
  54. package/dist/node_modules/nodemailer/dist/cjs/qp/index.d.ts +32 -0
  55. package/dist/node_modules/nodemailer/dist/cjs/qp/index.js +207 -0
  56. package/dist/node_modules/nodemailer/dist/cjs/sendmail-transport/index.d.ts +69 -0
  57. package/dist/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +210 -0
  58. package/dist/node_modules/nodemailer/dist/cjs/ses-transport/index.d.ts +112 -0
  59. package/dist/node_modules/nodemailer/dist/cjs/ses-transport/index.js +252 -0
  60. package/dist/node_modules/nodemailer/dist/cjs/shared/index.d.ts +260 -0
  61. package/dist/node_modules/nodemailer/dist/cjs/shared/index.js +680 -0
  62. package/dist/node_modules/nodemailer/dist/cjs/shared/objects.d.ts +23 -0
  63. package/dist/node_modules/nodemailer/dist/cjs/shared/objects.js +43 -0
  64. package/dist/node_modules/nodemailer/dist/cjs/shared/url.d.ts +21 -0
  65. package/dist/node_modules/nodemailer/dist/cjs/shared/url.js +255 -0
  66. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.d.ts +15 -0
  67. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.js +108 -0
  68. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +47 -0
  69. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +189 -0
  70. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +416 -0
  71. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +1837 -0
  72. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +133 -0
  73. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +569 -0
  74. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.d.ts +62 -0
  75. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +257 -0
  76. package/dist/node_modules/nodemailer/dist/cjs/smtp-transport/index.d.ts +156 -0
  77. package/dist/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +371 -0
  78. package/dist/node_modules/nodemailer/dist/cjs/stream-transport/index.d.ts +65 -0
  79. package/dist/node_modules/nodemailer/dist/cjs/stream-transport/index.js +146 -0
  80. package/dist/node_modules/nodemailer/dist/cjs/well-known/index.d.ts +35 -0
  81. package/dist/node_modules/nodemailer/dist/cjs/well-known/index.js +41 -0
  82. package/dist/node_modules/nodemailer/dist/cjs/well-known/services.d.ts +2 -0
  83. package/dist/node_modules/nodemailer/dist/cjs/well-known/services.js +674 -0
  84. package/dist/node_modules/nodemailer/dist/cjs/xoauth2/index.d.ts +201 -0
  85. package/dist/node_modules/nodemailer/dist/cjs/xoauth2/index.js +426 -0
  86. package/dist/node_modules/nodemailer/dist/esm/addressparser/index.d.ts +59 -0
  87. package/dist/node_modules/nodemailer/dist/esm/addressparser/index.js +786 -0
  88. package/dist/node_modules/nodemailer/dist/esm/base64/index.d.ts +36 -0
  89. package/dist/node_modules/nodemailer/{lib → dist/esm}/base64/index.js +39 -68
  90. package/dist/node_modules/nodemailer/dist/esm/dkim/index.d.ts +52 -0
  91. package/dist/node_modules/nodemailer/{lib → dist/esm}/dkim/index.js +62 -92
  92. package/dist/node_modules/nodemailer/dist/esm/dkim/message-parser.d.ts +48 -0
  93. package/dist/node_modules/nodemailer/dist/esm/dkim/message-parser.js +176 -0
  94. package/dist/node_modules/nodemailer/dist/esm/dkim/relaxed-body.d.ts +36 -0
  95. package/dist/node_modules/nodemailer/dist/esm/dkim/relaxed-body.js +141 -0
  96. package/dist/node_modules/nodemailer/dist/esm/dkim/sign.d.ts +52 -0
  97. package/dist/node_modules/nodemailer/dist/esm/dkim/sign.js +125 -0
  98. package/dist/node_modules/nodemailer/dist/esm/errors.d.ts +96 -0
  99. package/dist/node_modules/nodemailer/dist/esm/errors.js +67 -0
  100. package/dist/node_modules/nodemailer/dist/esm/fetch/cookies.d.ts +107 -0
  101. package/dist/node_modules/nodemailer/dist/esm/fetch/cookies.js +242 -0
  102. package/dist/node_modules/nodemailer/dist/esm/fetch/index.d.ts +65 -0
  103. package/dist/node_modules/nodemailer/dist/esm/fetch/index.js +344 -0
  104. package/dist/node_modules/nodemailer/dist/esm/json-transport/index.d.ts +64 -0
  105. package/dist/node_modules/nodemailer/dist/esm/json-transport/index.js +62 -0
  106. package/dist/node_modules/nodemailer/dist/esm/mail-composer/index.d.ts +174 -0
  107. package/dist/node_modules/nodemailer/{lib → dist/esm}/mail-composer/index.js +283 -275
  108. package/dist/node_modules/nodemailer/dist/esm/mailer/index.d.ts +237 -0
  109. package/dist/node_modules/nodemailer/{lib → dist/esm}/mailer/index.js +151 -205
  110. package/dist/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +97 -0
  111. package/dist/node_modules/nodemailer/{lib → dist/esm}/mailer/mail-message.js +111 -125
  112. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/index.d.ts +161 -0
  113. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/index.js +738 -0
  114. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/mime-types.d.ts +2 -0
  115. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-funcs/mime-types.js +43 -49
  116. package/dist/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +333 -0
  117. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/index.js +576 -437
  118. package/dist/node_modules/nodemailer/dist/esm/mime-node/last-newline.d.ts +5 -0
  119. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/last-newline.js +4 -10
  120. package/dist/node_modules/nodemailer/dist/esm/mime-node/le-unix.d.ts +9 -0
  121. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/le-unix.js +7 -15
  122. package/dist/node_modules/nodemailer/dist/esm/mime-node/le-windows.d.ts +10 -0
  123. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/le-windows.js +6 -16
  124. package/dist/node_modules/nodemailer/dist/esm/nodemailer.d.ts +124 -0
  125. package/dist/node_modules/nodemailer/dist/esm/nodemailer.js +166 -0
  126. package/dist/node_modules/nodemailer/dist/esm/package-info.d.ts +3 -0
  127. package/dist/node_modules/nodemailer/dist/esm/package-info.js +4 -0
  128. package/dist/node_modules/nodemailer/dist/esm/package.json +3 -0
  129. package/dist/node_modules/nodemailer/dist/esm/punycode/index.d.ts +73 -0
  130. package/dist/node_modules/nodemailer/dist/esm/punycode/index.js +405 -0
  131. package/dist/node_modules/nodemailer/dist/esm/qp/index.d.ts +32 -0
  132. package/dist/node_modules/nodemailer/{lib → dist/esm}/qp/index.js +59 -77
  133. package/dist/node_modules/nodemailer/dist/esm/sendmail-transport/index.d.ts +69 -0
  134. package/dist/node_modules/nodemailer/dist/esm/sendmail-transport/index.js +170 -0
  135. package/dist/node_modules/nodemailer/dist/esm/ses-transport/index.d.ts +112 -0
  136. package/dist/node_modules/nodemailer/dist/esm/ses-transport/index.js +212 -0
  137. package/dist/node_modules/nodemailer/dist/esm/shared/index.d.ts +260 -0
  138. package/dist/node_modules/nodemailer/dist/esm/shared/index.js +631 -0
  139. package/dist/node_modules/nodemailer/dist/esm/shared/objects.d.ts +23 -0
  140. package/dist/node_modules/nodemailer/dist/esm/shared/objects.js +38 -0
  141. package/dist/node_modules/nodemailer/dist/esm/shared/url.d.ts +21 -0
  142. package/dist/node_modules/nodemailer/dist/esm/shared/url.js +214 -0
  143. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.d.ts +15 -0
  144. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.js +103 -0
  145. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +47 -0
  146. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +149 -0
  147. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +416 -0
  148. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/index.js +1797 -0
  149. package/dist/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +133 -0
  150. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-pool/index.js +195 -314
  151. package/dist/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.d.ts +62 -0
  152. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-pool/pool-resource.js +70 -107
  153. package/dist/node_modules/nodemailer/dist/esm/smtp-transport/index.d.ts +156 -0
  154. package/dist/node_modules/nodemailer/dist/esm/smtp-transport/index.js +331 -0
  155. package/dist/node_modules/nodemailer/dist/esm/stream-transport/index.d.ts +65 -0
  156. package/dist/node_modules/nodemailer/dist/esm/stream-transport/index.js +106 -0
  157. package/dist/node_modules/nodemailer/dist/esm/well-known/index.d.ts +35 -0
  158. package/dist/node_modules/nodemailer/{lib → dist/esm}/well-known/index.js +12 -23
  159. package/dist/node_modules/nodemailer/dist/esm/well-known/services.d.ts +2 -0
  160. package/dist/node_modules/nodemailer/dist/esm/well-known/services.js +671 -0
  161. package/dist/node_modules/nodemailer/dist/esm/xoauth2/index.d.ts +201 -0
  162. package/dist/node_modules/nodemailer/dist/esm/xoauth2/index.js +386 -0
  163. package/dist/node_modules/nodemailer/dist/well-known/services.json +640 -0
  164. package/dist/node_modules/nodemailer/package.json +1 -1
  165. package/package.json +4 -4
  166. package/dist/node_modules/nodemailer/.gitattributes +0 -6
  167. package/dist/node_modules/nodemailer/.ncurc.js +0 -7
  168. package/dist/node_modules/nodemailer/.prettierrc.js +0 -8
  169. package/dist/node_modules/nodemailer/lib/addressparser/index.js +0 -313
  170. package/dist/node_modules/nodemailer/lib/dkim/message-parser.js +0 -155
  171. package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +0 -154
  172. package/dist/node_modules/nodemailer/lib/dkim/sign.js +0 -117
  173. package/dist/node_modules/nodemailer/lib/fetch/cookies.js +0 -281
  174. package/dist/node_modules/nodemailer/lib/fetch/index.js +0 -274
  175. package/dist/node_modules/nodemailer/lib/json-transport/index.js +0 -82
  176. package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +0 -625
  177. package/dist/node_modules/nodemailer/lib/nodemailer.js +0 -1
  178. package/dist/node_modules/nodemailer/lib/sendmail-transport/index.js +0 -210
  179. package/dist/node_modules/nodemailer/lib/ses-transport/index.js +0 -349
  180. package/dist/node_modules/nodemailer/lib/shared/index.js +0 -688
  181. package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +0 -108
  182. package/dist/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +0 -143
  183. package/dist/node_modules/nodemailer/lib/smtp-connection/index.js +0 -1825
  184. package/dist/node_modules/nodemailer/lib/smtp-transport/index.js +0 -416
  185. package/dist/node_modules/nodemailer/lib/stream-transport/index.js +0 -135
  186. package/dist/node_modules/nodemailer/lib/well-known/services.json +0 -343
  187. package/dist/node_modules/nodemailer/lib/xoauth2/index.js +0 -376
@@ -0,0 +1,790 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Restores the quoting of a local part that was read out of a quoted string.
5
+ *
6
+ * RFC 5321 allows '@' inside a quoted local part, so handing '"user@evil.com"@good.com'
7
+ * on as the bare 'user@evil.com@good.com' leaves it to the consumer which '@' splits the
8
+ * domain off. Getting that wrong is a misrouting vector, so the quotes go back on. The
9
+ * same holds for the other specials: a ',' or a ';' that loses its quotes reads as a
10
+ * recipient separator once the consumer puts the address back into a header.
11
+ *
12
+ * This module has no dependencies so that it can ship on its own, which is why the two
13
+ * grammar tests below are spelled out here instead of shared with src/mime-node. Keeping
14
+ * only what is ambiguous quoted is deliberate, mime-node applies the stricter RFC 5321
15
+ * dot-atom rule on top of this when it emits an address.
16
+ *
17
+ * @param address Address with an unquoted local part
18
+ * @return Address with the local part as a quoted-string
19
+ */
20
+ function _quoteLocalPart(address) {
21
+ const lastAt = address.lastIndexOf('@');
22
+ if (lastAt < 0) {
23
+ // no domain to split off, nothing can be misrouted
24
+ return address;
25
+ }
26
+ const user = address.substr(0, lastAt);
27
+ if (/^[^\s"(),:;<>@[\\\]]+$/.test(user) || /^"(?:[^"\\]|\\[\s\S])*"$/.test(user)) {
28
+ // a local part that carries no special reads the same with or without the quotes,
29
+ // and one that is already a complete quoted-string needs nothing either
30
+ return address;
31
+ }
32
+ return '"' + user.replace(/["\\]/g, '\\$&') + '"@' + address.substr(lastAt + 1);
33
+ }
34
+ /**
35
+ * Reached for every parsed address, so it is built once rather than per call.
36
+ */
37
+ const HAS_WHITESPACE = /\s/;
38
+ /**
39
+ * An addr-spec that carries its whitespace legally, inside a quoted local part. The
40
+ * optional tail is the malformed shape: a real mailbox with wreckage trailing it.
41
+ */
42
+ const QUOTED_LOCAL_ADDR = /^("(?:[^"\\]|\\[\s\S])*"@\S+)(?:\s+([\s\S]+))?$/;
43
+ /**
44
+ * One run holding a single '@' and no whitespace, the shape an addr-spec has to have.
45
+ */
46
+ const ADDR_SPEC = /^[^@\s]+@[^@\s]+$/;
47
+ /**
48
+ * The looser reading applied once the strict one finds nothing, which tolerates the
49
+ * further '@' that a domain should not have but malformed headers carry anyway.
50
+ */
51
+ const LOOSE_ADDR_SPEC = /^[^@\s]+@\S+$/;
52
+ /**
53
+ * An addr-spec sitting inside free text, together with the whitespace around it. Sticky
54
+ * on purpose: it is run at the one offset _looseAddressStart picks rather than being let
55
+ * loose to search, see there.
56
+ */
57
+ const LOOSE_TEXT_ADDR = /\s*\b[^@\s]+@[^\s]+\b\s*/y;
58
+ /**
59
+ * The characters JS `\s` matches, which the scan below has to agree with to land on the
60
+ * same match the pattern would.
61
+ */
62
+ function _isSpaceCode(code) {
63
+ return (code === 0x20 ||
64
+ (code >= 0x09 && code <= 0x0d) ||
65
+ code === 0xa0 ||
66
+ code === 0x1680 ||
67
+ (code >= 0x2000 && code <= 0x200a) ||
68
+ code === 0x2028 ||
69
+ code === 0x2029 ||
70
+ code === 0x202f ||
71
+ code === 0x205f ||
72
+ code === 0x3000 ||
73
+ code === 0xfeff);
74
+ }
75
+ /**
76
+ * The characters JS `\w` matches without the unicode flag, the set the `\b` in
77
+ * LOOSE_TEXT_ADDR is read against. charCodeAt off either end of the string gives NaN,
78
+ * which compares false throughout, so out of range reads as the non-word the pattern
79
+ * treats them as.
80
+ */
81
+ function _isWordCode(code) {
82
+ return (code >= 0x30 && code <= 0x39) || (code >= 0x41 && code <= 0x5a) || (code >= 0x61 && code <= 0x7a) || code === 0x5f;
83
+ }
84
+ /**
85
+ * Whether `\b` holds at an offset
86
+ */
87
+ function _isBoundary(text, at) {
88
+ return _isWordCode(text.charCodeAt(at - 1)) !== _isWordCode(text.charCodeAt(at));
89
+ }
90
+ /**
91
+ * Offset of the first '@' in `text` between `from` and `to`, or -1 when the range holds none.
92
+ *
93
+ * indexOf would scan on to the end of the value, and the value here is a whole header. The
94
+ * walk below steps one whitespace delimited run at a time and only ever uses a '@' that sits
95
+ * inside the run it is on, so an unbounded probe rescans everything behind that run once per
96
+ * run and grows with the square of the header: 400KB of free text carrying no '@' took a
97
+ * quarter of a second. GHSA-v53p-9fqp-m79j took the pattern search out of this walk and left
98
+ * the probe unbounded behind it.
99
+ *
100
+ * @param text Text to look in
101
+ * @param from Offset to start at
102
+ * @param to Offset to stop before
103
+ * @return Offset of the '@', or -1
104
+ */
105
+ function _indexOfAt(text, from, to) {
106
+ for (let i = from; i < to; i++) {
107
+ if (text.charCodeAt(i) === 0x40) {
108
+ return i;
109
+ }
110
+ }
111
+ return -1;
112
+ }
113
+ /**
114
+ * Finds the offset LOOSE_TEXT_ADDR matches at, or -1 when it does not match at all.
115
+ *
116
+ * Letting the pattern search for itself is quadratic: '[^@\s]+' is retried from every
117
+ * offset and rescans the run to the next '@' each time, so 140KB of header holding no
118
+ * usable '@' blocks the event loop for about ten seconds (GHSA-v53p-9fqp-m79j). The search is also unnecessary.
119
+ * '[^@\s]+' crosses neither whitespace nor a '@', so a match can only begin at the head of
120
+ * a whitespace delimited run or just past a '@' inside one, and '[^\s]+\b' gives characters
121
+ * back until it lands on a boundary, so the only end it can take in that run is the last
122
+ * boundary in it. Both are found in one pass, and the pattern is then run at that single
123
+ * offset.
124
+ *
125
+ * @param text Free text to look in
126
+ * @return Offset to match at, or -1
127
+ */
128
+ function _looseAddressStart(text) {
129
+ const len = text.length;
130
+ let pos = 0;
131
+ while (pos < len) {
132
+ while (pos < len && _isSpaceCode(text.charCodeAt(pos))) {
133
+ pos++;
134
+ }
135
+ if (pos >= len) {
136
+ break;
137
+ }
138
+ const runStart = pos;
139
+ let runEnd = pos;
140
+ while (runEnd < len && !_isSpaceCode(text.charCodeAt(runEnd))) {
141
+ runEnd++;
142
+ }
143
+ let at = _indexOfAt(text, runStart, runEnd);
144
+ if (at >= 0) {
145
+ let lastBoundary = -1;
146
+ for (let k = runEnd; k > runStart; k--) {
147
+ if (_isBoundary(text, k)) {
148
+ lastBoundary = k;
149
+ break;
150
+ }
151
+ }
152
+ let atomStart = runStart;
153
+ while (lastBoundary >= 0 && at >= 0) {
154
+ // '[^@\s]+' has to cover a character before the '@' and '[^\s]+' one after it,
155
+ // and the boundary that ends the match has to sit past both
156
+ if (at > atomStart && runEnd > at + 1 && lastBoundary > at + 1) {
157
+ for (let start = atomStart; start < at; start++) {
158
+ if (_isBoundary(text, start)) {
159
+ if (start > runStart) {
160
+ return start;
161
+ }
162
+ // the leading '\s*' is greedy, so a match that begins at the run
163
+ // takes the whitespace in front of it along
164
+ let padded = runStart;
165
+ while (padded > 0 && _isSpaceCode(text.charCodeAt(padded - 1))) {
166
+ padded--;
167
+ }
168
+ return padded;
169
+ }
170
+ }
171
+ }
172
+ atomStart = at + 1;
173
+ at = _indexOfAt(text, atomStart, runEnd);
174
+ }
175
+ }
176
+ pos = runEnd;
177
+ }
178
+ return -1;
179
+ }
180
+ /**
181
+ * Recovers the addr-spec from an angle-addr that came back holding unquoted whitespace.
182
+ *
183
+ * A malformed header can put more than a mailbox between the angle brackets, most often
184
+ * because the generator wrote the recipient twice: '<user@example.com user@example.com>'
185
+ * or '<example.com user@example.com>'. Whitespace is not addr-spec, so the whole run can
186
+ * never be a mailbox anyone could deliver to, and passing it on as the address loses the
187
+ * recipient that is sitting right there in the header.
188
+ *
189
+ * The run that still reads as an addr-spec is kept and whatever is left over becomes
190
+ * display text rather than being dropped. Candidates are read strictly first and then
191
+ * under the looser grammar, the same two tiers the unquoted-text branch below applies to
192
+ * the same problem, so that '<a@b@c.com junk>' and a bare 'a@b@c.com junk' agree on the
193
+ * recipient. When several runs qualify the first wins, which is what that branch's looser
194
+ * tier does within a token.
195
+ *
196
+ * A quoted local part is left alone: RFC 5321 allows whitespace inside it, so
197
+ * '<"user name"@example.com>' is well formed and means exactly what it says.
198
+ *
199
+ * @param data Collected address parts, mutated in place
200
+ */
201
+ function _recoverAddrSpec(data) {
202
+ if (!HAS_WHITESPACE.test(data.address)) {
203
+ return;
204
+ }
205
+ let address;
206
+ let rest;
207
+ const quoted = data.address.match(QUOTED_LOCAL_ADDR);
208
+ if (quoted) {
209
+ if (!quoted[2]) {
210
+ // the whitespace sits inside the quoted local part, this is a well formed mailbox
211
+ return;
212
+ }
213
+ // a real mailbox with wreckage trailing it, so peel the addr-spec off whole rather
214
+ // than splitting into the quotes
215
+ address = quoted[1];
216
+ rest = [quoted[2]];
217
+ }
218
+ else {
219
+ if (data.address.indexOf('"') >= 0) {
220
+ // Splitting on whitespace loses track of where the quoted string starts and ends,
221
+ // and this module does not take addresses out of quoted strings: the run picked out
222
+ // of '<junk "user@evil.com b"@good.com>' would be an address from the domain the
223
+ // quotes were hiding. Every well formed shape was already handled above, so what is
224
+ // left is wreckage either way and the original is the honest answer
225
+ return;
226
+ }
227
+ const parts = data.address.split(/\s+/);
228
+ let addrIndex = parts.findIndex(part => ADDR_SPEC.test(part));
229
+ if (addrIndex < 0) {
230
+ addrIndex = parts.findIndex(part => LOOSE_ADDR_SPEC.test(part));
231
+ }
232
+ if (addrIndex < 0) {
233
+ // nothing in there reads as an address, there is no better answer than the original
234
+ return;
235
+ }
236
+ address = parts.splice(addrIndex, 1)[0];
237
+ rest = parts;
238
+ }
239
+ data.address = address;
240
+ data.text = [data.text]
241
+ .concat(rest)
242
+ .filter(part => part)
243
+ .join(' ');
244
+ }
245
+ /**
246
+ * Takes the RFC 5322 comments out of the contents of an angle-addr.
247
+ *
248
+ * The tokenizer tracks a single open operator, so once '<' is open a '(' is plain text and
249
+ * a comment inside the brackets reached the address verbatim: 'Name <user@example.com(x)evil.com>'
250
+ * put 'user@example.com(x)evil.com' into the envelope, a value that is no mailbox and that a
251
+ * receiver stripping the comment reads as 'user@example.comevil.com' (GHSA-g73g-hqqh-jr95).
252
+ * A comment is folding whitespace, so it is read as a space that splits two atoms and as
253
+ * nothing next to an '@', the same rule the token walk applies outside the brackets. Quoted
254
+ * strings and domain literals are copied through untouched, a '(' in there is not a comment.
255
+ *
256
+ * @param address Contents of the angle brackets
257
+ * @return The address with the comments removed, and the text of the comments
258
+ */
259
+ function _stripAddressComments(address) {
260
+ const comments = [];
261
+ let result = '';
262
+ let comment = '';
263
+ let depth = 0;
264
+ let closer = '';
265
+ // carried along rather than read back off the growing result, which would flatten it on
266
+ // every comment (see lastChars in _handleAddress)
267
+ let lastChar = '';
268
+ for (let i = 0, len = address.length; i < len; i++) {
269
+ const chr = address.charAt(i);
270
+ if (depth) {
271
+ if (chr === '\\' && i < len - 1) {
272
+ comment += address.charAt(++i);
273
+ }
274
+ else if (chr === '(') {
275
+ depth++;
276
+ comment += chr;
277
+ }
278
+ else if (chr === ')' && !--depth) {
279
+ comments.push(comment.trim());
280
+ comment = '';
281
+ if (lastChar !== '@' && address.charAt(i + 1) !== '@') {
282
+ result += ' ';
283
+ lastChar = ' ';
284
+ }
285
+ }
286
+ else {
287
+ comment += chr;
288
+ }
289
+ continue;
290
+ }
291
+ if (closer) {
292
+ if (chr === '\\' && closer === '"' && i < len - 1) {
293
+ result += chr + address.charAt(++i);
294
+ lastChar = address.charAt(i);
295
+ continue;
296
+ }
297
+ if (chr === closer) {
298
+ closer = '';
299
+ }
300
+ }
301
+ else if (chr === '"') {
302
+ closer = '"';
303
+ }
304
+ else if (chr === '[') {
305
+ closer = ']';
306
+ }
307
+ else if (chr === '(') {
308
+ depth = 1;
309
+ continue;
310
+ }
311
+ result += chr;
312
+ lastChar = chr;
313
+ }
314
+ if (depth) {
315
+ // an unterminated comment runs to the end of the address
316
+ comments.push(comment.trim());
317
+ }
318
+ return { address: result.trim(), comments: comments.filter(text => text) };
319
+ }
320
+ /**
321
+ * Converts tokens for a single address into an address object
322
+ *
323
+ * @param tokens Tokens object
324
+ * @param depth Current recursion depth for nested group protection
325
+ * @return Address object
326
+ */
327
+ function _handleAddress(tokens, depth) {
328
+ let isGroup = false;
329
+ let state = 'text';
330
+ const addresses = [];
331
+ const data = {
332
+ address: [],
333
+ comment: [],
334
+ group: [],
335
+ text: [],
336
+ textWasQuoted: []
337
+ };
338
+ let insideQuotes = false;
339
+ // Last character of the run each state is currently accumulating. Reading it back off
340
+ // the accumulator with slice(-1) makes the engine flatten the whole growing string on
341
+ // every token, which is quadratic over an address built from many comment-joined atoms
342
+ // (GHSA-prgh-xp8r-p3m5). A run only ever grows by the token appended below, so the
343
+ // character is carried along instead of re-read.
344
+ const lastChars = { address: '', comment: '', group: '', text: '' };
345
+ // Filter out <addresses>, (comments) and regular text
346
+ for (let i = 0, len = tokens.length; i < len; i++) {
347
+ const token = tokens[i];
348
+ const prevToken = i ? tokens[i - 1] : null;
349
+ if (token.type === 'operator') {
350
+ switch (token.value) {
351
+ case '<':
352
+ state = 'address';
353
+ insideQuotes = false;
354
+ break;
355
+ case '(':
356
+ state = 'comment';
357
+ insideQuotes = false;
358
+ break;
359
+ case ':':
360
+ state = 'group';
361
+ isGroup = true;
362
+ insideQuotes = false;
363
+ break;
364
+ case '"':
365
+ insideQuotes = !insideQuotes;
366
+ state = 'text';
367
+ break;
368
+ default:
369
+ state = 'text';
370
+ insideQuotes = false;
371
+ break;
372
+ }
373
+ }
374
+ else if (token.value) {
375
+ // An empty quoted string is dropped by the tokenizer, leaving no text token of its
376
+ // own for textWasQuoted to be recorded on, so the pair of quote operators right in
377
+ // front of this token is all that is left of it and the run it opens carries the
378
+ // quoting instead. Without this '""@example.com' reads as the bare '@example.com',
379
+ // the quotes never go back on, and the value is no longer an addr-spec a trailing
380
+ // comment can be peeled off of. It only ever opens a run: a run that already holds
381
+ // material collected outside the quotes is not a quoted string, whatever follows it
382
+ const prevPrevToken = i > 1 ? tokens[i - 2] : null;
383
+ const opensAfterEmptyQuotedString = prevToken?.type === 'operator' &&
384
+ prevToken.value === '"' &&
385
+ !!prevToken.noBreak &&
386
+ prevPrevToken?.type === 'operator' &&
387
+ prevPrevToken.value === '"';
388
+ if (state === 'address') {
389
+ // Handle unquoted name that includes a "<".
390
+ // Apple Mail truncates everything between an unexpected < and an address.
391
+ token.value = token.value.replace(/^[^<]*<\s*/, '');
392
+ }
393
+ // A comment is folding whitespace. It may sit inside an addr-spec, on either side
394
+ // of the '@', but it cannot join two atoms into one: gluing across it would read
395
+ // 'user@example.com(x)evil.com' as the single domain 'example.comevil.com' and
396
+ // deliver to a domain the sender never named.
397
+ const parts = data[state];
398
+ const joins = prevToken &&
399
+ prevToken.noBreak &&
400
+ parts.length &&
401
+ (prevToken.value !== ')' || lastChars[state] === '@' || token.value.charAt(0) === '@');
402
+ if (joins) {
403
+ data[state][data[state].length - 1] += token.value;
404
+ if (token.value) {
405
+ lastChars[state] = token.value.charAt(token.value.length - 1);
406
+ }
407
+ if (state === 'text' && insideQuotes) {
408
+ data.textWasQuoted[data.textWasQuoted.length - 1] = true;
409
+ }
410
+ }
411
+ else {
412
+ data[state].push(token.value);
413
+ lastChars[state] = token.value.charAt(token.value.length - 1);
414
+ if (state === 'text') {
415
+ data.textWasQuoted.push(insideQuotes || opensAfterEmptyQuotedString);
416
+ }
417
+ }
418
+ }
419
+ }
420
+ // If there is no text but a comment, replace the two
421
+ if (!data.text.length && data.comment.length) {
422
+ data.text = data.comment;
423
+ data.comment = [];
424
+ }
425
+ if (isGroup) {
426
+ // http://tools.ietf.org/html/rfc2822#appendix-A.1.3
427
+ data.text = data.text.join(' ');
428
+ // Parse group members, but flatten any nested groups (RFC 5322 doesn't allow nesting)
429
+ let groupMembers = [];
430
+ if (data.group.length) {
431
+ const parsedGroup = _parseAddressList(data.group.join(','), depth + 1);
432
+ parsedGroup.forEach(member => {
433
+ if (member.group) {
434
+ groupMembers = groupMembers.concat(member.group);
435
+ }
436
+ else {
437
+ groupMembers.push(member);
438
+ }
439
+ });
440
+ }
441
+ addresses.push({
442
+ name: data.text || '',
443
+ group: groupMembers
444
+ });
445
+ }
446
+ else {
447
+ // Comments come out of the angle-addr before anything asks whether one was found, so
448
+ // that brackets holding nothing but a comment read the same as empty ones
449
+ const addressComments = [];
450
+ const addressParts = [];
451
+ for (const part of data.address) {
452
+ if (part.indexOf('(') < 0) {
453
+ addressParts.push(part);
454
+ continue;
455
+ }
456
+ const stripped = _stripAddressComments(part);
457
+ for (const comment of stripped.comments) {
458
+ addressComments.push(comment);
459
+ }
460
+ if (stripped.address) {
461
+ addressParts.push(stripped.address);
462
+ }
463
+ }
464
+ data.address = addressParts;
465
+ // If no address was found, try to detect one from regular text
466
+ if (!data.address.length && data.text.length) {
467
+ for (let i = data.text.length - 1; i >= 0; i--) {
468
+ // Security: Do not extract email addresses from quoted strings.
469
+ // RFC 5321 allows @ inside quoted local-parts like "user@domain"@example.com.
470
+ // Extracting emails from quoted text leads to misrouting vulnerabilities.
471
+ if (!data.textWasQuoted[i] && ADDR_SPEC.test(data.text[i])) {
472
+ data.address = data.text.splice(i, 1);
473
+ data.textWasQuoted.splice(i, 1);
474
+ break;
475
+ }
476
+ }
477
+ // Try a looser regex match if strict match found nothing
478
+ if (!data.address.length) {
479
+ let extracted = false;
480
+ for (let i = data.text.length - 1; i >= 0; i--) {
481
+ // Security: Do not extract email addresses from quoted strings
482
+ if (!data.textWasQuoted[i]) {
483
+ const part = data.text[i];
484
+ let remainder = part;
485
+ const at = _looseAddressStart(part);
486
+ if (at >= 0) {
487
+ LOOSE_TEXT_ADDR.lastIndex = at;
488
+ const match = LOOSE_TEXT_ADDR.exec(part);
489
+ if (match) {
490
+ data.address = [match[0].trim()];
491
+ extracted = true;
492
+ remainder = part.slice(0, at) + ' ' + part.slice(at + match[0].length);
493
+ }
494
+ }
495
+ data.text[i] = remainder.trim();
496
+ if (extracted) {
497
+ break;
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
503
+ // If there's still no text but a comment exists, replace the two
504
+ if (!data.text.length && data.comment.length) {
505
+ data.text = data.comment;
506
+ data.comment = [];
507
+ }
508
+ // Keep only the first address occurrence, push others to regular text
509
+ if (data.address.length > 1) {
510
+ data.text = data.text.concat(data.address.splice(1));
511
+ }
512
+ // An address is only taken from unquoted text, so anything left in the text at this
513
+ // point that still has to serve as the address carries its quoting in this flag
514
+ const addressFromQuotedText = !data.address.length && data.textWasQuoted.some(wasQuoted => wasQuoted);
515
+ // Join values with spaces
516
+ data.text = data.text.join(' ');
517
+ data.address = data.address.join(' ');
518
+ if (addressFromQuotedText && data.text.indexOf('@') >= 0) {
519
+ // A quoted run with no '@' in it is a display name and nothing else, so it stays
520
+ // where it is. Moving it over anyway made a comment the mailbox was read from:
521
+ // '"Display Name" <(a comment)>' handed on 'Display Name' as the address with the
522
+ // comment as the name, and a quoted name holding a ',' or a ';' became an address
523
+ // that reads as two recipients once a consumer writes it back into a header.
524
+ //
525
+ // The mailbox is still sitting in the text, so it moves over here and is quoted
526
+ // before the recovery below rather than after it. Anything else the text holds
527
+ // came along with it: a comment ends the domain but leaves the atoms behind it in
528
+ // the same text, and '"user"@example.com(x)evil.com' was handed on as the address
529
+ // 'user@example.com evil.com', a second domain riding into the envelope recipient
530
+ // on a value that is no addr-spec at all (GHSA-g57g-f23g-4646). Putting the quotes
531
+ // back first is what lets the recovery tell the whitespace an addr-spec may carry
532
+ // from the wreckage trailing one, as only a quoted local part may hold whitespace
533
+ data.address = _quoteLocalPart(data.text);
534
+ data.text = '';
535
+ }
536
+ _recoverAddrSpec(data);
537
+ // a comment names the mailbox only when nothing else does, as one outside the brackets
538
+ if (!data.text && addressComments.length) {
539
+ data.text = addressComments.join(' ');
540
+ }
541
+ const address = {
542
+ address: data.address || data.text || '',
543
+ name: data.text || data.address || ''
544
+ };
545
+ if (address.address === address.name) {
546
+ if (/@/.test(address.address || '')) {
547
+ address.name = '';
548
+ }
549
+ else {
550
+ address.address = '';
551
+ }
552
+ }
553
+ addresses.push(address);
554
+ }
555
+ return addresses;
556
+ }
557
+ /**
558
+ * Creates a Tokenizer object for tokenizing address field strings
559
+ *
560
+ * @constructor
561
+ * @param str Address field string
562
+ */
563
+ class Tokenizer {
564
+ constructor(str) {
565
+ this.str = (str || '').toString();
566
+ this.operatorCurrent = '';
567
+ this.operatorExpecting = '';
568
+ this.node = null;
569
+ this.escaped = false;
570
+ this.inDomainLiteral = false;
571
+ this.afterAt = false;
572
+ this.list = [];
573
+ /**
574
+ * Operator tokens and which tokens are expected to end the sequence
575
+ */
576
+ this.operators = {
577
+ '"': '"',
578
+ '(': ')',
579
+ '<': '>',
580
+ ',': '',
581
+ ':': ';',
582
+ // Semicolons are not a legal delimiter per the RFC2822 grammar other
583
+ // than for terminating a group, but they are also not valid for any
584
+ // other use in this context. Given that some mail clients have
585
+ // historically allowed the semicolon as a delimiter equivalent to the
586
+ // comma in their UI, it makes sense to treat them the same as a comma
587
+ // when used outside of a group.
588
+ ';': ''
589
+ };
590
+ }
591
+ /**
592
+ * Tokenizes the original input string
593
+ *
594
+ * @return An array of operator|text tokens
595
+ */
596
+ tokenize() {
597
+ const list = [];
598
+ for (let i = 0, len = this.str.length; i < len; i++) {
599
+ const chr = this.str.charAt(i);
600
+ const nextChr = i < len - 1 ? this.str.charAt(i + 1) : null;
601
+ this.checkChar(chr, nextChr);
602
+ }
603
+ this.list.forEach(node => {
604
+ node.value = (node.value || '').toString().trim();
605
+ if (node.value) {
606
+ list.push(node);
607
+ }
608
+ });
609
+ return list;
610
+ }
611
+ /**
612
+ * Checks if a character is an operator or text and acts accordingly
613
+ *
614
+ * @param chr Character from the address field
615
+ * @param nextChr Character following chr, null at the end of the field
616
+ */
617
+ checkChar(chr, nextChr) {
618
+ // Track RFC 5322 domain-literals ("[" *dtext "]"). The ":" of an IPv6
619
+ // address-literal (user@[IPv6:2001:db8::1]) is dtext and must not be treated as
620
+ // the group delimiter while inside the brackets. That is the only operator the
621
+ // literal hides: letting it hide the others as well meant a "[" could turn the
622
+ // comment, quoted string or angle-addr after it into text and pick a different
623
+ // mailbox, '[ ( ] <a@victim.com> ) <b@evil.com>' yielded a@victim.com where
624
+ // RFC 5322 reads b@evil.com. A "[" also only opens a literal right after the "@"
625
+ // of an addr-spec, so the group delimiter of a display name holding one stays
626
+ // intact. Quoted strings and comments are handled separately via
627
+ // operatorExpecting, so only enter this state when no operator is open. The list
628
+ // separators "," and ";" always end the literal (and split the address list) so
629
+ // that an unclosed "[" cannot swallow later recipients.
630
+ if (!this.escaped && !this.operatorExpecting) {
631
+ if (!this.inDomainLiteral && chr === '[' && this.afterAt) {
632
+ this.inDomainLiteral = true;
633
+ }
634
+ else if (this.inDomainLiteral && (chr === ']' || chr === ',' || chr === ';')) {
635
+ this.inDomainLiteral = false;
636
+ }
637
+ }
638
+ if (this.escaped) {
639
+ // ignore next condition blocks
640
+ }
641
+ else if (chr === this.operatorExpecting) {
642
+ this.node = {
643
+ type: 'operator',
644
+ value: chr
645
+ };
646
+ if (nextChr && ![' ', '\t', '\r', '\n', ',', ';'].includes(nextChr)) {
647
+ this.node.noBreak = true;
648
+ }
649
+ this.list.push(this.node);
650
+ this.node = null;
651
+ if (chr !== ')') {
652
+ // a comment is folding whitespace, it does not part the "@" from the "["
653
+ this.afterAt = false;
654
+ }
655
+ this.operatorExpecting = '';
656
+ this.escaped = false;
657
+ return;
658
+ }
659
+ else if (!this.operatorExpecting && !(this.inDomainLiteral && chr === ':') && chr in this.operators) {
660
+ this.node = {
661
+ type: 'operator',
662
+ value: chr
663
+ };
664
+ this.list.push(this.node);
665
+ this.node = null;
666
+ if (chr !== '(') {
667
+ this.afterAt = false;
668
+ }
669
+ this.operatorExpecting = this.operators[chr];
670
+ this.escaped = false;
671
+ return;
672
+ }
673
+ else if (['"', "'"].includes(this.operatorExpecting) && chr === '\\') {
674
+ this.escaped = true;
675
+ return;
676
+ }
677
+ if (!this.node) {
678
+ this.node = {
679
+ type: 'text',
680
+ value: ''
681
+ };
682
+ this.list.push(this.node);
683
+ }
684
+ if (chr === '\n') {
685
+ // Convert newlines to spaces. Carriage return is ignored as \r and \n usually
686
+ // go together anyway and there already is a WS for \n. Lone \r means something is fishy.
687
+ chr = ' ';
688
+ }
689
+ if (chr.charCodeAt(0) >= 0x21 || [' ', '\t'].includes(chr)) {
690
+ // skip command bytes
691
+ this.node.value += chr;
692
+ // text inside a quoted string or a comment is not part of an addr-spec. Tracked
693
+ // as it goes rather than read back off the value (see lastChars in _handleAddress)
694
+ if (!this.operatorExpecting && chr !== ' ' && chr !== '\t') {
695
+ this.afterAt = chr === '@';
696
+ }
697
+ }
698
+ this.escaped = false;
699
+ }
700
+ }
701
+ /**
702
+ * Maximum recursion depth for parsing nested groups.
703
+ * RFC 5322 doesn't allow nested groups, so this is a safeguard against
704
+ * malicious input that could cause stack overflow.
705
+ */
706
+ const MAX_NESTED_GROUP_DEPTH = 50;
707
+ /**
708
+ * Parses an address list, recursing into the groups it holds.
709
+ *
710
+ * The depth is threaded through the calls rather than carried in an options object, so a
711
+ * caller supplied one can not seed it and lift the recursion limit: `{ _depth: -1e9 }`
712
+ * bought a billion levels of nesting and turned the guard below into the stack overflow it
713
+ * is there to prevent.
714
+ *
715
+ * @param str Address field
716
+ * @param depth Current recursion depth for nested group protection
717
+ * @return An array of address objects
718
+ */
719
+ function _parseAddressList(str, depth) {
720
+ // Prevent stack overflow from deeply nested groups (DoS protection)
721
+ if (depth > MAX_NESTED_GROUP_DEPTH) {
722
+ return [];
723
+ }
724
+ const tokenizer = new Tokenizer(str);
725
+ const tokens = tokenizer.tokenize();
726
+ const addresses = [];
727
+ let address = [];
728
+ const parsedAddresses = [];
729
+ tokens.forEach(token => {
730
+ if (token.type === 'operator' && (token.value === ',' || token.value === ';')) {
731
+ if (address.length) {
732
+ addresses.push(address);
733
+ }
734
+ address = [];
735
+ }
736
+ else {
737
+ address.push(token);
738
+ }
739
+ });
740
+ if (address.length) {
741
+ addresses.push(address);
742
+ }
743
+ addresses.forEach(addr => {
744
+ const handled = _handleAddress(addr, depth);
745
+ // Appended in place. Rebuilding the accumulator with concat() would copy every
746
+ // entry collected so far on each address, making a flat list cost O(n^2).
747
+ for (let i = 0; i < handled.length; i++) {
748
+ parsedAddresses.push(handled[i]);
749
+ }
750
+ });
751
+ // Merge fragments produced when unquoted display names contain commas.
752
+ // "Joe Foo, PhD <joe@example.com>" is split on the comma into
753
+ // [{name:"Joe Foo", address:""}, {name:"PhD", address:"joe@example.com"}].
754
+ // Recombine: a name-only entry followed by an entry with both name and address.
755
+ // Walked back to front so that a run of fragments folds into one entry in a single
756
+ // pass. Splicing each fragment out of the list instead would cost O(n^2).
757
+ const mergedAddresses = [];
758
+ for (let i = parsedAddresses.length - 1; i >= 0; i--) {
759
+ const current = parsedAddresses[i];
760
+ const next = mergedAddresses.length ? mergedAddresses[mergedAddresses.length - 1] : null;
761
+ if (next && current.address === '' && current.name && !current.group && next.address && next.name) {
762
+ next.name = current.name + ', ' + next.name;
763
+ }
764
+ else {
765
+ mergedAddresses.push(current);
766
+ }
767
+ }
768
+ mergedAddresses.reverse();
769
+ return mergedAddresses;
770
+ }
771
+ function addressparser(str, options) {
772
+ const parsedAddresses = _parseAddressList(str, 0);
773
+ if (options?.flatten) {
774
+ const flatAddresses = [];
775
+ const walkAddressList = (list) => {
776
+ list.forEach(entry => {
777
+ if (entry.group) {
778
+ return walkAddressList(entry.group);
779
+ }
780
+ flatAddresses.push(entry);
781
+ });
782
+ };
783
+ walkAddressList(parsedAddresses);
784
+ return flatAddresses;
785
+ }
786
+ return parsedAddresses;
787
+ }
788
+ exports.default = addressparser;
789
+ module.exports = exports.default;
790
+ Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });