polikolog 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/.idea/5lab.iml +12 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +10 -0
  3. package/.idea/jsLibraryMappings.xml +6 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/06-02.js +48 -0
  7. package/06-03.js +22 -0
  8. package/06-04.js +22 -0
  9. package/index.html +41 -0
  10. package/m0603.js +28 -0
  11. package/mypackage/m0603.js +28 -0
  12. package/mypackage/node_modules/.package-lock.json +24 -0
  13. package/mypackage/node_modules/nodemailer/.gitattributes +6 -0
  14. package/mypackage/node_modules/nodemailer/.prettierrc.js +8 -0
  15. package/mypackage/node_modules/nodemailer/CHANGELOG.md +725 -0
  16. package/mypackage/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
  17. package/mypackage/node_modules/nodemailer/CONTRIBUTING.md +67 -0
  18. package/mypackage/node_modules/nodemailer/LICENSE +16 -0
  19. package/mypackage/node_modules/nodemailer/README.md +97 -0
  20. package/mypackage/node_modules/nodemailer/SECURITY.txt +22 -0
  21. package/mypackage/node_modules/nodemailer/lib/addressparser/index.js +313 -0
  22. package/mypackage/node_modules/nodemailer/lib/base64/index.js +142 -0
  23. package/mypackage/node_modules/nodemailer/lib/dkim/index.js +251 -0
  24. package/mypackage/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
  25. package/mypackage/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
  26. package/mypackage/node_modules/nodemailer/lib/dkim/sign.js +117 -0
  27. package/mypackage/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
  28. package/mypackage/node_modules/nodemailer/lib/fetch/index.js +274 -0
  29. package/mypackage/node_modules/nodemailer/lib/json-transport/index.js +82 -0
  30. package/mypackage/node_modules/nodemailer/lib/mail-composer/index.js +558 -0
  31. package/mypackage/node_modules/nodemailer/lib/mailer/index.js +427 -0
  32. package/mypackage/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
  33. package/mypackage/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
  34. package/mypackage/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
  35. package/mypackage/node_modules/nodemailer/lib/mime-node/index.js +1290 -0
  36. package/mypackage/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
  37. package/mypackage/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
  38. package/mypackage/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
  39. package/mypackage/node_modules/nodemailer/lib/nodemailer.js +143 -0
  40. package/mypackage/node_modules/nodemailer/lib/qp/index.js +219 -0
  41. package/mypackage/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
  42. package/mypackage/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
  43. package/mypackage/node_modules/nodemailer/lib/shared/index.js +638 -0
  44. package/mypackage/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
  45. package/mypackage/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
  46. package/mypackage/node_modules/nodemailer/lib/smtp-connection/index.js +1796 -0
  47. package/mypackage/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
  48. package/mypackage/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
  49. package/mypackage/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
  50. package/mypackage/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
  51. package/mypackage/node_modules/nodemailer/lib/well-known/index.js +47 -0
  52. package/mypackage/node_modules/nodemailer/lib/well-known/services.json +286 -0
  53. package/mypackage/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
  54. package/mypackage/node_modules/nodemailer/package.json +46 -0
  55. package/mypackage/node_modules/nodemailer/postinstall.js +101 -0
  56. package/mypackage/package.json +15 -0
  57. package/package.json +15 -0
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ const Transform = require('stream').Transform;
4
+
5
+ /**
6
+ * Encodes a Buffer into a base64 encoded string
7
+ *
8
+ * @param {Buffer} buffer Buffer to convert
9
+ * @returns {String} base64 encoded string
10
+ */
11
+ function encode(buffer) {
12
+ if (typeof buffer === 'string') {
13
+ buffer = Buffer.from(buffer, 'utf-8');
14
+ }
15
+
16
+ return buffer.toString('base64');
17
+ }
18
+
19
+ /**
20
+ * Adds soft line breaks to a base64 string
21
+ *
22
+ * @param {String} str base64 encoded string that might need line wrapping
23
+ * @param {Number} [lineLength=76] Maximum allowed length for a line
24
+ * @returns {String} Soft-wrapped base64 encoded string
25
+ */
26
+ function wrap(str, lineLength) {
27
+ str = (str || '').toString();
28
+ lineLength = lineLength || 76;
29
+
30
+ if (str.length <= lineLength) {
31
+ return str;
32
+ }
33
+
34
+ let result = [];
35
+ let pos = 0;
36
+ let chunkLength = lineLength * 1024;
37
+ while (pos < str.length) {
38
+ let wrappedLines = str
39
+ .substr(pos, chunkLength)
40
+ .replace(new RegExp('.{' + lineLength + '}', 'g'), '$&\r\n')
41
+ .trim();
42
+ result.push(wrappedLines);
43
+ pos += chunkLength;
44
+ }
45
+
46
+ return result.join('\r\n').trim();
47
+ }
48
+
49
+ /**
50
+ * Creates a transform stream for encoding data to base64 encoding
51
+ *
52
+ * @constructor
53
+ * @param {Object} options Stream options
54
+ * @param {Number} [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
55
+ */
56
+ class Encoder extends Transform {
57
+ constructor(options) {
58
+ super();
59
+ // init Transform
60
+ this.options = options || {};
61
+
62
+ if (this.options.lineLength !== false) {
63
+ this.options.lineLength = this.options.lineLength || 76;
64
+ }
65
+
66
+ this._curLine = '';
67
+ this._remainingBytes = false;
68
+
69
+ this.inputBytes = 0;
70
+ this.outputBytes = 0;
71
+ }
72
+
73
+ _transform(chunk, encoding, done) {
74
+ if (encoding !== 'buffer') {
75
+ chunk = Buffer.from(chunk, encoding);
76
+ }
77
+
78
+ if (!chunk || !chunk.length) {
79
+ return setImmediate(done);
80
+ }
81
+
82
+ this.inputBytes += chunk.length;
83
+
84
+ if (this._remainingBytes && this._remainingBytes.length) {
85
+ chunk = Buffer.concat([this._remainingBytes, chunk], this._remainingBytes.length + chunk.length);
86
+ this._remainingBytes = false;
87
+ }
88
+
89
+ if (chunk.length % 3) {
90
+ this._remainingBytes = chunk.slice(chunk.length - (chunk.length % 3));
91
+ chunk = chunk.slice(0, chunk.length - (chunk.length % 3));
92
+ } else {
93
+ this._remainingBytes = false;
94
+ }
95
+
96
+ let b64 = this._curLine + encode(chunk);
97
+
98
+ if (this.options.lineLength) {
99
+ b64 = wrap(b64, this.options.lineLength);
100
+
101
+ // remove last line as it is still most probably incomplete
102
+ let lastLF = b64.lastIndexOf('\n');
103
+ if (lastLF < 0) {
104
+ this._curLine = b64;
105
+ b64 = '';
106
+ } else if (lastLF === b64.length - 1) {
107
+ this._curLine = '';
108
+ } else {
109
+ this._curLine = b64.substr(lastLF + 1);
110
+ b64 = b64.substr(0, lastLF + 1);
111
+ }
112
+ }
113
+
114
+ if (b64) {
115
+ this.outputBytes += b64.length;
116
+ this.push(Buffer.from(b64, 'ascii'));
117
+ }
118
+
119
+ setImmediate(done);
120
+ }
121
+
122
+ _flush(done) {
123
+ if (this._remainingBytes && this._remainingBytes.length) {
124
+ this._curLine += encode(this._remainingBytes);
125
+ }
126
+
127
+ if (this._curLine) {
128
+ this._curLine = wrap(this._curLine, this.options.lineLength);
129
+ this.outputBytes += this._curLine.length;
130
+ this.push(this._curLine, 'ascii');
131
+ this._curLine = '';
132
+ }
133
+ done();
134
+ }
135
+ }
136
+
137
+ // expose to the world
138
+ module.exports = {
139
+ encode,
140
+ wrap,
141
+ Encoder
142
+ };
@@ -0,0 +1,251 @@
1
+ 'use strict';
2
+
3
+ // FIXME:
4
+ // replace this Transform mess with a method that pipes input argument to output argument
5
+
6
+ const MessageParser = require('./message-parser');
7
+ const RelaxedBody = require('./relaxed-body');
8
+ const sign = require('./sign');
9
+ const PassThrough = require('stream').PassThrough;
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const crypto = require('crypto');
13
+
14
+ const DKIM_ALGO = 'sha256';
15
+ const MAX_MESSAGE_SIZE = 128 * 1024; // buffer messages larger than this to disk
16
+
17
+ /*
18
+ // Usage:
19
+
20
+ let dkim = new DKIM({
21
+ domainName: 'example.com',
22
+ keySelector: 'key-selector',
23
+ privateKey,
24
+ cacheDir: '/tmp'
25
+ });
26
+ dkim.sign(input).pipe(process.stdout);
27
+
28
+ // Where inputStream is a rfc822 message (either a stream, string or Buffer)
29
+ // and outputStream is a DKIM signed rfc822 message
30
+ */
31
+
32
+ class DKIMSigner {
33
+ constructor(options, keys, input, output) {
34
+ this.options = options || {};
35
+ this.keys = keys;
36
+
37
+ this.cacheTreshold = Number(this.options.cacheTreshold) || MAX_MESSAGE_SIZE;
38
+ this.hashAlgo = this.options.hashAlgo || DKIM_ALGO;
39
+
40
+ this.cacheDir = this.options.cacheDir || false;
41
+
42
+ this.chunks = [];
43
+ this.chunklen = 0;
44
+ this.readPos = 0;
45
+ this.cachePath = this.cacheDir ? path.join(this.cacheDir, 'message.' + Date.now() + '-' + crypto.randomBytes(14).toString('hex')) : false;
46
+ this.cache = false;
47
+
48
+ this.headers = false;
49
+ this.bodyHash = false;
50
+ this.parser = false;
51
+ this.relaxedBody = false;
52
+
53
+ this.input = input;
54
+ this.output = output;
55
+ this.output.usingCache = false;
56
+
57
+ this.hasErrored = false;
58
+
59
+ this.input.on('error', err => {
60
+ this.hasErrored = true;
61
+ this.cleanup();
62
+ output.emit('error', err);
63
+ });
64
+ }
65
+
66
+ cleanup() {
67
+ if (!this.cache || !this.cachePath) {
68
+ return;
69
+ }
70
+ fs.unlink(this.cachePath, () => false);
71
+ }
72
+
73
+ createReadCache() {
74
+ // pipe remainings to cache file
75
+ this.cache = fs.createReadStream(this.cachePath);
76
+ this.cache.once('error', err => {
77
+ this.cleanup();
78
+ this.output.emit('error', err);
79
+ });
80
+ this.cache.once('close', () => {
81
+ this.cleanup();
82
+ });
83
+ this.cache.pipe(this.output);
84
+ }
85
+
86
+ sendNextChunk() {
87
+ if (this.hasErrored) {
88
+ return;
89
+ }
90
+
91
+ if (this.readPos >= this.chunks.length) {
92
+ if (!this.cache) {
93
+ return this.output.end();
94
+ }
95
+ return this.createReadCache();
96
+ }
97
+ let chunk = this.chunks[this.readPos++];
98
+ if (this.output.write(chunk) === false) {
99
+ return this.output.once('drain', () => {
100
+ this.sendNextChunk();
101
+ });
102
+ }
103
+ setImmediate(() => this.sendNextChunk());
104
+ }
105
+
106
+ sendSignedOutput() {
107
+ let keyPos = 0;
108
+ let signNextKey = () => {
109
+ if (keyPos >= this.keys.length) {
110
+ this.output.write(this.parser.rawHeaders);
111
+ return setImmediate(() => this.sendNextChunk());
112
+ }
113
+ let key = this.keys[keyPos++];
114
+ let dkimField = sign(this.headers, this.hashAlgo, this.bodyHash, {
115
+ domainName: key.domainName,
116
+ keySelector: key.keySelector,
117
+ privateKey: key.privateKey,
118
+ headerFieldNames: this.options.headerFieldNames,
119
+ skipFields: this.options.skipFields
120
+ });
121
+ if (dkimField) {
122
+ this.output.write(Buffer.from(dkimField + '\r\n'));
123
+ }
124
+ return setImmediate(signNextKey);
125
+ };
126
+
127
+ if (this.bodyHash && this.headers) {
128
+ return signNextKey();
129
+ }
130
+
131
+ this.output.write(this.parser.rawHeaders);
132
+ this.sendNextChunk();
133
+ }
134
+
135
+ createWriteCache() {
136
+ this.output.usingCache = true;
137
+ // pipe remainings to cache file
138
+ this.cache = fs.createWriteStream(this.cachePath);
139
+ this.cache.once('error', err => {
140
+ this.cleanup();
141
+ // drain input
142
+ this.relaxedBody.unpipe(this.cache);
143
+ this.relaxedBody.on('readable', () => {
144
+ while (this.relaxedBody.read() !== null) {
145
+ // do nothing
146
+ }
147
+ });
148
+ this.hasErrored = true;
149
+ // emit error
150
+ this.output.emit('error', err);
151
+ });
152
+ this.cache.once('close', () => {
153
+ this.sendSignedOutput();
154
+ });
155
+ this.relaxedBody.removeAllListeners('readable');
156
+ this.relaxedBody.pipe(this.cache);
157
+ }
158
+
159
+ signStream() {
160
+ this.parser = new MessageParser();
161
+ this.relaxedBody = new RelaxedBody({
162
+ hashAlgo: this.hashAlgo
163
+ });
164
+
165
+ this.parser.on('headers', value => {
166
+ this.headers = value;
167
+ });
168
+
169
+ this.relaxedBody.on('hash', value => {
170
+ this.bodyHash = value;
171
+ });
172
+
173
+ this.relaxedBody.on('readable', () => {
174
+ let chunk;
175
+ if (this.cache) {
176
+ return;
177
+ }
178
+ while ((chunk = this.relaxedBody.read()) !== null) {
179
+ this.chunks.push(chunk);
180
+ this.chunklen += chunk.length;
181
+ if (this.chunklen >= this.cacheTreshold && this.cachePath) {
182
+ return this.createWriteCache();
183
+ }
184
+ }
185
+ });
186
+
187
+ this.relaxedBody.on('end', () => {
188
+ if (this.cache) {
189
+ return;
190
+ }
191
+ this.sendSignedOutput();
192
+ });
193
+
194
+ this.parser.pipe(this.relaxedBody);
195
+ setImmediate(() => this.input.pipe(this.parser));
196
+ }
197
+ }
198
+
199
+ class DKIM {
200
+ constructor(options) {
201
+ this.options = options || {};
202
+ this.keys = [].concat(
203
+ this.options.keys || {
204
+ domainName: options.domainName,
205
+ keySelector: options.keySelector,
206
+ privateKey: options.privateKey
207
+ }
208
+ );
209
+ }
210
+
211
+ sign(input, extraOptions) {
212
+ let output = new PassThrough();
213
+ let inputStream = input;
214
+ let writeValue = false;
215
+
216
+ if (Buffer.isBuffer(input)) {
217
+ writeValue = input;
218
+ inputStream = new PassThrough();
219
+ } else if (typeof input === 'string') {
220
+ writeValue = Buffer.from(input);
221
+ inputStream = new PassThrough();
222
+ }
223
+
224
+ let options = this.options;
225
+ if (extraOptions && Object.keys(extraOptions).length) {
226
+ options = {};
227
+ Object.keys(this.options || {}).forEach(key => {
228
+ options[key] = this.options[key];
229
+ });
230
+ Object.keys(extraOptions || {}).forEach(key => {
231
+ if (!(key in options)) {
232
+ options[key] = extraOptions[key];
233
+ }
234
+ });
235
+ }
236
+
237
+ let signer = new DKIMSigner(options, this.keys, inputStream, output);
238
+ setImmediate(() => {
239
+ signer.signStream();
240
+ if (writeValue) {
241
+ setImmediate(() => {
242
+ inputStream.end(writeValue);
243
+ });
244
+ }
245
+ });
246
+
247
+ return output;
248
+ }
249
+ }
250
+
251
+ module.exports = DKIM;
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ const Transform = require('stream').Transform;
4
+
5
+ /**
6
+ * MessageParser instance is a transform stream that separates message headers
7
+ * from the rest of the body. Headers are emitted with the 'headers' event. Message
8
+ * body is passed on as the resulting stream.
9
+ */
10
+ class MessageParser extends Transform {
11
+ constructor(options) {
12
+ super(options);
13
+ this.lastBytes = Buffer.alloc(4);
14
+ this.headersParsed = false;
15
+ this.headerBytes = 0;
16
+ this.headerChunks = [];
17
+ this.rawHeaders = false;
18
+ this.bodySize = 0;
19
+ }
20
+
21
+ /**
22
+ * Keeps count of the last 4 bytes in order to detect line breaks on chunk boundaries
23
+ *
24
+ * @param {Buffer} data Next data chunk from the stream
25
+ */
26
+ updateLastBytes(data) {
27
+ let lblen = this.lastBytes.length;
28
+ let nblen = Math.min(data.length, lblen);
29
+
30
+ // shift existing bytes
31
+ for (let i = 0, len = lblen - nblen; i < len; i++) {
32
+ this.lastBytes[i] = this.lastBytes[i + nblen];
33
+ }
34
+
35
+ // add new bytes
36
+ for (let i = 1; i <= nblen; i++) {
37
+ this.lastBytes[lblen - i] = data[data.length - i];
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Finds and removes message headers from the remaining body. We want to keep
43
+ * headers separated until final delivery to be able to modify these
44
+ *
45
+ * @param {Buffer} data Next chunk of data
46
+ * @return {Boolean} Returns true if headers are already found or false otherwise
47
+ */
48
+ checkHeaders(data) {
49
+ if (this.headersParsed) {
50
+ return true;
51
+ }
52
+
53
+ let lblen = this.lastBytes.length;
54
+ let headerPos = 0;
55
+ this.curLinePos = 0;
56
+ for (let i = 0, len = this.lastBytes.length + data.length; i < len; i++) {
57
+ let chr;
58
+ if (i < lblen) {
59
+ chr = this.lastBytes[i];
60
+ } else {
61
+ chr = data[i - lblen];
62
+ }
63
+ if (chr === 0x0a && i) {
64
+ let pr1 = i - 1 < lblen ? this.lastBytes[i - 1] : data[i - 1 - lblen];
65
+ let pr2 = i > 1 ? (i - 2 < lblen ? this.lastBytes[i - 2] : data[i - 2 - lblen]) : false;
66
+ if (pr1 === 0x0a) {
67
+ this.headersParsed = true;
68
+ headerPos = i - lblen + 1;
69
+ this.headerBytes += headerPos;
70
+ break;
71
+ } else if (pr1 === 0x0d && pr2 === 0x0a) {
72
+ this.headersParsed = true;
73
+ headerPos = i - lblen + 1;
74
+ this.headerBytes += headerPos;
75
+ break;
76
+ }
77
+ }
78
+ }
79
+
80
+ if (this.headersParsed) {
81
+ this.headerChunks.push(data.slice(0, headerPos));
82
+ this.rawHeaders = Buffer.concat(this.headerChunks, this.headerBytes);
83
+ this.headerChunks = null;
84
+ this.emit('headers', this.parseHeaders());
85
+ if (data.length - 1 > headerPos) {
86
+ let chunk = data.slice(headerPos);
87
+ this.bodySize += chunk.length;
88
+ // this would be the first chunk of data sent downstream
89
+ setImmediate(() => this.push(chunk));
90
+ }
91
+ return false;
92
+ } else {
93
+ this.headerBytes += data.length;
94
+ this.headerChunks.push(data);
95
+ }
96
+
97
+ // store last 4 bytes to catch header break
98
+ this.updateLastBytes(data);
99
+
100
+ return false;
101
+ }
102
+
103
+ _transform(chunk, encoding, callback) {
104
+ if (!chunk || !chunk.length) {
105
+ return callback();
106
+ }
107
+
108
+ if (typeof chunk === 'string') {
109
+ chunk = Buffer.from(chunk, encoding);
110
+ }
111
+
112
+ let headersFound;
113
+
114
+ try {
115
+ headersFound = this.checkHeaders(chunk);
116
+ } catch (E) {
117
+ return callback(E);
118
+ }
119
+
120
+ if (headersFound) {
121
+ this.bodySize += chunk.length;
122
+ this.push(chunk);
123
+ }
124
+
125
+ setImmediate(callback);
126
+ }
127
+
128
+ _flush(callback) {
129
+ if (this.headerChunks) {
130
+ let chunk = Buffer.concat(this.headerChunks, this.headerBytes);
131
+ this.bodySize += chunk.length;
132
+ this.push(chunk);
133
+ this.headerChunks = null;
134
+ }
135
+ callback();
136
+ }
137
+
138
+ parseHeaders() {
139
+ let lines = (this.rawHeaders || '').toString().split(/\r?\n/);
140
+ for (let i = lines.length - 1; i > 0; i--) {
141
+ if (/^\s/.test(lines[i])) {
142
+ lines[i - 1] += '\n' + lines[i];
143
+ lines.splice(i, 1);
144
+ }
145
+ }
146
+ return lines
147
+ .filter(line => line.trim())
148
+ .map(line => ({
149
+ key: line.substr(0, line.indexOf(':')).trim().toLowerCase(),
150
+ line
151
+ }));
152
+ }
153
+ }
154
+
155
+ module.exports = MessageParser;
@@ -0,0 +1,154 @@
1
+ 'use strict';
2
+
3
+ // streams through a message body and calculates relaxed body hash
4
+
5
+ const Transform = require('stream').Transform;
6
+ const crypto = require('crypto');
7
+
8
+ class RelaxedBody extends Transform {
9
+ constructor(options) {
10
+ super();
11
+ options = options || {};
12
+ this.chunkBuffer = [];
13
+ this.chunkBufferLen = 0;
14
+ this.bodyHash = crypto.createHash(options.hashAlgo || 'sha1');
15
+ this.remainder = '';
16
+ this.byteLength = 0;
17
+
18
+ this.debug = options.debug;
19
+ this._debugBody = options.debug ? [] : false;
20
+ }
21
+
22
+ updateHash(chunk) {
23
+ let bodyStr;
24
+
25
+ // find next remainder
26
+ let nextRemainder = '';
27
+
28
+ // This crux finds and removes the spaces from the last line and the newline characters after the last non-empty line
29
+ // If we get another chunk that does not match this description then we can restore the previously processed data
30
+ let state = 'file';
31
+ for (let i = chunk.length - 1; i >= 0; i--) {
32
+ let c = chunk[i];
33
+
34
+ if (state === 'file' && (c === 0x0a || c === 0x0d)) {
35
+ // do nothing, found \n or \r at the end of chunk, stil end of file
36
+ } else if (state === 'file' && (c === 0x09 || c === 0x20)) {
37
+ // switch to line ending mode, this is the last non-empty line
38
+ state = 'line';
39
+ } else if (state === 'line' && (c === 0x09 || c === 0x20)) {
40
+ // do nothing, found ' ' or \t at the end of line, keep processing the last non-empty line
41
+ } else if (state === 'file' || state === 'line') {
42
+ // non line/file ending character found, switch to body mode
43
+ state = 'body';
44
+ if (i === chunk.length - 1) {
45
+ // final char is not part of line end or file end, so do nothing
46
+ break;
47
+ }
48
+ }
49
+
50
+ if (i === 0) {
51
+ // reached to the beginning of the chunk, check if it is still about the ending
52
+ // and if the remainder also matches
53
+ if (
54
+ (state === 'file' && (!this.remainder || /[\r\n]$/.test(this.remainder))) ||
55
+ (state === 'line' && (!this.remainder || /[ \t]$/.test(this.remainder)))
56
+ ) {
57
+ // keep everything
58
+ this.remainder += chunk.toString('binary');
59
+ return;
60
+ } else if (state === 'line' || state === 'file') {
61
+ // process existing remainder as normal line but store the current chunk
62
+ nextRemainder = chunk.toString('binary');
63
+ chunk = false;
64
+ break;
65
+ }
66
+ }
67
+
68
+ if (state !== 'body') {
69
+ continue;
70
+ }
71
+
72
+ // reached first non ending byte
73
+ nextRemainder = chunk.slice(i + 1).toString('binary');
74
+ chunk = chunk.slice(0, i + 1);
75
+ break;
76
+ }
77
+
78
+ let needsFixing = !!this.remainder;
79
+ if (chunk && !needsFixing) {
80
+ // check if we even need to change anything
81
+ for (let i = 0, len = chunk.length; i < len; i++) {
82
+ if (i && chunk[i] === 0x0a && chunk[i - 1] !== 0x0d) {
83
+ // missing \r before \n
84
+ needsFixing = true;
85
+ break;
86
+ } else if (i && chunk[i] === 0x0d && chunk[i - 1] === 0x20) {
87
+ // trailing WSP found
88
+ needsFixing = true;
89
+ break;
90
+ } else if (i && chunk[i] === 0x20 && chunk[i - 1] === 0x20) {
91
+ // multiple spaces found, needs to be replaced with just one
92
+ needsFixing = true;
93
+ break;
94
+ } else if (chunk[i] === 0x09) {
95
+ // TAB found, needs to be replaced with a space
96
+ needsFixing = true;
97
+ break;
98
+ }
99
+ }
100
+ }
101
+
102
+ if (needsFixing) {
103
+ bodyStr = this.remainder + (chunk ? chunk.toString('binary') : '');
104
+ this.remainder = nextRemainder;
105
+ bodyStr = bodyStr
106
+ .replace(/\r?\n/g, '\n') // use js line endings
107
+ .replace(/[ \t]*$/gm, '') // remove line endings, rtrim
108
+ .replace(/[ \t]+/gm, ' ') // single spaces
109
+ .replace(/\n/g, '\r\n'); // restore rfc822 line endings
110
+ chunk = Buffer.from(bodyStr, 'binary');
111
+ } else if (nextRemainder) {
112
+ this.remainder = nextRemainder;
113
+ }
114
+
115
+ if (this.debug) {
116
+ this._debugBody.push(chunk);
117
+ }
118
+ this.bodyHash.update(chunk);
119
+ }
120
+
121
+ _transform(chunk, encoding, callback) {
122
+ if (!chunk || !chunk.length) {
123
+ return callback();
124
+ }
125
+
126
+ if (typeof chunk === 'string') {
127
+ chunk = Buffer.from(chunk, encoding);
128
+ }
129
+
130
+ this.updateHash(chunk);
131
+
132
+ this.byteLength += chunk.length;
133
+ this.push(chunk);
134
+ callback();
135
+ }
136
+
137
+ _flush(callback) {
138
+ // generate final hash and emit it
139
+ if (/[\r\n]$/.test(this.remainder) && this.byteLength > 2) {
140
+ // add terminating line end
141
+ this.bodyHash.update(Buffer.from('\r\n'));
142
+ }
143
+ if (!this.byteLength) {
144
+ // emit empty line buffer to keep the stream flowing
145
+ this.push(Buffer.from('\r\n'));
146
+ // this.bodyHash.update(Buffer.from('\r\n'));
147
+ }
148
+
149
+ this.emit('hash', this.bodyHash.digest('base64'), this.debug ? Buffer.concat(this._debugBody) : false);
150
+ callback();
151
+ }
152
+ }
153
+
154
+ module.exports = RelaxedBody;