diff --git a/api.js b/api.js index dc345789..43f1905b 100644 --- a/api.js +++ b/api.js @@ -21,6 +21,7 @@ const os = require('os'); const util = require('util'); const ObjectID = require('mongodb').ObjectID; const tls = require('tls'); +const Lock = require('ioredfour'); const acmeRoutes = require('./lib/api/acme'); const usersRoutes = require('./lib/api/users'); @@ -498,6 +499,11 @@ module.exports = done => { server.loggelf = message => loggelf(message); + server.lock = new Lock({ + redis: db.redis, + namespace: 'mail' + }); + acmeRoutes(db, server); usersRoutes(db, server, userHandler); addressesRoutes(db, server, userHandler); diff --git a/imap.js b/imap.js index ddd86ea1..ab7eab63 100644 --- a/imap.js +++ b/imap.js @@ -121,7 +121,7 @@ let createInterface = (ifaceOptions, callback) => { server.lock = new Lock({ redis: db.redis, - namespace: 'imap' + namespace: 'mail' }); // setup command handlers for the server instance diff --git a/lib/api/messages.js b/lib/api/messages.js index f94d7007..cbebc31b 100644 --- a/lib/api/messages.js +++ b/lib/api/messages.js @@ -172,6 +172,25 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler) => { if (moveTo) { let info; + + let lockKey = ['mbwr', mailbox.toString()].join(':'); + + let lock; + + try { + lock = await server.lock.waitAcquireLock(lockKey, 5 * 60 * 1000, 1 * 60 * 1000); + if (!lock.success) { + throw new Error('Failed to get folder write lock'); + } + } catch (err) { + res.status(500); + res.json({ + error: err.message, + code: err.code || 'LockFail' + }); + return next(); + } + try { let data = await moveMessage({ user, @@ -188,6 +207,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler) => { code: err.code }); return next(); + } finally { + await server.lock.releaseLock(lock); } if (!info || !info.destinationUid || !info.destinationUid.length) { @@ -204,6 +225,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler) => { mailbox: moveTo, id: info && info.sourceUid && info.sourceUid.map((uid, i) => [uid, info.destinationUid && info.destinationUid[i]]) }); + return next(); }