polikolog 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.idea/5lab.iml +12 -0
- package/.idea/inspectionProfiles/Project_Default.xml +10 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/06-02.js +48 -0
- package/06-03.js +22 -0
- package/06-04.js +22 -0
- package/index.html +41 -0
- package/m0603.js +28 -0
- package/mypackage/m0603.js +28 -0
- package/mypackage/node_modules/.package-lock.json +24 -0
- package/mypackage/node_modules/nodemailer/.gitattributes +6 -0
- package/mypackage/node_modules/nodemailer/.prettierrc.js +8 -0
- package/mypackage/node_modules/nodemailer/CHANGELOG.md +725 -0
- package/mypackage/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
- package/mypackage/node_modules/nodemailer/CONTRIBUTING.md +67 -0
- package/mypackage/node_modules/nodemailer/LICENSE +16 -0
- package/mypackage/node_modules/nodemailer/README.md +97 -0
- package/mypackage/node_modules/nodemailer/SECURITY.txt +22 -0
- package/mypackage/node_modules/nodemailer/lib/addressparser/index.js +313 -0
- package/mypackage/node_modules/nodemailer/lib/base64/index.js +142 -0
- package/mypackage/node_modules/nodemailer/lib/dkim/index.js +251 -0
- package/mypackage/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
- package/mypackage/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
- package/mypackage/node_modules/nodemailer/lib/dkim/sign.js +117 -0
- package/mypackage/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
- package/mypackage/node_modules/nodemailer/lib/fetch/index.js +274 -0
- package/mypackage/node_modules/nodemailer/lib/json-transport/index.js +82 -0
- package/mypackage/node_modules/nodemailer/lib/mail-composer/index.js +558 -0
- package/mypackage/node_modules/nodemailer/lib/mailer/index.js +427 -0
- package/mypackage/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
- package/mypackage/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
- package/mypackage/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
- package/mypackage/node_modules/nodemailer/lib/mime-node/index.js +1290 -0
- package/mypackage/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
- package/mypackage/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
- package/mypackage/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
- package/mypackage/node_modules/nodemailer/lib/nodemailer.js +143 -0
- package/mypackage/node_modules/nodemailer/lib/qp/index.js +219 -0
- package/mypackage/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
- package/mypackage/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
- package/mypackage/node_modules/nodemailer/lib/shared/index.js +638 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-connection/index.js +1796 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
- package/mypackage/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
- package/mypackage/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
- package/mypackage/node_modules/nodemailer/lib/well-known/index.js +47 -0
- package/mypackage/node_modules/nodemailer/lib/well-known/services.json +286 -0
- package/mypackage/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
- package/mypackage/node_modules/nodemailer/package.json +46 -0
- package/mypackage/node_modules/nodemailer/postinstall.js +101 -0
- package/mypackage/package.json +15 -0
- package/package.json +15 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const Transform = require('stream').Transform;
|
4
|
+
|
5
|
+
class LastNewline extends Transform {
|
6
|
+
constructor() {
|
7
|
+
super();
|
8
|
+
this.lastByte = false;
|
9
|
+
}
|
10
|
+
|
11
|
+
_transform(chunk, encoding, done) {
|
12
|
+
if (chunk.length) {
|
13
|
+
this.lastByte = chunk[chunk.length - 1];
|
14
|
+
}
|
15
|
+
|
16
|
+
this.push(chunk);
|
17
|
+
done();
|
18
|
+
}
|
19
|
+
|
20
|
+
_flush(done) {
|
21
|
+
if (this.lastByte === 0x0a) {
|
22
|
+
return done();
|
23
|
+
}
|
24
|
+
if (this.lastByte === 0x0d) {
|
25
|
+
this.push(Buffer.from('\n'));
|
26
|
+
return done();
|
27
|
+
}
|
28
|
+
this.push(Buffer.from('\r\n'));
|
29
|
+
return done();
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
module.exports = LastNewline;
|
@@ -0,0 +1,43 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const stream = require('stream');
|
4
|
+
const Transform = stream.Transform;
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Ensures that only <LF> is used for linebreaks
|
8
|
+
*
|
9
|
+
* @param {Object} options Stream options
|
10
|
+
*/
|
11
|
+
class LeWindows extends Transform {
|
12
|
+
constructor(options) {
|
13
|
+
super(options);
|
14
|
+
// init Transform
|
15
|
+
this.options = options || {};
|
16
|
+
}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Escapes dots
|
20
|
+
*/
|
21
|
+
_transform(chunk, encoding, done) {
|
22
|
+
let buf;
|
23
|
+
let lastPos = 0;
|
24
|
+
|
25
|
+
for (let i = 0, len = chunk.length; i < len; i++) {
|
26
|
+
if (chunk[i] === 0x0d) {
|
27
|
+
// \n
|
28
|
+
buf = chunk.slice(lastPos, i);
|
29
|
+
lastPos = i + 1;
|
30
|
+
this.push(buf);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
if (lastPos && lastPos < chunk.length) {
|
34
|
+
buf = chunk.slice(lastPos);
|
35
|
+
this.push(buf);
|
36
|
+
} else if (!lastPos) {
|
37
|
+
this.push(chunk);
|
38
|
+
}
|
39
|
+
done();
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
module.exports = LeWindows;
|
@@ -0,0 +1,52 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const stream = require('stream');
|
4
|
+
const Transform = stream.Transform;
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Ensures that only <CR><LF> sequences are used for linebreaks
|
8
|
+
*
|
9
|
+
* @param {Object} options Stream options
|
10
|
+
*/
|
11
|
+
class LeWindows extends Transform {
|
12
|
+
constructor(options) {
|
13
|
+
super(options);
|
14
|
+
// init Transform
|
15
|
+
this.options = options || {};
|
16
|
+
this.lastByte = false;
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Escapes dots
|
21
|
+
*/
|
22
|
+
_transform(chunk, encoding, done) {
|
23
|
+
let buf;
|
24
|
+
let lastPos = 0;
|
25
|
+
|
26
|
+
for (let i = 0, len = chunk.length; i < len; i++) {
|
27
|
+
if (chunk[i] === 0x0a) {
|
28
|
+
// \n
|
29
|
+
if ((i && chunk[i - 1] !== 0x0d) || (!i && this.lastByte !== 0x0d)) {
|
30
|
+
if (i > lastPos) {
|
31
|
+
buf = chunk.slice(lastPos, i);
|
32
|
+
this.push(buf);
|
33
|
+
}
|
34
|
+
this.push(Buffer.from('\r\n'));
|
35
|
+
lastPos = i + 1;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
if (lastPos && lastPos < chunk.length) {
|
41
|
+
buf = chunk.slice(lastPos);
|
42
|
+
this.push(buf);
|
43
|
+
} else if (!lastPos) {
|
44
|
+
this.push(chunk);
|
45
|
+
}
|
46
|
+
|
47
|
+
this.lastByte = chunk[chunk.length - 1];
|
48
|
+
done();
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
module.exports = LeWindows;
|
@@ -0,0 +1,143 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const Mailer = require('./mailer');
|
4
|
+
const shared = require('./shared');
|
5
|
+
const SMTPPool = require('./smtp-pool');
|
6
|
+
const SMTPTransport = require('./smtp-transport');
|
7
|
+
const SendmailTransport = require('./sendmail-transport');
|
8
|
+
const StreamTransport = require('./stream-transport');
|
9
|
+
const JSONTransport = require('./json-transport');
|
10
|
+
const SESTransport = require('./ses-transport');
|
11
|
+
const nmfetch = require('./fetch');
|
12
|
+
const packageData = require('../package.json');
|
13
|
+
|
14
|
+
const ETHEREAL_API = (process.env.ETHEREAL_API || 'https://api.nodemailer.com').replace(/\/+$/, '');
|
15
|
+
const ETHEREAL_WEB = (process.env.ETHEREAL_WEB || 'https://ethereal.email').replace(/\/+$/, '');
|
16
|
+
const ETHEREAL_CACHE = ['true', 'yes', 'y', '1'].includes((process.env.ETHEREAL_CACHE || 'yes').toString().trim().toLowerCase());
|
17
|
+
|
18
|
+
let testAccount = false;
|
19
|
+
|
20
|
+
module.exports.createTransport = function (transporter, defaults) {
|
21
|
+
let urlConfig;
|
22
|
+
let options;
|
23
|
+
let mailer;
|
24
|
+
|
25
|
+
if (
|
26
|
+
// provided transporter is a configuration object, not transporter plugin
|
27
|
+
(typeof transporter === 'object' && typeof transporter.send !== 'function') ||
|
28
|
+
// provided transporter looks like a connection url
|
29
|
+
(typeof transporter === 'string' && /^(smtps?|direct):/i.test(transporter))
|
30
|
+
) {
|
31
|
+
if ((urlConfig = typeof transporter === 'string' ? transporter : transporter.url)) {
|
32
|
+
// parse a configuration URL into configuration options
|
33
|
+
options = shared.parseConnectionUrl(urlConfig);
|
34
|
+
} else {
|
35
|
+
options = transporter;
|
36
|
+
}
|
37
|
+
|
38
|
+
if (options.pool) {
|
39
|
+
transporter = new SMTPPool(options);
|
40
|
+
} else if (options.sendmail) {
|
41
|
+
transporter = new SendmailTransport(options);
|
42
|
+
} else if (options.streamTransport) {
|
43
|
+
transporter = new StreamTransport(options);
|
44
|
+
} else if (options.jsonTransport) {
|
45
|
+
transporter = new JSONTransport(options);
|
46
|
+
} else if (options.SES) {
|
47
|
+
transporter = new SESTransport(options);
|
48
|
+
} else {
|
49
|
+
transporter = new SMTPTransport(options);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
mailer = new Mailer(transporter, options, defaults);
|
54
|
+
|
55
|
+
return mailer;
|
56
|
+
};
|
57
|
+
|
58
|
+
module.exports.createTestAccount = function (apiUrl, callback) {
|
59
|
+
let promise;
|
60
|
+
|
61
|
+
if (!callback && typeof apiUrl === 'function') {
|
62
|
+
callback = apiUrl;
|
63
|
+
apiUrl = false;
|
64
|
+
}
|
65
|
+
|
66
|
+
if (!callback) {
|
67
|
+
promise = new Promise((resolve, reject) => {
|
68
|
+
callback = shared.callbackPromise(resolve, reject);
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
if (ETHEREAL_CACHE && testAccount) {
|
73
|
+
setImmediate(() => callback(null, testAccount));
|
74
|
+
return promise;
|
75
|
+
}
|
76
|
+
|
77
|
+
apiUrl = apiUrl || ETHEREAL_API;
|
78
|
+
|
79
|
+
let chunks = [];
|
80
|
+
let chunklen = 0;
|
81
|
+
|
82
|
+
let req = nmfetch(apiUrl + '/user', {
|
83
|
+
contentType: 'application/json',
|
84
|
+
method: 'POST',
|
85
|
+
body: Buffer.from(
|
86
|
+
JSON.stringify({
|
87
|
+
requestor: packageData.name,
|
88
|
+
version: packageData.version
|
89
|
+
})
|
90
|
+
)
|
91
|
+
});
|
92
|
+
|
93
|
+
req.on('readable', () => {
|
94
|
+
let chunk;
|
95
|
+
while ((chunk = req.read()) !== null) {
|
96
|
+
chunks.push(chunk);
|
97
|
+
chunklen += chunk.length;
|
98
|
+
}
|
99
|
+
});
|
100
|
+
|
101
|
+
req.once('error', err => callback(err));
|
102
|
+
|
103
|
+
req.once('end', () => {
|
104
|
+
let res = Buffer.concat(chunks, chunklen);
|
105
|
+
let data;
|
106
|
+
let err;
|
107
|
+
try {
|
108
|
+
data = JSON.parse(res.toString());
|
109
|
+
} catch (E) {
|
110
|
+
err = E;
|
111
|
+
}
|
112
|
+
if (err) {
|
113
|
+
return callback(err);
|
114
|
+
}
|
115
|
+
if (data.status !== 'success' || data.error) {
|
116
|
+
return callback(new Error(data.error || 'Request failed'));
|
117
|
+
}
|
118
|
+
delete data.status;
|
119
|
+
testAccount = data;
|
120
|
+
callback(null, testAccount);
|
121
|
+
});
|
122
|
+
|
123
|
+
return promise;
|
124
|
+
};
|
125
|
+
|
126
|
+
module.exports.getTestMessageUrl = function (info) {
|
127
|
+
if (!info || !info.response) {
|
128
|
+
return false;
|
129
|
+
}
|
130
|
+
|
131
|
+
let infoProps = new Map();
|
132
|
+
info.response.replace(/\[([^\]]+)\]$/, (m, props) => {
|
133
|
+
props.replace(/\b([A-Z0-9]+)=([^\s]+)/g, (m, key, value) => {
|
134
|
+
infoProps.set(key, value);
|
135
|
+
});
|
136
|
+
});
|
137
|
+
|
138
|
+
if (infoProps.has('STATUS') && infoProps.has('MSGID')) {
|
139
|
+
return (testAccount.web || ETHEREAL_WEB) + '/message/' + infoProps.get('MSGID');
|
140
|
+
}
|
141
|
+
|
142
|
+
return false;
|
143
|
+
};
|
@@ -0,0 +1,219 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const Transform = require('stream').Transform;
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Encodes a Buffer into a Quoted-Printable encoded string
|
7
|
+
*
|
8
|
+
* @param {Buffer} buffer Buffer to convert
|
9
|
+
* @returns {String} Quoted-Printable encoded string
|
10
|
+
*/
|
11
|
+
function encode(buffer) {
|
12
|
+
if (typeof buffer === 'string') {
|
13
|
+
buffer = Buffer.from(buffer, 'utf-8');
|
14
|
+
}
|
15
|
+
|
16
|
+
// usable characters that do not need encoding
|
17
|
+
let ranges = [
|
18
|
+
// https://tools.ietf.org/html/rfc2045#section-6.7
|
19
|
+
[0x09], // <TAB>
|
20
|
+
[0x0a], // <LF>
|
21
|
+
[0x0d], // <CR>
|
22
|
+
[0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
|
23
|
+
[0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
|
24
|
+
];
|
25
|
+
let result = '';
|
26
|
+
let ord;
|
27
|
+
|
28
|
+
for (let i = 0, len = buffer.length; i < len; i++) {
|
29
|
+
ord = buffer[i];
|
30
|
+
// if the char is in allowed range, then keep as is, unless it is a WS in the end of a line
|
31
|
+
if (checkRanges(ord, ranges) && !((ord === 0x20 || ord === 0x09) && (i === len - 1 || buffer[i + 1] === 0x0a || buffer[i + 1] === 0x0d))) {
|
32
|
+
result += String.fromCharCode(ord);
|
33
|
+
continue;
|
34
|
+
}
|
35
|
+
result += '=' + (ord < 0x10 ? '0' : '') + ord.toString(16).toUpperCase();
|
36
|
+
}
|
37
|
+
|
38
|
+
return result;
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Adds soft line breaks to a Quoted-Printable string
|
43
|
+
*
|
44
|
+
* @param {String} str Quoted-Printable encoded string that might need line wrapping
|
45
|
+
* @param {Number} [lineLength=76] Maximum allowed length for a line
|
46
|
+
* @returns {String} Soft-wrapped Quoted-Printable encoded string
|
47
|
+
*/
|
48
|
+
function wrap(str, lineLength) {
|
49
|
+
str = (str || '').toString();
|
50
|
+
lineLength = lineLength || 76;
|
51
|
+
|
52
|
+
if (str.length <= lineLength) {
|
53
|
+
return str;
|
54
|
+
}
|
55
|
+
|
56
|
+
let pos = 0;
|
57
|
+
let len = str.length;
|
58
|
+
let match, code, line;
|
59
|
+
let lineMargin = Math.floor(lineLength / 3);
|
60
|
+
let result = '';
|
61
|
+
|
62
|
+
// insert soft linebreaks where needed
|
63
|
+
while (pos < len) {
|
64
|
+
line = str.substr(pos, lineLength);
|
65
|
+
if ((match = line.match(/\r\n/))) {
|
66
|
+
line = line.substr(0, match.index + match[0].length);
|
67
|
+
result += line;
|
68
|
+
pos += line.length;
|
69
|
+
continue;
|
70
|
+
}
|
71
|
+
|
72
|
+
if (line.substr(-1) === '\n') {
|
73
|
+
// nothing to change here
|
74
|
+
result += line;
|
75
|
+
pos += line.length;
|
76
|
+
continue;
|
77
|
+
} else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
|
78
|
+
// truncate to nearest line break
|
79
|
+
line = line.substr(0, line.length - (match[0].length - 1));
|
80
|
+
result += line;
|
81
|
+
pos += line.length;
|
82
|
+
continue;
|
83
|
+
} else if (line.length > lineLength - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
|
84
|
+
// truncate to nearest space
|
85
|
+
line = line.substr(0, line.length - (match[0].length - 1));
|
86
|
+
} else if (line.match(/[=][\da-f]{0,2}$/i)) {
|
87
|
+
// push incomplete encoding sequences to the next line
|
88
|
+
if ((match = line.match(/[=][\da-f]{0,1}$/i))) {
|
89
|
+
line = line.substr(0, line.length - match[0].length);
|
90
|
+
}
|
91
|
+
|
92
|
+
// ensure that utf-8 sequences are not split
|
93
|
+
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/[=][\da-f]{2}$/gi))) {
|
94
|
+
code = parseInt(match[0].substr(1, 2), 16);
|
95
|
+
if (code < 128) {
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
|
99
|
+
line = line.substr(0, line.length - 3);
|
100
|
+
|
101
|
+
if (code >= 0xc0) {
|
102
|
+
break;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
if (pos + line.length < len && line.substr(-1) !== '\n') {
|
108
|
+
if (line.length === lineLength && line.match(/[=][\da-f]{2}$/i)) {
|
109
|
+
line = line.substr(0, line.length - 3);
|
110
|
+
} else if (line.length === lineLength) {
|
111
|
+
line = line.substr(0, line.length - 1);
|
112
|
+
}
|
113
|
+
pos += line.length;
|
114
|
+
line += '=\r\n';
|
115
|
+
} else {
|
116
|
+
pos += line.length;
|
117
|
+
}
|
118
|
+
|
119
|
+
result += line;
|
120
|
+
}
|
121
|
+
|
122
|
+
return result;
|
123
|
+
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Helper function to check if a number is inside provided ranges
|
127
|
+
*
|
128
|
+
* @param {Number} nr Number to check for
|
129
|
+
* @param {Array} ranges An Array of allowed values
|
130
|
+
* @returns {Boolean} True if the value was found inside allowed ranges, false otherwise
|
131
|
+
*/
|
132
|
+
function checkRanges(nr, ranges) {
|
133
|
+
for (let i = ranges.length - 1; i >= 0; i--) {
|
134
|
+
if (!ranges[i].length) {
|
135
|
+
continue;
|
136
|
+
}
|
137
|
+
if (ranges[i].length === 1 && nr === ranges[i][0]) {
|
138
|
+
return true;
|
139
|
+
}
|
140
|
+
if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1]) {
|
141
|
+
return true;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
return false;
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Creates a transform stream for encoding data to Quoted-Printable encoding
|
149
|
+
*
|
150
|
+
* @constructor
|
151
|
+
* @param {Object} options Stream options
|
152
|
+
* @param {Number} [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
|
153
|
+
*/
|
154
|
+
class Encoder extends Transform {
|
155
|
+
constructor(options) {
|
156
|
+
super();
|
157
|
+
|
158
|
+
// init Transform
|
159
|
+
this.options = options || {};
|
160
|
+
|
161
|
+
if (this.options.lineLength !== false) {
|
162
|
+
this.options.lineLength = this.options.lineLength || 76;
|
163
|
+
}
|
164
|
+
|
165
|
+
this._curLine = '';
|
166
|
+
|
167
|
+
this.inputBytes = 0;
|
168
|
+
this.outputBytes = 0;
|
169
|
+
}
|
170
|
+
|
171
|
+
_transform(chunk, encoding, done) {
|
172
|
+
let qp;
|
173
|
+
|
174
|
+
if (encoding !== 'buffer') {
|
175
|
+
chunk = Buffer.from(chunk, encoding);
|
176
|
+
}
|
177
|
+
|
178
|
+
if (!chunk || !chunk.length) {
|
179
|
+
return done();
|
180
|
+
}
|
181
|
+
|
182
|
+
this.inputBytes += chunk.length;
|
183
|
+
|
184
|
+
if (this.options.lineLength) {
|
185
|
+
qp = this._curLine + encode(chunk);
|
186
|
+
qp = wrap(qp, this.options.lineLength);
|
187
|
+
qp = qp.replace(/(^|\n)([^\n]*)$/, (match, lineBreak, lastLine) => {
|
188
|
+
this._curLine = lastLine;
|
189
|
+
return lineBreak;
|
190
|
+
});
|
191
|
+
|
192
|
+
if (qp) {
|
193
|
+
this.outputBytes += qp.length;
|
194
|
+
this.push(qp);
|
195
|
+
}
|
196
|
+
} else {
|
197
|
+
qp = encode(chunk);
|
198
|
+
this.outputBytes += qp.length;
|
199
|
+
this.push(qp, 'ascii');
|
200
|
+
}
|
201
|
+
|
202
|
+
done();
|
203
|
+
}
|
204
|
+
|
205
|
+
_flush(done) {
|
206
|
+
if (this._curLine) {
|
207
|
+
this.outputBytes += this._curLine.length;
|
208
|
+
this.push(this._curLine, 'ascii');
|
209
|
+
}
|
210
|
+
done();
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
// expose to the world
|
215
|
+
module.exports = {
|
216
|
+
encode,
|
217
|
+
wrap,
|
218
|
+
Encoder
|
219
|
+
};
|
@@ -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;
|