@depup/nodemailer 7.0.12-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +6 -0
- package/.ncurc.js +9 -0
- package/.prettierignore +8 -0
- package/.prettierrc +12 -0
- package/.prettierrc.js +10 -0
- package/.release-please-config.json +9 -0
- package/CHANGELOG.md +929 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/LICENSE +16 -0
- package/README.md +86 -0
- package/SECURITY.txt +22 -0
- package/eslint.config.js +88 -0
- package/lib/addressparser/index.js +383 -0
- package/lib/base64/index.js +139 -0
- package/lib/dkim/index.js +253 -0
- package/lib/dkim/message-parser.js +155 -0
- package/lib/dkim/relaxed-body.js +154 -0
- package/lib/dkim/sign.js +117 -0
- package/lib/fetch/cookies.js +281 -0
- package/lib/fetch/index.js +280 -0
- package/lib/json-transport/index.js +82 -0
- package/lib/mail-composer/index.js +629 -0
- package/lib/mailer/index.js +441 -0
- package/lib/mailer/mail-message.js +316 -0
- package/lib/mime-funcs/index.js +625 -0
- package/lib/mime-funcs/mime-types.js +2113 -0
- package/lib/mime-node/index.js +1316 -0
- package/lib/mime-node/last-newline.js +33 -0
- package/lib/mime-node/le-unix.js +43 -0
- package/lib/mime-node/le-windows.js +52 -0
- package/lib/nodemailer.js +157 -0
- package/lib/punycode/index.js +460 -0
- package/lib/qp/index.js +227 -0
- package/lib/sendmail-transport/index.js +210 -0
- package/lib/ses-transport/index.js +234 -0
- package/lib/shared/index.js +754 -0
- package/lib/smtp-connection/data-stream.js +108 -0
- package/lib/smtp-connection/http-proxy-client.js +143 -0
- package/lib/smtp-connection/index.js +1865 -0
- package/lib/smtp-pool/index.js +652 -0
- package/lib/smtp-pool/pool-resource.js +259 -0
- package/lib/smtp-transport/index.js +421 -0
- package/lib/stream-transport/index.js +135 -0
- package/lib/well-known/index.js +47 -0
- package/lib/well-known/services.json +611 -0
- package/lib/xoauth2/index.js +427 -0
- package/package.json +47 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const spawn = require('child_process').spawn;
|
|
4
|
+
const packageData = require('../../package.json');
|
|
5
|
+
const shared = require('../shared');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generates a Transport object for Sendmail
|
|
9
|
+
*
|
|
10
|
+
* Possible options can be the following:
|
|
11
|
+
*
|
|
12
|
+
* * **path** optional path to sendmail binary
|
|
13
|
+
* * **newline** either 'windows' or 'unix'
|
|
14
|
+
* * **args** an array of arguments for the sendmail binary
|
|
15
|
+
*
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param {Object} optional config parameter for Sendmail
|
|
18
|
+
*/
|
|
19
|
+
class SendmailTransport {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
options = options || {};
|
|
22
|
+
|
|
23
|
+
// use a reference to spawn for mocking purposes
|
|
24
|
+
this._spawn = spawn;
|
|
25
|
+
|
|
26
|
+
this.options = options || {};
|
|
27
|
+
|
|
28
|
+
this.name = 'Sendmail';
|
|
29
|
+
this.version = packageData.version;
|
|
30
|
+
|
|
31
|
+
this.path = 'sendmail';
|
|
32
|
+
this.args = false;
|
|
33
|
+
this.winbreak = false;
|
|
34
|
+
|
|
35
|
+
this.logger = shared.getLogger(this.options, {
|
|
36
|
+
component: this.options.component || 'sendmail'
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (options) {
|
|
40
|
+
if (typeof options === 'string') {
|
|
41
|
+
this.path = options;
|
|
42
|
+
} else if (typeof options === 'object') {
|
|
43
|
+
if (options.path) {
|
|
44
|
+
this.path = options.path;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(options.args)) {
|
|
47
|
+
this.args = options.args;
|
|
48
|
+
}
|
|
49
|
+
this.winbreak = ['win', 'windows', 'dos', '\r\n'].includes((options.newline || '').toString().toLowerCase());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* <p>Compiles a mailcomposer message and forwards it to handler that sends it.</p>
|
|
56
|
+
*
|
|
57
|
+
* @param {Object} emailMessage MailComposer object
|
|
58
|
+
* @param {Function} callback Callback function to run when the sending is completed
|
|
59
|
+
*/
|
|
60
|
+
send(mail, done) {
|
|
61
|
+
// Sendmail strips this header line by itself
|
|
62
|
+
mail.message.keepBcc = true;
|
|
63
|
+
|
|
64
|
+
let envelope = mail.data.envelope || mail.message.getEnvelope();
|
|
65
|
+
let messageId = mail.message.messageId();
|
|
66
|
+
let args;
|
|
67
|
+
let sendmail;
|
|
68
|
+
let returned;
|
|
69
|
+
|
|
70
|
+
const hasInvalidAddresses = []
|
|
71
|
+
.concat(envelope.from || [])
|
|
72
|
+
.concat(envelope.to || [])
|
|
73
|
+
.some(addr => /^-/.test(addr));
|
|
74
|
+
if (hasInvalidAddresses) {
|
|
75
|
+
return done(new Error('Can not send mail. Invalid envelope addresses.'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.args) {
|
|
79
|
+
// force -i to keep single dots
|
|
80
|
+
args = ['-i'].concat(this.args).concat(envelope.to);
|
|
81
|
+
} else {
|
|
82
|
+
args = ['-i'].concat(envelope.from ? ['-f', envelope.from] : []).concat(envelope.to);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let callback = err => {
|
|
86
|
+
if (returned) {
|
|
87
|
+
// ignore any additional responses, already done
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
returned = true;
|
|
91
|
+
if (typeof done === 'function') {
|
|
92
|
+
if (err) {
|
|
93
|
+
return done(err);
|
|
94
|
+
} else {
|
|
95
|
+
return done(null, {
|
|
96
|
+
envelope: mail.data.envelope || mail.message.getEnvelope(),
|
|
97
|
+
messageId,
|
|
98
|
+
response: 'Messages queued for delivery'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
sendmail = this._spawn(this.path, args);
|
|
106
|
+
} catch (E) {
|
|
107
|
+
this.logger.error(
|
|
108
|
+
{
|
|
109
|
+
err: E,
|
|
110
|
+
tnx: 'spawn',
|
|
111
|
+
messageId
|
|
112
|
+
},
|
|
113
|
+
'Error occurred while spawning sendmail. %s',
|
|
114
|
+
E.message
|
|
115
|
+
);
|
|
116
|
+
return callback(E);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (sendmail) {
|
|
120
|
+
sendmail.on('error', err => {
|
|
121
|
+
this.logger.error(
|
|
122
|
+
{
|
|
123
|
+
err,
|
|
124
|
+
tnx: 'spawn',
|
|
125
|
+
messageId
|
|
126
|
+
},
|
|
127
|
+
'Error occurred when sending message %s. %s',
|
|
128
|
+
messageId,
|
|
129
|
+
err.message
|
|
130
|
+
);
|
|
131
|
+
callback(err);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
sendmail.once('exit', code => {
|
|
135
|
+
if (!code) {
|
|
136
|
+
return callback();
|
|
137
|
+
}
|
|
138
|
+
let err;
|
|
139
|
+
if (code === 127) {
|
|
140
|
+
err = new Error('Sendmail command not found, process exited with code ' + code);
|
|
141
|
+
} else {
|
|
142
|
+
err = new Error('Sendmail exited with code ' + code);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.logger.error(
|
|
146
|
+
{
|
|
147
|
+
err,
|
|
148
|
+
tnx: 'stdin',
|
|
149
|
+
messageId
|
|
150
|
+
},
|
|
151
|
+
'Error sending message %s to sendmail. %s',
|
|
152
|
+
messageId,
|
|
153
|
+
err.message
|
|
154
|
+
);
|
|
155
|
+
callback(err);
|
|
156
|
+
});
|
|
157
|
+
sendmail.once('close', callback);
|
|
158
|
+
|
|
159
|
+
sendmail.stdin.on('error', err => {
|
|
160
|
+
this.logger.error(
|
|
161
|
+
{
|
|
162
|
+
err,
|
|
163
|
+
tnx: 'stdin',
|
|
164
|
+
messageId
|
|
165
|
+
},
|
|
166
|
+
'Error occurred when piping message %s to sendmail. %s',
|
|
167
|
+
messageId,
|
|
168
|
+
err.message
|
|
169
|
+
);
|
|
170
|
+
callback(err);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
let recipients = [].concat(envelope.to || []);
|
|
174
|
+
if (recipients.length > 3) {
|
|
175
|
+
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
176
|
+
}
|
|
177
|
+
this.logger.info(
|
|
178
|
+
{
|
|
179
|
+
tnx: 'send',
|
|
180
|
+
messageId
|
|
181
|
+
},
|
|
182
|
+
'Sending message %s to <%s>',
|
|
183
|
+
messageId,
|
|
184
|
+
recipients.join(', ')
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
let sourceStream = mail.message.createReadStream();
|
|
188
|
+
sourceStream.once('error', err => {
|
|
189
|
+
this.logger.error(
|
|
190
|
+
{
|
|
191
|
+
err,
|
|
192
|
+
tnx: 'stdin',
|
|
193
|
+
messageId
|
|
194
|
+
},
|
|
195
|
+
'Error occurred when generating message %s. %s',
|
|
196
|
+
messageId,
|
|
197
|
+
err.message
|
|
198
|
+
);
|
|
199
|
+
sendmail.kill('SIGINT'); // do not deliver the message
|
|
200
|
+
callback(err);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
sourceStream.pipe(sendmail.stdin);
|
|
204
|
+
} else {
|
|
205
|
+
return callback(new Error('sendmail was not found'));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = SendmailTransport;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const packageData = require('../../package.json');
|
|
5
|
+
const shared = require('../shared');
|
|
6
|
+
const LeWindows = require('../mime-node/le-windows');
|
|
7
|
+
const MimeNode = require('../mime-node');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates a Transport object for AWS SES
|
|
11
|
+
*
|
|
12
|
+
* @constructor
|
|
13
|
+
* @param {Object} optional config parameter
|
|
14
|
+
*/
|
|
15
|
+
class SESTransport extends EventEmitter {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
super();
|
|
18
|
+
options = options || {};
|
|
19
|
+
|
|
20
|
+
this.options = options || {};
|
|
21
|
+
this.ses = this.options.SES;
|
|
22
|
+
|
|
23
|
+
this.name = 'SESTransport';
|
|
24
|
+
this.version = packageData.version;
|
|
25
|
+
|
|
26
|
+
this.logger = shared.getLogger(this.options, {
|
|
27
|
+
component: this.options.component || 'ses-transport'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getRegion(cb) {
|
|
32
|
+
if (this.ses.sesClient.config && typeof this.ses.sesClient.config.region === 'function') {
|
|
33
|
+
// promise
|
|
34
|
+
return this.ses.sesClient.config
|
|
35
|
+
.region()
|
|
36
|
+
.then(region => cb(null, region))
|
|
37
|
+
.catch(err => cb(err));
|
|
38
|
+
}
|
|
39
|
+
return cb(null, false);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Compiles a mailcomposer message and forwards it to SES
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} emailMessage MailComposer object
|
|
46
|
+
* @param {Function} callback Callback function to run when the sending is completed
|
|
47
|
+
*/
|
|
48
|
+
send(mail, callback) {
|
|
49
|
+
let statObject = {
|
|
50
|
+
ts: Date.now(),
|
|
51
|
+
pending: true
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let fromHeader = mail.message._headers.find(header => /^from$/i.test(header.key));
|
|
55
|
+
if (fromHeader) {
|
|
56
|
+
let mimeNode = new MimeNode('text/plain');
|
|
57
|
+
fromHeader = mimeNode._convertAddresses(mimeNode._parseAddresses(fromHeader.value));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let envelope = mail.data.envelope || mail.message.getEnvelope();
|
|
61
|
+
let messageId = mail.message.messageId();
|
|
62
|
+
|
|
63
|
+
let recipients = [].concat(envelope.to || []);
|
|
64
|
+
if (recipients.length > 3) {
|
|
65
|
+
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
66
|
+
}
|
|
67
|
+
this.logger.info(
|
|
68
|
+
{
|
|
69
|
+
tnx: 'send',
|
|
70
|
+
messageId
|
|
71
|
+
},
|
|
72
|
+
'Sending message %s to <%s>',
|
|
73
|
+
messageId,
|
|
74
|
+
recipients.join(', ')
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
let getRawMessage = next => {
|
|
78
|
+
// do not use Message-ID and Date in DKIM signature
|
|
79
|
+
if (!mail.data._dkim) {
|
|
80
|
+
mail.data._dkim = {};
|
|
81
|
+
}
|
|
82
|
+
if (mail.data._dkim.skipFields && typeof mail.data._dkim.skipFields === 'string') {
|
|
83
|
+
mail.data._dkim.skipFields += ':date:message-id';
|
|
84
|
+
} else {
|
|
85
|
+
mail.data._dkim.skipFields = 'date:message-id';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let sourceStream = mail.message.createReadStream();
|
|
89
|
+
let stream = sourceStream.pipe(new LeWindows());
|
|
90
|
+
let chunks = [];
|
|
91
|
+
let chunklen = 0;
|
|
92
|
+
|
|
93
|
+
stream.on('readable', () => {
|
|
94
|
+
let chunk;
|
|
95
|
+
while ((chunk = stream.read()) !== null) {
|
|
96
|
+
chunks.push(chunk);
|
|
97
|
+
chunklen += chunk.length;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
sourceStream.once('error', err => stream.emit('error', err));
|
|
102
|
+
|
|
103
|
+
stream.once('error', err => {
|
|
104
|
+
next(err);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
stream.once('end', () => next(null, Buffer.concat(chunks, chunklen)));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
setImmediate(() =>
|
|
111
|
+
getRawMessage((err, raw) => {
|
|
112
|
+
if (err) {
|
|
113
|
+
this.logger.error(
|
|
114
|
+
{
|
|
115
|
+
err,
|
|
116
|
+
tnx: 'send',
|
|
117
|
+
messageId
|
|
118
|
+
},
|
|
119
|
+
'Failed creating message for %s. %s',
|
|
120
|
+
messageId,
|
|
121
|
+
err.message
|
|
122
|
+
);
|
|
123
|
+
statObject.pending = false;
|
|
124
|
+
return callback(err);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let sesMessage = {
|
|
128
|
+
Content: {
|
|
129
|
+
Raw: {
|
|
130
|
+
// required
|
|
131
|
+
Data: raw // required
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
FromEmailAddress: fromHeader ? fromHeader : envelope.from,
|
|
135
|
+
Destination: {
|
|
136
|
+
ToAddresses: envelope.to
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
Object.keys(mail.data.ses || {}).forEach(key => {
|
|
141
|
+
sesMessage[key] = mail.data.ses[key];
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this.getRegion((err, region) => {
|
|
145
|
+
if (err || !region) {
|
|
146
|
+
region = 'us-east-1';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const command = new this.ses.SendEmailCommand(sesMessage);
|
|
150
|
+
const sendPromise = this.ses.sesClient.send(command);
|
|
151
|
+
|
|
152
|
+
sendPromise
|
|
153
|
+
.then(data => {
|
|
154
|
+
if (region === 'us-east-1') {
|
|
155
|
+
region = 'email';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
statObject.pending = true;
|
|
159
|
+
callback(null, {
|
|
160
|
+
envelope: {
|
|
161
|
+
from: envelope.from,
|
|
162
|
+
to: envelope.to
|
|
163
|
+
},
|
|
164
|
+
messageId: '<' + data.MessageId + (!/@/.test(data.MessageId) ? '@' + region + '.amazonses.com' : '') + '>',
|
|
165
|
+
response: data.MessageId,
|
|
166
|
+
raw
|
|
167
|
+
});
|
|
168
|
+
})
|
|
169
|
+
.catch(err => {
|
|
170
|
+
this.logger.error(
|
|
171
|
+
{
|
|
172
|
+
err,
|
|
173
|
+
tnx: 'send'
|
|
174
|
+
},
|
|
175
|
+
'Send error for %s: %s',
|
|
176
|
+
messageId,
|
|
177
|
+
err.message
|
|
178
|
+
);
|
|
179
|
+
statObject.pending = false;
|
|
180
|
+
callback(err);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Verifies SES configuration
|
|
189
|
+
*
|
|
190
|
+
* @param {Function} callback Callback function
|
|
191
|
+
*/
|
|
192
|
+
verify(callback) {
|
|
193
|
+
let promise;
|
|
194
|
+
if (!callback) {
|
|
195
|
+
promise = new Promise((resolve, reject) => {
|
|
196
|
+
callback = shared.callbackPromise(resolve, reject);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const cb = err => {
|
|
201
|
+
if (err && !['InvalidParameterValue', 'MessageRejected'].includes(err.code || err.Code || err.name)) {
|
|
202
|
+
return callback(err);
|
|
203
|
+
}
|
|
204
|
+
return callback(null, true);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const sesMessage = {
|
|
208
|
+
Content: {
|
|
209
|
+
Raw: {
|
|
210
|
+
Data: Buffer.from('From: <invalid@invalid>\r\nTo: <invalid@invalid>\r\n Subject: Invalid\r\n\r\nInvalid')
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
FromEmailAddress: 'invalid@invalid',
|
|
214
|
+
Destination: {
|
|
215
|
+
ToAddresses: ['invalid@invalid']
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this.getRegion((err, region) => {
|
|
220
|
+
if (err || !region) {
|
|
221
|
+
region = 'us-east-1';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const command = new this.ses.SendEmailCommand(sesMessage);
|
|
225
|
+
const sendPromise = this.ses.sesClient.send(command);
|
|
226
|
+
|
|
227
|
+
sendPromise.then(data => cb(null, data)).catch(err => cb(err));
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return promise;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = SESTransport;
|