Skip to content

Commit bfabe13

Browse files
authored
Revert "fix(rebuild): ZMSA-48: fix rebuilding of multipart parts (#942)"
This reverts commit 11b4fcb.
1 parent 0c1cf93 commit bfabe13

File tree

7 files changed

+37
-382
lines changed

7 files changed

+37
-382
lines changed

imap-core/lib/imap-tools.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -668,13 +668,6 @@ module.exports.getQueryResponse = function (query, message, options) {
668668
mimeTree = indexer.parseMimeTree(message.raw);
669669
}
670670
value = indexer.getContents(mimeTree);
671-
if (value && value.type === 'stream') {
672-
let messageSize = Number(message.size);
673-
let expectedLength = Number(value.expectedLength);
674-
if (!Number.isFinite(expectedLength) && Number.isFinite(messageSize)) {
675-
value.expectedLength = messageSize;
676-
}
677-
}
678671
break;
679672

680673
case 'rfc822.size':
@@ -734,14 +727,6 @@ module.exports.getQueryResponse = function (query, message, options) {
734727
});
735728
}
736729

737-
if (value && value.type === 'stream' && item.path === '' && item.type === 'content') {
738-
let messageSize = Number(message.size);
739-
let expectedLength = Number(value.expectedLength);
740-
if (!Number.isFinite(expectedLength) && Number.isFinite(messageSize)) {
741-
value.expectedLength = messageSize;
742-
}
743-
}
744-
745730
if (item.partial) {
746731
let len;
747732

imap-core/lib/indexer/indexer.js

Lines changed: 20 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ class Indexer {
7070

7171
let finalize = () => {
7272
if (node.boundary) {
73-
append(`--${node.boundary}--`);
74-
if (node.epilogue && node.epilogue.length) {
75-
size += node.epilogue.length;
76-
}
73+
append(`--${node.boundary}--\r\n`);
7774
}
7875

7976
append();
@@ -85,21 +82,7 @@ class Indexer {
8582
if (!node.boundary) {
8683
append(false, true); // force newline
8784
}
88-
let nodeSize = Number(node.size);
89-
if (!Number.isFinite(nodeSize)) {
90-
if (Buffer.isBuffer(node.body)) {
91-
nodeSize = node.body.length;
92-
} else if (node.body && node.body.buffer && Buffer.isBuffer(node.body.buffer)) {
93-
nodeSize = node.body.buffer.length;
94-
} else if (typeof node.body === 'string') {
95-
nodeSize = Buffer.byteLength(node.body, 'binary');
96-
} else if (Array.isArray(node.body)) {
97-
nodeSize = Buffer.byteLength(node.body.join(''), 'binary');
98-
} else {
99-
nodeSize = 0;
100-
}
101-
}
102-
size += nodeSize;
85+
size += node.size;
10386
}
10487

10588
if (node.boundary) {
@@ -153,8 +136,6 @@ class Indexer {
153136

154137
let curWritePos = 0;
155138
let writeLength = 0;
156-
let lastByte = null;
157-
let forceSeparator = false;
158139

159140
let getCurrentBounds = size => {
160141
if (curWritePos + size < startFrom) {
@@ -185,10 +166,6 @@ class Indexer {
185166
if (!chunk || !chunk.length) {
186167
return;
187168
}
188-
chunk = normalizeChunk(chunk);
189-
if (!chunk || !chunk.length) {
190-
return;
191-
}
192169

193170
if (curWritePos >= startFrom) {
194171
// already allowed to write
@@ -219,7 +196,6 @@ class Indexer {
219196
}
220197
}
221198

222-
lastByte = chunk[chunk.length - 1];
223199
if (output.write(chunk) === false) {
224200
await new Promise(resolve => {
225201
output.once('drain', resolve());
@@ -236,10 +212,7 @@ class Indexer {
236212
let emit = async (data, force) => {
237213
if (remainder || data || force) {
238214
if (!firstLine) {
239-
if (forceSeparator || lastByte !== 0x0a) {
240-
await write(NEWLINE);
241-
}
242-
forceSeparator = false;
215+
await write(NEWLINE);
243216
} else {
244217
firstLine = false;
245218
}
@@ -262,11 +235,9 @@ class Indexer {
262235

263236
if (!textOnly || !isRootNode) {
264237
await emit(formatHeaders(node.header).join('\r\n') + '\r\n');
265-
forceSeparator = true;
266238
}
267239

268240
isRootNode = false;
269-
let epilogue = null;
270241
if (Buffer.isBuffer(node.body)) {
271242
// node Buffer
272243
remainder = node.body;
@@ -281,16 +252,6 @@ class Indexer {
281252
remainder = node.body;
282253
}
283254

284-
if (node.boundary) {
285-
if (node.epilogue && node.epilogue.length) {
286-
epilogue = normalizeChunk(node.epilogue);
287-
} else if (remainder && remainder.length) {
288-
let splitBody = splitMultipartBody(remainder);
289-
remainder = splitBody.preamble;
290-
epilogue = splitBody.epilogue;
291-
}
292-
}
293-
294255
if (node.boundary) {
295256
// this is a multipart node, so start with initial boundary before continuing
296257
await emit(`--${node.boundary}`);
@@ -328,62 +289,33 @@ class Indexer {
328289
}
329290
}
330291

331-
let attachmentSize = Number(node.size);
332-
if (!Number.isFinite(attachmentSize)) {
333-
attachmentSize = Number(attachmentData && attachmentData.metadata && attachmentData.metadata.esize);
334-
}
335-
if (!Number.isFinite(attachmentSize)) {
336-
attachmentSize = Number(attachmentData && attachmentData.length);
337-
}
338-
if (!Number.isFinite(attachmentSize)) {
339-
attachmentSize = 0;
340-
}
341-
let nodeTransferEncoding = ((node.parsedHeader && node.parsedHeader['content-transfer-encoding']) || '7bit')
342-
.toString()
343-
.toLowerCase()
344-
.trim();
292+
let attachmentSize = node.size;
345293
// we need to calculate expected length as the original does not apply anymore
346294
// original size matches input data but decoding/encoding is not 100% lossless so we need to
347295
// calculate the actual possible output size
348296
if (attachmentData.metadata && attachmentData.metadata.decoded && attachmentData.metadata.lineLen) {
349297
let b64Size = Math.ceil(attachmentData.length / 3) * 4;
350-
let lineBreaks = Math.floor((b64Size - 1) / attachmentData.metadata.lineLen);
351-
let storedLineCount = normalizeLineCount(attachmentData.metadata.lineCount);
352-
353-
if (storedLineCount !== null) {
354-
lineBreaks = storedLineCount;
355-
} else if (attachmentData.metadata.esize) {
356-
let recovered = Math.floor((attachmentData.metadata.esize - b64Size) / 2);
357-
if (recovered >= 0) {
358-
lineBreaks = Math.max(lineBreaks, recovered);
359-
}
360-
}
298+
let lineBreaks = Math.floor(b64Size / attachmentData.metadata.lineLen);
361299

362-
let computedSize = b64Size + lineBreaks * 2;
363-
if (Number.isFinite(attachmentSize)) {
364-
attachmentSize = Math.max(attachmentSize, computedSize);
365-
} else {
366-
attachmentSize = computedSize;
300+
// extra case where base64 string ends at line end
301+
// in this case we do not need the ending line break
302+
if (lineBreaks && b64Size % attachmentData.metadata.lineLen === 0) {
303+
lineBreaks--;
367304
}
305+
306+
attachmentSize = b64Size + lineBreaks * 2;
368307
}
369308

370309
let readBounds = getCurrentBounds(attachmentSize);
371310
if (readBounds) {
372311
// move write pointer ahead by skipped base64 bytes
373-
let bytes = Math.min(readBounds.startFrom, attachmentSize);
312+
let bytes = Math.min(readBounds.startFrom, node.size);
374313
curWritePos += bytes;
375314

376315
// only process attachment if we are reading inside existing bounds
377-
if (attachmentSize > readBounds.startFrom) {
316+
if (node.size > readBounds.startFrom) {
378317
let attachmentStream = this.attachmentStorage.createReadStream(attachmentId, attachmentData, readBounds);
379318
await new Promise((resolve, reject) => {
380-
let attachmentOutputBytes = 0;
381-
attachmentStream.on('data', chunk => {
382-
if (chunk && chunk.length) {
383-
lastByte = chunk[chunk.length - 1];
384-
attachmentOutputBytes += chunk.length;
385-
}
386-
});
387319
attachmentStream.once('error', err => {
388320
if (err.code === 'ENOENT') {
389321
this.loggelf({
@@ -398,36 +330,16 @@ class Indexer {
398330

399331
attachmentStream.once('end', () => {
400332
// update read offset counters
401-
let bytes = attachmentOutputBytes;
402-
403-
if (!bytes && 'outputBytes' in attachmentStream) {
404-
bytes = attachmentStream.outputBytes;
405-
}
406333

407-
if (!bytes) {
408-
bytes = readBounds.maxLength;
409-
}
334+
let bytes = 'outputBytes' in attachmentStream ? attachmentStream.outputBytes : readBounds.maxLength;
410335

411336
if (bytes) {
412337
curWritePos += bytes;
413338
if (maxLength) {
414339
writeLength += bytes;
415340
}
416341
}
417-
418-
if (!output.isLimited && attachmentSize && bytes && bytes < attachmentSize) {
419-
let missing = attachmentSize - bytes;
420-
if (missing > 0 && missing % 2 === 0) {
421-
let transferEncoding = (attachmentData && attachmentData.transferEncoding) || nodeTransferEncoding;
422-
if (transferEncoding === 'base64') {
423-
return write(Buffer.alloc(missing, '\r\n'))
424-
.then(resolve)
425-
.catch(reject);
426-
}
427-
}
428-
}
429-
430-
return resolve();
342+
resolve();
431343
});
432344

433345
attachmentStream.pipe(output, {
@@ -455,17 +367,18 @@ class Indexer {
455367
}
456368

457369
if (node.boundary) {
458-
await emit(`--${node.boundary}--`);
459-
if (epilogue && epilogue.length) {
460-
await write(epilogue);
461-
}
370+
await emit(`--${node.boundary}--\r\n`);
462371
}
463372

464373
await emit();
465374
};
466375

467376
await walk(mimeTree);
468377

378+
if (mimeTree.lineCount > 1) {
379+
await write(NEWLINE);
380+
}
381+
469382
output.end();
470383
};
471384

@@ -966,81 +879,6 @@ function formatHeaders(headers) {
966879
return headers;
967880
}
968881

969-
function normalizeChunk(chunk) {
970-
if (!chunk) {
971-
return chunk;
972-
}
973-
if (Buffer.isBuffer(chunk)) {
974-
return chunk;
975-
}
976-
if (chunk.buffer && Buffer.isBuffer(chunk.buffer)) {
977-
return chunk.buffer;
978-
}
979-
if (typeof chunk === 'string') {
980-
return Buffer.from(chunk, 'binary');
981-
}
982-
try {
983-
return Buffer.from(chunk);
984-
} catch {
985-
return null;
986-
}
987-
}
988-
989-
function normalizeLineCount(value) {
990-
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
991-
return value;
992-
}
993-
if (!value) {
994-
return null;
995-
}
996-
if (typeof value.toNumber === 'function') {
997-
const num = value.toNumber();
998-
if (Number.isFinite(num) && num >= 0) {
999-
return num;
1000-
}
1001-
}
1002-
const coerced = Number(value);
1003-
if (Number.isFinite(coerced) && coerced >= 0) {
1004-
return coerced;
1005-
}
1006-
return null;
1007-
}
1008-
1009-
function splitMultipartBody(body) {
1010-
if (!body || !body.length) {
1011-
return { preamble: body, epilogue: null };
1012-
}
1013-
1014-
let buffer = Buffer.isBuffer(body) ? body : Buffer.from(body, 'binary');
1015-
1016-
// Find last non-linebreak byte to ensure there is actual content.
1017-
let lastContent = buffer.length - 1;
1018-
while (lastContent >= 0 && (buffer[lastContent] === 0x0d || buffer[lastContent] === 0x0a)) {
1019-
lastContent--;
1020-
}
1021-
1022-
if (lastContent < 0) {
1023-
return { preamble: buffer, epilogue: null };
1024-
}
1025-
1026-
let pos = buffer.length;
1027-
let crlfCount = 0;
1028-
while (pos >= 2 && buffer[pos - 2] === 0x0d && buffer[pos - 1] === 0x0a) {
1029-
crlfCount++;
1030-
pos -= 2;
1031-
}
1032-
1033-
if (crlfCount <= 1) {
1034-
return { preamble: buffer, epilogue: null };
1035-
}
1036-
1037-
let splitIndex = buffer.length - (crlfCount - 1) * 2;
1038-
return {
1039-
preamble: buffer.slice(0, splitIndex),
1040-
epilogue: buffer.slice(splitIndex)
1041-
};
1042-
}
1043-
1044882
function textToHtml(str) {
1045883
let encoded = he
1046884
// encode special chars

0 commit comments

Comments
 (0)