This commit is contained in:
Andris Reinman 2017-04-11 00:36:22 +03:00
parent dcc3181891
commit 90550226c0
2 changed files with 101 additions and 13 deletions

View file

@ -38,8 +38,13 @@ class MessageHandler {
query._id = options.mailbox;
} else {
query.user = options.user;
query.path = options.path;
if (options.specialUse) {
query.specialUse = options.specialUse;
} else {
query.path = options.path;
}
}
this.database.collection('mailboxes').findOne(query, (err, mailbox) => {
if (err) {
return callback(err);
@ -55,6 +60,8 @@ class MessageHandler {
});
}
// Monster method for inserting new messages to a mailbox
// TODO: Refactor into smaller pieces
add(options, callback) {
let id = new ObjectID();
@ -113,7 +120,7 @@ class MessageHandler {
return callback(err);
}
// if a similar message already exists then delete the existing one
// if a similar message already exists then update existing one
let checkExisting = next => {
this.database.collection('messages').findOne({
mailbox: mailbox._id,
@ -125,7 +132,7 @@ class MessageHandler {
}
if (!existing) {
// nothing to do here
// nothing to do here, continue adding message
return next();
}
@ -134,18 +141,99 @@ class MessageHandler {
return callback(null, false);
}
// delete existing message
this.del({
query: {
_id: existing._id
},
mailbox,
session: options.session
}, err => {
// As duplicate message was found, update UID, MODSEQ and FLAGS
// Ensure sequential UID by locking mailbox
this.redlock.waitAcquireLock(mailbox._id.toString(), 30 * 1000, 10 * 1000, (err, lock) => {
if (err) {
return callback(err);
}
next();
if (!lock || !lock.success) {
// did not get a insert lock in 10 seconds
return callback(new Error('Failed to acquire lock'));
}
// acquire new UID+MODSEQ
this.database.collection('mailboxes').findOneAndUpdate({
_id: mailbox._id
}, {
$inc: {
// allocate bot UID and MODSEQ values so when journal is later sorted by
// modseq then UIDs are always in ascending order
uidNext: 1,
modifyIndex: 1
}
}, {
returnOriginal: true
}, (err, item) => {
if (err) {
return this.redlock.releaseLock(lock, () => callback(err));
}
if (!item || !item.value) {
// was not able to acquire a lock
let err = new Error('Mailbox is missing');
err.imapResponse = 'TRYCREATE';
return this.redlock.releaseLock(lock, () => callback(err));
}
let mailbox = item.value;
let uid = mailbox.uidNext;
let modseq = mailbox.modifyIndex + 1;
this.database.collection('messages').findOneAndUpdate({
_id: existing._id
}, {
$set: {
uid,
modseq,
flags
}
}, {
returnOriginal: false
}, (err, item) => {
if (err) {
return this.redlock.releaseLock(lock, () => callback(err));
}
if (!item || !item.value) {
// message was not found for whatever reason
return this.redlock.releaseLock(lock, next);
}
let updated = item.value;
if (options.session && options.session.selected && options.session.selected.mailbox === mailbox.path) {
options.session.writeStream.write(options.session.formatResponse('EXPUNGE', existing.uid));
}
if (options.session && options.session.selected && options.session.selected.mailbox === mailbox.path) {
options.session.writeStream.write(options.session.formatResponse('EXISTS', updated.uid));
}
this.notifier.addEntries(mailbox, false, {
command: 'EXPUNGE',
ignore: options.session && options.session.id,
uid: existing.uid,
message: existing._id
}, () => {
this.notifier.addEntries(mailbox, false, {
command: 'EXISTS',
uid: updated.uid,
ignore: options.session && options.session.id,
message: updated._id,
modseq: updated.modseq
}, () => {
this.notifier.fire(mailbox.user, mailbox.path);
return this.redlock.releaseLock(lock, () => callback(null, true, {
uidValidity: mailbox.uidValidity,
uid
}));
});
});
});
});
});
});
};

View file

@ -1,6 +1,6 @@
{
"name": "wildduck",
"version": "1.0.13",
"version": "1.0.14",
"description": "IMAP server built with Node.js and MongoDB",
"main": "server.js",
"scripts": {