nodemailer 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/lib/addressparser/index.js +68 -83
- package/lib/base64/index.js +6 -5
- package/lib/dkim/index.js +8 -16
- package/lib/dkim/message-parser.js +12 -13
- package/lib/dkim/relaxed-body.js +2 -2
- package/lib/dkim/sign.js +13 -14
- package/lib/errors.js +4 -7
- package/lib/fetch/cookies.js +13 -18
- package/lib/fetch/index.js +12 -15
- package/lib/json-transport/index.js +4 -4
- package/lib/mail-composer/index.js +86 -116
- package/lib/mailer/index.js +26 -26
- package/lib/mailer/mail-message.js +17 -21
- package/lib/mime-funcs/index.js +25 -40
- package/lib/mime-funcs/mime-types.js +7 -11
- package/lib/mime-node/index.js +81 -72
- package/lib/mime-node/last-newline.js +1 -1
- package/lib/mime-node/le-unix.js +4 -7
- package/lib/mime-node/le-windows.js +1 -4
- package/lib/nodemailer.js +11 -18
- package/lib/qp/index.js +24 -21
- package/lib/sendmail-transport/index.js +30 -41
- package/lib/ses-transport/index.js +23 -34
- package/lib/shared/index.js +94 -140
- package/lib/smtp-connection/data-stream.js +3 -6
- package/lib/smtp-connection/http-proxy-client.js +11 -13
- package/lib/smtp-connection/index.js +118 -182
- package/lib/smtp-pool/index.js +20 -32
- package/lib/smtp-pool/pool-resource.js +8 -12
- package/lib/smtp-transport/index.js +22 -42
- package/lib/stream-transport/index.js +7 -7
- package/lib/well-known/index.js +7 -7
- package/lib/xoauth2/index.js +22 -28
- package/package.json +4 -3
|
@@ -11,12 +11,10 @@ class MailMessage {
|
|
|
11
11
|
this.message = null;
|
|
12
12
|
|
|
13
13
|
data = data || {};
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const options = mailer.options || {};
|
|
15
|
+
const defaults = mailer._defaults || {};
|
|
16
16
|
|
|
17
|
-
Object.
|
|
18
|
-
this.data[key] = data[key];
|
|
19
|
-
});
|
|
17
|
+
Object.assign(this.data, data);
|
|
20
18
|
|
|
21
19
|
this.data.headers = this.data.headers || {};
|
|
22
20
|
|
|
@@ -47,7 +45,7 @@ class MailMessage {
|
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
resolveAll(callback) {
|
|
50
|
-
|
|
48
|
+
const keys = [
|
|
51
49
|
[this.data, 'html'],
|
|
52
50
|
[this.data, 'text'],
|
|
53
51
|
[this.data, 'watchHtml'],
|
|
@@ -79,9 +77,9 @@ class MailMessage {
|
|
|
79
77
|
});
|
|
80
78
|
}
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
const mimeNode = new MimeNode();
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
const addressKeys = ['from', 'to', 'cc', 'bcc', 'sender', 'replyTo'];
|
|
85
83
|
|
|
86
84
|
addressKeys.forEach(address => {
|
|
87
85
|
let value;
|
|
@@ -97,7 +95,7 @@ class MailMessage {
|
|
|
97
95
|
}
|
|
98
96
|
});
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
const singleKeys = ['from', 'sender'];
|
|
101
99
|
singleKeys.forEach(address => {
|
|
102
100
|
if (this.data[address]) {
|
|
103
101
|
this.data[address] = this.data[address].shift();
|
|
@@ -105,11 +103,11 @@ class MailMessage {
|
|
|
105
103
|
});
|
|
106
104
|
|
|
107
105
|
let pos = 0;
|
|
108
|
-
|
|
106
|
+
const resolveNext = () => {
|
|
109
107
|
if (pos >= keys.length) {
|
|
110
108
|
return callback(null, this.data);
|
|
111
109
|
}
|
|
112
|
-
|
|
110
|
+
const args = keys[pos++];
|
|
113
111
|
if (!args[0] || !args[0][args[1]]) {
|
|
114
112
|
return resolveNext();
|
|
115
113
|
}
|
|
@@ -118,7 +116,7 @@ class MailMessage {
|
|
|
118
116
|
return callback(err);
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
|
|
119
|
+
const node = {
|
|
122
120
|
content: value
|
|
123
121
|
};
|
|
124
122
|
if (args[0][args[1]] && typeof args[0][args[1]] === 'object' && !Buffer.isBuffer(args[0][args[1]])) {
|
|
@@ -138,8 +136,8 @@ class MailMessage {
|
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
normalize(callback) {
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
const envelope = this.data.envelope || this.message.getEnvelope();
|
|
140
|
+
const messageId = this.message.messageId();
|
|
143
141
|
|
|
144
142
|
this.resolveAll((err, data) => {
|
|
145
143
|
if (err) {
|
|
@@ -195,7 +193,7 @@ class MailMessage {
|
|
|
195
193
|
});
|
|
196
194
|
|
|
197
195
|
if (data.list && typeof data.list === 'object') {
|
|
198
|
-
|
|
196
|
+
const listHeaders = this._getListHeaders(data.list);
|
|
199
197
|
listHeaders.forEach(entry => {
|
|
200
198
|
data.normalizedHeaders[entry.key] = entry.value.map(val => (val && val.value) || val).join(', ');
|
|
201
199
|
});
|
|
@@ -245,13 +243,11 @@ class MailMessage {
|
|
|
245
243
|
return;
|
|
246
244
|
}
|
|
247
245
|
// add optional List-* headers
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.message.addHeader(listHeader.key, value);
|
|
252
|
-
});
|
|
246
|
+
this._getListHeaders(this.data.list).forEach(listHeader => {
|
|
247
|
+
listHeader.value.forEach(value => {
|
|
248
|
+
this.message.addHeader(listHeader.key, value);
|
|
253
249
|
});
|
|
254
|
-
}
|
|
250
|
+
});
|
|
255
251
|
}
|
|
256
252
|
|
|
257
253
|
_getListHeaders(listData) {
|
package/lib/mime-funcs/index.js
CHANGED
|
@@ -15,11 +15,7 @@ module.exports = {
|
|
|
15
15
|
*/
|
|
16
16
|
isPlainText(value, isParam) {
|
|
17
17
|
const re = isParam ? /[\x00-\x08\x0b\x0c\x0e-\x1f"\u0080-\uFFFF]/ : /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/;
|
|
18
|
-
|
|
19
|
-
return false;
|
|
20
|
-
} else {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
18
|
+
return typeof value === 'string' && !re.test(value);
|
|
23
19
|
},
|
|
24
20
|
|
|
25
21
|
/**
|
|
@@ -54,7 +50,7 @@ module.exports = {
|
|
|
54
50
|
maxLength = maxLength || 0;
|
|
55
51
|
|
|
56
52
|
let encodedStr;
|
|
57
|
-
|
|
53
|
+
const toCharset = 'UTF-8';
|
|
58
54
|
|
|
59
55
|
if (maxLength && maxLength > 7 + toCharset.length) {
|
|
60
56
|
maxLength -= 7 + toCharset.length;
|
|
@@ -63,12 +59,11 @@ module.exports = {
|
|
|
63
59
|
if (mimeWordEncoding === 'Q') {
|
|
64
60
|
// https://tools.ietf.org/html/rfc2047#section-5 rule (3)
|
|
65
61
|
encodedStr = qp.encode(data).replace(/[^a-z0-9!*+\-/=]/gi, chr => {
|
|
66
|
-
|
|
62
|
+
const ord = chr.charCodeAt(0).toString(16).toUpperCase();
|
|
67
63
|
if (chr === ' ') {
|
|
68
64
|
return '_';
|
|
69
|
-
} else {
|
|
70
|
-
return '=' + (ord.length === 1 ? '0' + ord : ord);
|
|
71
65
|
}
|
|
66
|
+
return '=' + (ord.length === 1 ? '0' + ord : ord);
|
|
72
67
|
});
|
|
73
68
|
} else if (mimeWordEncoding === 'B') {
|
|
74
69
|
encodedStr = typeof data === 'string' ? data : base64.encode(data);
|
|
@@ -80,7 +75,7 @@ module.exports = {
|
|
|
80
75
|
encodedStr = this.splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
|
|
81
76
|
} else {
|
|
82
77
|
// RFC2047 6.3 (2) states that encoded-word must include an integral number of characters, so no chopping unicode sequences
|
|
83
|
-
|
|
78
|
+
const parts = [];
|
|
84
79
|
let lpart = '';
|
|
85
80
|
for (let i = 0, len = encodedStr.length; i < len; i++) {
|
|
86
81
|
let chr = encodedStr.charAt(i);
|
|
@@ -129,42 +124,38 @@ module.exports = {
|
|
|
129
124
|
encodeWords(value, mimeWordEncoding, maxLength, encodeAll) {
|
|
130
125
|
maxLength = maxLength || 0;
|
|
131
126
|
|
|
132
|
-
let encodedValue;
|
|
133
|
-
|
|
134
127
|
// find first word with a non-printable ascii or special symbol in it
|
|
135
|
-
|
|
128
|
+
const firstMatch = value.match(/(?:^|\s)([^\s]*["\u0080-\uFFFF])/);
|
|
136
129
|
if (!firstMatch) {
|
|
137
130
|
return value;
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
if (encodeAll) {
|
|
141
134
|
// if it is requested to encode everything or the string contains something that resebles encoded word, then encode everything
|
|
142
|
-
|
|
143
135
|
return this.encodeWord(value, mimeWordEncoding, maxLength);
|
|
144
136
|
}
|
|
145
137
|
|
|
146
138
|
// find the last word with a non-printable ascii in it
|
|
147
|
-
|
|
139
|
+
const lastMatch = value.match(/(["\u0080-\uFFFF][^\s]*)[^"\u0080-\uFFFF]*$/);
|
|
148
140
|
if (!lastMatch) {
|
|
149
141
|
// should not happen
|
|
150
142
|
return value;
|
|
151
143
|
}
|
|
152
144
|
|
|
153
|
-
|
|
145
|
+
const startIndex =
|
|
154
146
|
firstMatch.index +
|
|
155
147
|
(
|
|
156
148
|
firstMatch[0].match(/[^\s]/) || {
|
|
157
149
|
index: 0
|
|
158
150
|
}
|
|
159
151
|
).index;
|
|
160
|
-
|
|
152
|
+
const endIndex = lastMatch.index + (lastMatch[1] || '').length;
|
|
161
153
|
|
|
162
|
-
|
|
154
|
+
return (
|
|
163
155
|
(startIndex ? value.substr(0, startIndex) : '') +
|
|
164
156
|
this.encodeWord(value.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLength) +
|
|
165
|
-
(endIndex < value.length ? value.substr(endIndex) : '')
|
|
166
|
-
|
|
167
|
-
return encodedValue;
|
|
157
|
+
(endIndex < value.length ? value.substr(endIndex) : '')
|
|
158
|
+
);
|
|
168
159
|
},
|
|
169
160
|
|
|
170
161
|
/**
|
|
@@ -175,12 +166,12 @@ module.exports = {
|
|
|
175
166
|
* @return {String} joined header value
|
|
176
167
|
*/
|
|
177
168
|
buildHeaderValue(structured) {
|
|
178
|
-
|
|
169
|
+
const paramsArray = [];
|
|
179
170
|
|
|
180
171
|
Object.keys(structured.params || {}).forEach(param => {
|
|
181
172
|
// filename might include unicode characters so it is a special case
|
|
182
173
|
// other values probably do not
|
|
183
|
-
|
|
174
|
+
const value = structured.params[param];
|
|
184
175
|
if (!this.isPlainText(value, true) || value.length >= 75) {
|
|
185
176
|
this.buildHeaderParam(param, value, 50).forEach(encodedParam => {
|
|
186
177
|
if (!/[\s"\\;:/=(),<>@[\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {
|
|
@@ -215,9 +206,8 @@ module.exports = {
|
|
|
215
206
|
* @return {Array} A list of encoded keys and headers
|
|
216
207
|
*/
|
|
217
208
|
buildHeaderParam(key, data, maxLength) {
|
|
218
|
-
|
|
209
|
+
const list = [];
|
|
219
210
|
let encodedStr = typeof data === 'string' ? data : (data || '').toString();
|
|
220
|
-
let encodedStrArr;
|
|
221
211
|
let chr, ord;
|
|
222
212
|
let line;
|
|
223
213
|
let startPos = 0;
|
|
@@ -252,7 +242,7 @@ module.exports = {
|
|
|
252
242
|
} else {
|
|
253
243
|
if (/[\uD800-\uDBFF]/.test(encodedStr)) {
|
|
254
244
|
// string containts surrogate pairs, so normalize it to an array of bytes
|
|
255
|
-
encodedStrArr = [];
|
|
245
|
+
const encodedStrArr = [];
|
|
256
246
|
for (i = 0, len = encodedStr.length; i < len; i++) {
|
|
257
247
|
chr = encodedStr.charAt(i);
|
|
258
248
|
ord = chr.charCodeAt(0);
|
|
@@ -356,7 +346,7 @@ module.exports = {
|
|
|
356
346
|
* @return {Object} Header value as a parsed structure
|
|
357
347
|
*/
|
|
358
348
|
parseHeaderValue(str) {
|
|
359
|
-
|
|
349
|
+
const response = {
|
|
360
350
|
value: false,
|
|
361
351
|
params: {}
|
|
362
352
|
};
|
|
@@ -458,12 +448,11 @@ module.exports = {
|
|
|
458
448
|
value
|
|
459
449
|
// fix invalidly encoded chars
|
|
460
450
|
.replace(/[=?_\s]/g, s => {
|
|
461
|
-
|
|
451
|
+
const c = s.charCodeAt(0).toString(16);
|
|
462
452
|
if (s === ' ') {
|
|
463
453
|
return '_';
|
|
464
|
-
} else {
|
|
465
|
-
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
466
454
|
}
|
|
455
|
+
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
467
456
|
})
|
|
468
457
|
// change from urlencoding to percent encoding
|
|
469
458
|
.replace(/%/g, '=') +
|
|
@@ -508,11 +497,10 @@ module.exports = {
|
|
|
508
497
|
str = (str || '').toString();
|
|
509
498
|
lineLength = lineLength || 76;
|
|
510
499
|
|
|
511
|
-
let pos = 0
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
match;
|
|
500
|
+
let pos = 0;
|
|
501
|
+
const len = str.length;
|
|
502
|
+
let result = '';
|
|
503
|
+
let line, match;
|
|
516
504
|
|
|
517
505
|
while (pos < len) {
|
|
518
506
|
line = str.substr(pos, lineLength);
|
|
@@ -549,11 +537,8 @@ module.exports = {
|
|
|
549
537
|
* @return {Array} Split string
|
|
550
538
|
*/
|
|
551
539
|
splitMimeEncodedString: (str, maxlen) => {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
chr,
|
|
555
|
-
done,
|
|
556
|
-
lines = [];
|
|
540
|
+
const lines = [];
|
|
541
|
+
let curLine, match, chr, done;
|
|
557
542
|
|
|
558
543
|
// require at least 12 symbols to fit possible 4 octet UTF-8 sequences
|
|
559
544
|
maxlen = Math.max(maxlen || 0, 12);
|
|
@@ -2073,13 +2073,9 @@ module.exports = {
|
|
|
2073
2073
|
return defaultMimeType;
|
|
2074
2074
|
}
|
|
2075
2075
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
if (extensions.has(extension)) {
|
|
2081
|
-
value = extensions.get(extension);
|
|
2082
|
-
}
|
|
2076
|
+
const parsed = path.parse(filename);
|
|
2077
|
+
const extension = (parsed.ext.substr(1) || parsed.name || '').split('?').shift().trim().toLowerCase();
|
|
2078
|
+
const value = extensions.has(extension) ? extensions.get(extension) : defaultMimeType;
|
|
2083
2079
|
|
|
2084
2080
|
if (Array.isArray(value)) {
|
|
2085
2081
|
return value[0];
|
|
@@ -2091,12 +2087,12 @@ module.exports = {
|
|
|
2091
2087
|
if (!mimeType) {
|
|
2092
2088
|
return defaultExtension;
|
|
2093
2089
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2090
|
+
const parts = (mimeType || '').toLowerCase().trim().split('/');
|
|
2091
|
+
const rootType = parts.shift().trim();
|
|
2092
|
+
const subType = parts.join('/').trim();
|
|
2097
2093
|
|
|
2098
2094
|
if (mimeTypes.has(rootType + '/' + subType)) {
|
|
2099
|
-
|
|
2095
|
+
const value = mimeTypes.get(rootType + '/' + subType);
|
|
2100
2096
|
if (Array.isArray(value)) {
|
|
2101
2097
|
return value[0];
|
|
2102
2098
|
}
|