zhuha 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of zhuha might be problematic. Click here for more details.

Files changed (53) hide show
  1. package/06-02.html +81 -0
  2. package/06-02.js +72 -0
  3. package/06-03.js +7 -0
  4. package/06-04.js +7 -0
  5. package/AnswersLW5.pdf +0 -0
  6. package/m0603/m0603.js +30 -0
  7. package/m0603/node_modules/.package-lock.json +16 -0
  8. package/m0603/node_modules/nodemailer/.gitattributes +6 -0
  9. package/m0603/node_modules/nodemailer/.prettierrc.js +8 -0
  10. package/m0603/node_modules/nodemailer/CHANGELOG.md +725 -0
  11. package/m0603/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
  12. package/m0603/node_modules/nodemailer/CONTRIBUTING.md +67 -0
  13. package/m0603/node_modules/nodemailer/LICENSE +16 -0
  14. package/m0603/node_modules/nodemailer/README.md +97 -0
  15. package/m0603/node_modules/nodemailer/SECURITY.txt +22 -0
  16. package/m0603/node_modules/nodemailer/lib/addressparser/index.js +313 -0
  17. package/m0603/node_modules/nodemailer/lib/base64/index.js +142 -0
  18. package/m0603/node_modules/nodemailer/lib/dkim/index.js +251 -0
  19. package/m0603/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
  20. package/m0603/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
  21. package/m0603/node_modules/nodemailer/lib/dkim/sign.js +117 -0
  22. package/m0603/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
  23. package/m0603/node_modules/nodemailer/lib/fetch/index.js +274 -0
  24. package/m0603/node_modules/nodemailer/lib/json-transport/index.js +82 -0
  25. package/m0603/node_modules/nodemailer/lib/mail-composer/index.js +558 -0
  26. package/m0603/node_modules/nodemailer/lib/mailer/index.js +427 -0
  27. package/m0603/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
  28. package/m0603/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
  29. package/m0603/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
  30. package/m0603/node_modules/nodemailer/lib/mime-node/index.js +1290 -0
  31. package/m0603/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
  32. package/m0603/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
  33. package/m0603/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
  34. package/m0603/node_modules/nodemailer/lib/nodemailer.js +143 -0
  35. package/m0603/node_modules/nodemailer/lib/qp/index.js +219 -0
  36. package/m0603/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
  37. package/m0603/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
  38. package/m0603/node_modules/nodemailer/lib/shared/index.js +638 -0
  39. package/m0603/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
  40. package/m0603/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
  41. package/m0603/node_modules/nodemailer/lib/smtp-connection/index.js +1796 -0
  42. package/m0603/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
  43. package/m0603/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
  44. package/m0603/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
  45. package/m0603/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
  46. package/m0603/node_modules/nodemailer/lib/well-known/index.js +47 -0
  47. package/m0603/node_modules/nodemailer/lib/well-known/services.json +286 -0
  48. package/m0603/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
  49. package/m0603/node_modules/nodemailer/package.json +46 -0
  50. package/m0603/node_modules/nodemailer/postinstall.js +101 -0
  51. package/m0603/package-lock.json +31 -0
  52. package/m0603/package.json +15 -0
  53. package/package.json +16 -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;