diff --git a/lib/imap.js b/lib/imap.js index 36350db..b3829b6 100644 --- a/lib/imap.js +++ b/lib/imap.js @@ -861,8 +861,20 @@ ImapConnection.prototype.status = function(boxName, cb) { this._send(cmd, cb); }; -ImapConnection.prototype.removeDeleted = function(cb) { - this._send('EXPUNGE', cb); +ImapConnection.prototype.removeDeleted = function(uids, cb) { + if (typeof uids === 'function') { + cb = uids; + uids = undefined; + } + if (uids !== undefined) { + if (!Array.isArray(uids)) + uids = [uids]; + + utils.validateUIDList(uids); + + this._send('UID EXPUNGE ' + uids.join(','), cb); + } else + this._send('EXPUNGE', cb); }; ImapConnection.prototype.getBoxes = function(namespace, cb) { @@ -1343,51 +1355,75 @@ ImapConnection.prototype._move = function(which, uids, boxTo, cb) { var self = this; if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); - if (this._state.box.permFlags.indexOf('deleted') === -1) { + + if (this.serverSupports('MOVE')) { + if (!Array.isArray(uids)) + uids = [uids]; + + utils.validateUIDList(uids); + + this._send(which + 'MOVE ' + uids.join(',') + ' "' + + utils.escape(utf7.encode(''+boxTo)) + '"', cb); + } else if (this._state.box.permFlags.indexOf('deleted') === -1) { throw new Error('Cannot move message: ' + 'server does not allow deletion of messages'); } else { - this._copy(which, uids, boxTo, - function ccb(err, info, reentryCount, deletedUIDs, counter) { - if (err) - return cb(err, info); - - counter = counter || 0; - // Make sure we don't expunge any messages marked as Deleted except the - // one we are moving - if (reentryCount === undefined) { - self.search(['DELETED'], function(e, result) { - ccb(e, info, 1, result); - }); - } else if (reentryCount === 1) { - if (counter < deletedUIDs.length) { - self.delFlags(deletedUIDs[counter], 'Deleted', function(e) { - process.nextTick(function() { - ccb(e, info, reentryCount, deletedUIDs, counter + 1); - }); - }); - } else - ccb(err, info, reentryCount + 1, deletedUIDs); - } else if (reentryCount === 2) { - self.addFlags(uids, 'Deleted', function(e) { - ccb(e, info, reentryCount + 1, deletedUIDs); + var deletedUIDs, task = 0; + this._copy(which, uids, boxTo, function ccb(err, info) { + if (err) + return cb(err, info); + + if (task === 0 && which && self.serverSupports('UIDPLUS')) { + // UIDPLUS gives us a 'UID EXPUNGE n' command to expunge a subset of + // messages with the \Deleted flag set. This allows us to skip some + // actions. + task = 2; + } + // Make sure we don't expunge any messages marked as Deleted except the + // one we are moving + if (task === 0) { + self.search(['DELETED'], function(e, result) { + ++task; + deletedUIDs = result; + ccb(e, info); + }); + } else if (task === 1) { + if (deletedUIDs.length) { + self.delFlags(deletedUIDs, 'Deleted', function(e) { + ++task; + ccb(e, info); }); - } else if (reentryCount === 3) { + } else { + ++task; + ccb(err, info); + } + } else if (task === 2) { + function cbMarkDel(e) { + ++task; + ccb(e, info); + } + if (which) + self.addFlags(uids, 'Deleted', cbMarkDel); + else + self.seq.addFlags(uids, 'Deleted', cbMarkDel); + } else if (task === 3) { + if (which && self.serverSupports('UIDPLUS')) + self.removeDeleted(uids, cb); + else { self.removeDeleted(function(e) { - ccb(e, info, reentryCount + 1, deletedUIDs); + ++task; + ccb(e, info); }); - } else if (reentryCount === 4) { - if (counter < deletedUIDs.length) { - self.addFlags(deletedUIDs[counter], 'Deleted', function(e) { - process.nextTick(function() { - ccb(e, info, reentryCount, deletedUIDs, counter + 1); - }); - }); - } else - cb(err, info); } + } else if (task === 4) { + if (deletedUIDs.length) { + self.addFlags(deletedUIDs, 'Deleted', function(e) { + cb(e, info); + }); + } else + cb(err, info); } - ); + }); } };