Skip to content

Commit fa849f2

Browse files
committed
encrypt messages copied into encrypted mailbox
1 parent 076f2c6 commit fa849f2

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

lib/handlers/on-copy.js

+90
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ async function copyHandler(server, messageHandler, connection, mailbox, update,
132132

133133
notifyLongRunning();
134134

135+
let targetMailboxEncrypted = false;
136+
137+
if (targetData.encryptMessages) {
138+
targetMailboxEncrypted = true;
139+
}
140+
135141
try {
136142
while ((messageData = await cursor.next())) {
137143
tools.checkSocket(socket); // do we even have to copy anything?
@@ -141,6 +147,12 @@ async function copyHandler(server, messageHandler, connection, mailbox, update,
141147
uid: messageData.uid,
142148
_id: messageData._id
143149
};
150+
151+
const parsedHeader = (messageData.mimeTree && messageData.mimeTree.parsedHeader) || {};
152+
const parsedContentType = parsedHeader['content-type'];
153+
154+
const isMessageEncrypted = parsedContentType ? parsedContentType.subtype === 'encrypted' : false;
155+
144156
// Copying is not done in bulk to minimize risk of going out of sync with incremental UIDs
145157
sourceUid.unshift(messageData.uid);
146158
let item = await db.database.collection('mailboxes').findOneAndUpdate(
@@ -218,6 +230,84 @@ async function copyHandler(server, messageHandler, connection, mailbox, update,
218230
{ writeConcern: 'majority' }
219231
);
220232

233+
const newPrepared = await new Promise((resolve, reject) => {
234+
if (targetMailboxEncrypted && !isMessageEncrypted && userData.pubKey) {
235+
// encrypt message
236+
const outputStream = messageHandler.indexer.rebuild(messageData.mimeTree).value; // get raw rebuilder stream
237+
let raw = Buffer.from([], 'binary'); // set initial raw
238+
239+
outputStream
240+
.on('data', data => {
241+
raw = Buffer.concat([raw, data]);
242+
})
243+
.on('end', () => {
244+
messageHandler.encryptMessages(userData.pubKey || '', raw, (err, res) => {
245+
if (err) {
246+
return reject(err);
247+
}
248+
249+
// encrypted rebuilt raw
250+
251+
if (res) {
252+
messageHandler.prepareMessage({ raw: res }, (err, prepared) => {
253+
if (err) {
254+
return reject(err);
255+
}
256+
// prepared new message structure from encrypted raw
257+
258+
const maildata = messageHandler.indexer.getMaildata(prepared.mimeTree);
259+
260+
// add attachments of encrypted messages
261+
if (maildata.attachments && maildata.attachments.length) {
262+
messageData.attachments = maildata.attachments;
263+
messageData.ha = maildata.attachments.some(a => !a.related);
264+
} else {
265+
messageData.ha = false;
266+
}
267+
268+
// remove fields that may leak data in FE or DB
269+
delete messageData.text;
270+
delete messageData.html;
271+
messageData.intro = '';
272+
273+
messageHandler.indexer.storeNodeBodies(maildata, prepared.mimeTree, err => {
274+
// store new attachments
275+
let cleanup = () => {
276+
let attachmentIds = Object.keys(prepared.mimeTree.attachmentMap || {}).map(
277+
key => prepared.mimeTree.attachmentMap[key]
278+
);
279+
280+
messageHandler.attachmentStorage.deleteMany(attachmentIds, maildata.magic);
281+
282+
if (err) {
283+
return reject(err);
284+
}
285+
};
286+
287+
if (err) {
288+
return cleanup(err);
289+
}
290+
291+
return resolve(prepared);
292+
});
293+
});
294+
}
295+
});
296+
});
297+
} else {
298+
resolve(false);
299+
}
300+
});
301+
302+
// replace fields
303+
if (newPrepared) {
304+
messageData.mimeTree = newPrepared.mimeTree;
305+
messageData.size = newPrepared.size;
306+
messageData.bodystructure = newPrepared.bodystructure;
307+
messageData.envelope = newPrepared.envelope;
308+
messageData.headers = newPrepared.headers;
309+
}
310+
221311
let r = await db.database.collection('messages').insertOne(messageData, { writeConcern: 'majority' });
222312

223313
if (!r || !r.acknowledged) {

0 commit comments

Comments
 (0)