mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-02-26 17:04:28 +08:00
Added support for MOVE
This commit is contained in:
parent
2aa4f79142
commit
a671df6ed4
6 changed files with 139 additions and 23 deletions
|
@ -32,6 +32,7 @@ Wild Duck IMAP server supports the following IMAP standards:
|
|||
- **UIDPLUS**
|
||||
- **SPECIAL-USE**
|
||||
- **ID**
|
||||
- **MOVE** (RFC6851)
|
||||
- **AUTHENTICATE PLAIN** and **SASL-IR**
|
||||
- **APPENDLIMIT** (RFC7889) – maximum global allowed message size is advertised in CAPABILITY listing
|
||||
- **UTF8=ACCEPT** (RFC6855) – this also means that Wild Duck natively supports unicode email usernames. For example <андрис@уайлддак.орг> is a valid email address that is hosted by a test instance of Wild Duck
|
||||
|
|
|
@ -22,7 +22,7 @@ const transporter = nodemailer.createTransport({
|
|||
});
|
||||
|
||||
let sent = 0;
|
||||
let total = 10000;
|
||||
let total = 5;
|
||||
let startTime = Date.now();
|
||||
|
||||
function send() {
|
||||
|
|
|
@ -30,7 +30,7 @@ module.exports = {
|
|||
capabilities.push('UTF8=ACCEPT');
|
||||
capabilities.push('QUOTA');
|
||||
|
||||
// capabilities.push('MOVE');
|
||||
capabilities.push('MOVE');
|
||||
|
||||
if (this._server.options.maxMessage) {
|
||||
capabilities.push('APPENDLIMIT=' + this._server.options.maxMessage);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
let imapTools = require('../imap-tools');
|
||||
let imapHandler = require('../handler/imap-handler');
|
||||
|
||||
module.exports = {
|
||||
state: 'Selected',
|
||||
|
@ -48,27 +47,12 @@ module.exports = {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
let code = typeof success === 'string' ? success.toUpperCase() : false;
|
||||
|
||||
if (success === true) {
|
||||
this.send(imapHandler.compiler({
|
||||
tag: '*',
|
||||
command: 'OK',
|
||||
attributes: [{
|
||||
type: 'SECTION',
|
||||
section: [{
|
||||
type: 'TEXT',
|
||||
value: 'COPYUID ' + info.uidValidity + ' ' + imapTools.packMessageRange(info.sourceUid) + ' ' + imapTools.packMessageRange(info.destinationUid)
|
||||
}]
|
||||
}]
|
||||
}));
|
||||
}
|
||||
let code = typeof success === 'string' ? success.toUpperCase() : 'COPYUID ' + info.uidValidity + ' ' + imapTools.packMessageRange(info.sourceUid) + ' ' + imapTools.packMessageRange(info.destinationUid);
|
||||
|
||||
callback(null, {
|
||||
response: success === true ? 'OK' : 'NO',
|
||||
code
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
135
imap.js
135
imap.js
|
@ -548,7 +548,6 @@ server.onStore = function (path, update, session, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
// EXPUNGE deletes all messages in selected mailbox marked with \Delete
|
||||
// EXPUNGE deletes all messages in selected mailbox marked with \Delete
|
||||
server.onExpunge = function (path, update, session, callback) {
|
||||
this.logger.debug('[%s] Deleting messages from "%s"', session.id, path);
|
||||
|
@ -690,7 +689,9 @@ server.onCopy = function (path, update, session, callback) {
|
|||
uid: {
|
||||
$in: update.messages
|
||||
}
|
||||
}); // no projection as we need to copy the entire message
|
||||
}).sort([
|
||||
['uid', 1]
|
||||
]); // no projection as we need to copy the entire message
|
||||
|
||||
let copiedMessages = 0;
|
||||
let copiedStorage = 0;
|
||||
|
@ -800,6 +801,136 @@ server.onCopy = function (path, update, session, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
// MOVE / UID MOVE sequence mailbox
|
||||
server.onMove = function (path, update, session, callback) {
|
||||
this.logger.debug('[%s] Moving messages from "%s" to "%s"', session.id, path, update.destination);
|
||||
db.database.collection('mailboxes').findOne({
|
||||
user: session.user.id,
|
||||
path
|
||||
}, (err, mailbox) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!mailbox) {
|
||||
return callback(null, 'NONEXISTENT');
|
||||
}
|
||||
|
||||
db.database.collection('mailboxes').findOne({
|
||||
user: session.user.id,
|
||||
path: update.destination
|
||||
}, (err, target) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!target) {
|
||||
return callback(null, 'TRYCREATE');
|
||||
}
|
||||
|
||||
let cursor = db.database.collection('messages').find({
|
||||
mailbox: mailbox._id,
|
||||
uid: {
|
||||
$in: update.messages
|
||||
}
|
||||
}).project({
|
||||
uid: 1
|
||||
}).sort([
|
||||
['uid', 1]
|
||||
]);
|
||||
|
||||
let sourceUid = [];
|
||||
let destinationUid = [];
|
||||
|
||||
let processNext = () => {
|
||||
cursor.next((err, message) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!message) {
|
||||
return cursor.close(() => {
|
||||
db.database.collection('mailboxes').findOneAndUpdate({
|
||||
_id: mailbox._id
|
||||
}, {
|
||||
$inc: {
|
||||
// increase the mailbox modification index
|
||||
// to indicate that something happened
|
||||
modifyIndex: 1
|
||||
}
|
||||
}, {
|
||||
uidNext: true
|
||||
}, () => {
|
||||
this.notifier.fire(session.user.id, target.path);
|
||||
return callback(null, true, {
|
||||
uidValidity: target.uidValidity,
|
||||
sourceUid,
|
||||
destinationUid
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sourceUid.unshift(message.uid);
|
||||
db.database.collection('mailboxes').findOneAndUpdate({
|
||||
_id: target._id
|
||||
}, {
|
||||
$inc: {
|
||||
uidNext: 1
|
||||
}
|
||||
}, {
|
||||
uidNext: true
|
||||
}, (err, item) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!item || !item.value) {
|
||||
// was not able to acquire a lock
|
||||
return callback(null, 'TRYCREATE');
|
||||
}
|
||||
|
||||
let uidNext = item.value.uidNext;
|
||||
destinationUid.unshift(uidNext);
|
||||
|
||||
// update message, change mailbox from old to new one
|
||||
db.database.collection('messages').findOneAndUpdate({
|
||||
_id: message._id
|
||||
}, {
|
||||
$set: {
|
||||
mailbox: target._id,
|
||||
// new mailbox means new UID
|
||||
uid: uidNext,
|
||||
// this will be changed later by the notification system
|
||||
modseq: 0
|
||||
}
|
||||
}, err => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
session.writeStream.write(session.formatResponse('EXPUNGE', message.uid));
|
||||
|
||||
// mark messages as deleted from old mailbox
|
||||
this.notifier.addEntries(session.user.id, path, {
|
||||
command: 'EXPUNGE',
|
||||
ignore: session.id,
|
||||
uid: message.uid
|
||||
}, () => {
|
||||
// mark messages as added to old mailbox
|
||||
this.notifier.addEntries(session.user.id, target.path, {
|
||||
command: 'EXISTS',
|
||||
uid: uidNext,
|
||||
message: message._id
|
||||
}, processNext);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
processNext();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// sends results to socket
|
||||
server.onFetch = function (path, options, session, callback) {
|
||||
this.logger.debug('[%s] Requested FETCH for "%s"', session.id, path);
|
||||
|
|
|
@ -210,10 +210,10 @@ class ImapNotifier extends EventEmitter {
|
|||
}
|
||||
|
||||
let entry = entries[updated++];
|
||||
let setModseq = !!entry.modseq;
|
||||
let setModseq = !entry.modseq;
|
||||
|
||||
entry.mailbox = mailbox._id;
|
||||
if (!setModseq) {
|
||||
if (setModseq) {
|
||||
entry.modseq = ++startIndex;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue