diff --git a/lib/handlers/on-copy.js b/lib/handlers/on-copy.js index a692396c..25a319c8 100644 --- a/lib/handlers/on-copy.js +++ b/lib/handlers/on-copy.js @@ -132,6 +132,12 @@ async function copyHandler(server, messageHandler, connection, mailbox, update, notifyLongRunning(); + let targetMailboxEncrypted = false; + + if (targetData.encryptMessages) { + targetMailboxEncrypted = true; + } + try { while ((messageData = await cursor.next())) { tools.checkSocket(socket); // do we even have to copy anything? @@ -141,6 +147,12 @@ async function copyHandler(server, messageHandler, connection, mailbox, update, uid: messageData.uid, _id: messageData._id }; + + const parsedHeader = (messageData.mimeTree && messageData.mimeTree.parsedHeader) || {}; + const parsedContentType = parsedHeader['content-type']; + + const isMessageEncrypted = parsedContentType ? parsedContentType.subtype === 'encrypted' : false; + // Copying is not done in bulk to minimize risk of going out of sync with incremental UIDs sourceUid.unshift(messageData.uid); let item = await db.database.collection('mailboxes').findOneAndUpdate( @@ -218,6 +230,84 @@ async function copyHandler(server, messageHandler, connection, mailbox, update, { writeConcern: 'majority' } ); + 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 + + outputStream + .on('data', data => { + raw = Buffer.concat([raw, data]); + }) + .on('end', () => { + messageHandler.encryptMessages(userData.pubKey || '', raw, (err, res) => { + if (err) { + return reject(err); + } + + // encrypted rebuilt raw + + if (res) { + messageHandler.prepareMessage({ raw: res }, (err, prepared) => { + if (err) { + return reject(err); + } + // prepared new message structure from encrypted raw + + const maildata = messageHandler.indexer.getMaildata(prepared.mimeTree); + + // add attachments of encrypted messages + if (maildata.attachments && maildata.attachments.length) { + messageData.attachments = maildata.attachments; + messageData.ha = maildata.attachments.some(a => !a.related); + } else { + messageData.ha = false; + } + + // remove fields that may leak data in FE or DB + delete messageData.text; + delete messageData.html; + messageData.intro = ''; + + messageHandler.indexer.storeNodeBodies(maildata, prepared.mimeTree, err => { + // store new attachments + let cleanup = () => { + let attachmentIds = Object.keys(prepared.mimeTree.attachmentMap || {}).map( + key => prepared.mimeTree.attachmentMap[key] + ); + + messageHandler.attachmentStorage.deleteMany(attachmentIds, maildata.magic); + + if (err) { + return reject(err); + } + }; + + if (err) { + return cleanup(err); + } + + return resolve(prepared); + }); + }); + } + }); + }); + } else { + resolve(false); + } + }); + + // replace fields + if (newPrepared) { + messageData.mimeTree = newPrepared.mimeTree; + messageData.size = newPrepared.size; + messageData.bodystructure = newPrepared.bodystructure; + messageData.envelope = newPrepared.envelope; + messageData.headers = newPrepared.headers; + } + let r = await db.database.collection('messages').insertOne(messageData, { writeConcern: 'majority' }); if (!r || !r.acknowledged) {