fix streams in on-copy and message-handler. message-handler optimizations, filter-handler optimizations

This commit is contained in:
Nikolai Ovtsinnikov 2024-12-04 21:39:08 +02:00
parent fa849f2fb4
commit d1c67e6264
No known key found for this signature in database
GPG key ID: CA6FC1AF1A8E269D
3 changed files with 467 additions and 401 deletions

View file

@ -141,10 +141,9 @@ class FilterHandler {
let rawchunks = chunks;
let raw;
let prepared;
let raw = Buffer.concat(chunks, chunklen);
if (options.mimeTree) {
if (options.mimeTree && options.mimeTree.header) {
// remove old headers
@ -159,6 +158,7 @@ class FilterHandler {
mimeTree: options.mimeTree
});
} else {
raw = Buffer.concat(chunks, chunklen);
prepared = await this.prepareMessage({
raw
});
@ -661,11 +661,14 @@ class FilterHandler {
date: false,
flags,
raw,
rawchunks
rawchunks,
chunklen
};
if (raw) {
messageOpts.raw = raw;
}
if (options.verificationResults) {
messageOpts.verificationResults = options.verificationResults;
}

View file

@ -233,15 +233,27 @@ async function copyHandler(server, messageHandler, connection, mailbox, update,
const newPrepared = await new Promise((resolve, reject) => {
if (targetMailboxEncrypted && !isMessageEncrypted && userData.pubKey) {
// encrypt message
const outputStream = messageHandler.indexer.rebuild(messageData.mimeTree).value; // get raw rebuilder stream
let raw = Buffer.from([], 'binary'); // set initial raw
// get raw from existing mimetree
let outputStream = messageHandler.indexer.rebuild(messageData.mimeTree); // get raw rebuilder response obj (.value is the stream)
if (!outputStream || outputStream.type !== 'stream' || !outputStream.value) {
return reject(new Error('Cannot fetch message'));
}
outputStream = outputStream.value; // set stream to actual stream object (.value)
let chunks = [];
let chunklen = 0;
outputStream
.on('data', data => {
raw = Buffer.concat([raw, data]);
.on('readable', () => {
let chunk;
while ((chunk = outputStream.read()) !== null) {
chunks.push(chunk);
chunklen += chunk.length;
}
})
.on('end', () => {
messageHandler.encryptMessages(userData.pubKey || '', raw, (err, res) => {
const raw = Buffer.concat(chunks, chunklen);
messageHandler.encryptMessages(userData.pubKey, raw, (err, res) => {
if (err) {
return reject(err);
}

View file

@ -204,22 +204,39 @@ class MessageHandler {
}
// get target user data
this.prepareMessage(options, (err, prepared) => {
if (err) {
return callback(err);
}
let prepared = options.prepared; // might be undefined
// check if already encrypted
let alreadyEncrypted = false;
// message already prepared, check if encrypted
if (prepared) {
// got prepared
const parsedHeader = (prepared.mimeTree && prepared.mimeTree?.parsedHeader) || {};
const parsedContentType = parsedHeader['content-type'];
if (parsedContentType && parsedContentType.subtype === 'encrypted') {
alreadyEncrypted = true;
}
} else {
// no prepared, use raw
if (options.rawchunks && !options.raw) {
// got rawchunks instead of raw
if (options.chunklen) {
options.raw = Buffer.concat(options.rawchunks, options.chunklen);
} else {
options.raw = Buffer.concat(options.rawchunks);
}
}
const rawString = options.raw.toString('binary'); // get string from the raw bytes of message
const regex = /Content-Type:\s*multipart\/encrypted/gim;
if (regex.test(rawString)) {
// if there is encrypted content-type then message already encrypted, no need to re-encrypt it
alreadyEncrypted = true;
}
}
let flags = Array.isArray(options.flags) ? options.flags : [].concat(options.flags || []);
@ -589,10 +606,7 @@ class MessageHandler {
let now = new Date();
for (let auditData of audits) {
if (
(auditData.start && auditData.start > now) ||
(auditData.end && auditData.end < now)
) {
if ((auditData.start && auditData.start > now) || (auditData.end && auditData.end < now)) {
// audit not active
continue;
}
@ -637,8 +651,17 @@ class MessageHandler {
if (!alreadyEncrypted) {
// not already encrypted, check if user has encryption on or target mailbox is encrypted
if ((userData.encryptMessages || !!mailboxData.encryptMessages) && userData.pubKey && !flags.includes('\\Draft')) {
if (options.rawchunks && !options.raw) {
// got rawchunks instead of raw
if (options.chunklen) {
options.raw = Buffer.concat(options.rawchunks, options.chunklen);
} else {
options.raw = Buffer.concat(options.rawchunks);
}
}
// user has encryption on or target mailbox encrypted, encrypt message and prepare again
// do not encrypt drafts
// may have a situation where we got prepared and no options.raw but options.rawchunks instead, concat them
this.encryptMessage(userData.pubKey, options.raw, (err, res) => {
if (err) {
return callback(err);
@ -665,13 +688,26 @@ class MessageHandler {
});
} else {
// not already encrypted and no need to
this.prepareMessage(options, (err, newPrepared) => {
if (err) {
return callback(err);
}
prepared = newPrepared;
addMessage();
});
}
} else {
// message already encrypted
addMessage();
this.prepareMessage(options, (err, newPrepared) => {
if (err) {
return callback(err);
}
prepared = newPrepared;
addMessage();
});
}
});
});
}
@ -1204,17 +1240,32 @@ class MessageHandler {
return done(err);
}
// get user data
if (!res.pubKey) {
return updateMessage();
}
// get raw from existing mimetree
const outputStream = this.indexer.rebuild(message.mimeTree).value; // get raw rebuilder stream
let raw = Buffer.from([], 'binary'); // set initial raw
let outputStream = this.indexer.rebuild(message.mimeTree); // get raw rebuilder response obj (.value is the stream)
if (!outputStream || outputStream.type !== 'stream' || !outputStream.value) {
return done(new Error('Cannot fetch message'));
}
outputStream = outputStream.value; // set stream to actual stream object (.value)
let chunks = [];
let chunklen = 0;
outputStream
.on('data', data => {
raw = Buffer.concat([raw, data]);
.on('readable', () => {
let chunk;
while ((chunk = outputStream.read()) !== null) {
chunks.push(chunk);
chunklen += chunk.length;
}
})
.on('end', () => {
// when done rebuilding
this.encryptMessage(res.pubKey || '', raw, (err, res) => {
const raw = Buffer.concat(chunks, chunklen);
this.encryptMessage(res.pubKey, raw, (err, res) => {
if (err) {
return done(err);
}