Merge branch 'upstream' into fix-empty-boxname

Conflicts:
	lib/imap.js
fork
Chotiwat Chawannakul 11 years ago
commit 73c7d4cea9

@ -22,19 +22,26 @@ var CRLF = '\r\n',
BOXSELECTING: 3, BOXSELECTING: 3,
BOXSELECTED: 4 BOXSELECTED: 4
}, },
RE_LITHEADER = /(?:((?:BODY\[.*\](?:<\d+>)?)?|[^ ]+) )?\{(\d+)\}$/i, RE_LITHEADER = /(?:((?:BODY\[.*\](?:<\d+>)?)?|[^ ]+) )?\{(\d+)\}(?:$|\r\n)/i,
RE_UNRESP = /^\* (OK|PREAUTH|NO|BAD) (?:\[(.+)\] )?(.+)$/i, RE_UNRESP = /^\* (OK|PREAUTH|NO|BAD)(?:\r\n|(?: \[(.+?)\])?(?: (.+))?)(?:$|\r\n)/i,
RE_TAGGED_RESP = /^A\d+ (OK|NO|BAD) (?:\[(.+?)\] )?(.+)(?:$|\r\n)/i,
RE_TEXT_CODE = /([^ ]+)(?: (.*))?$/,
RE_RES_IDLE = /^IDLE /i,
RE_RES_NOOP = /^NOOP /i,
RE_CMD_FETCH = /^(?:UID )?FETCH/i,
RE_PARTID = /^(?:[\d]+[\.]{0,1})*[\d]+$/,
RE_ESCAPE = /\\\\/g,
//RE_ISPARTIAL = /<(\d+)>$/, //RE_ISPARTIAL = /<(\d+)>$/,
RE_DBLQ = /"/g, RE_DBLQ = /"/g,
RE_CMD = /^([^ ]+)(?: |$)/, RE_CMD = /^([^ ]+)(?: |$)/,
RE_ISHEADER = /HEADER/, RE_ISHEADER = /HEADER/,
REX_UNRESPDATA = XRegExp('^\\* (?:(?:(?<type>NAMESPACE) (?<personal>(?:NIL|\\((?:\\(.+\\))+\\))) (?<other>(?:NIL|\\((?:\\(.+\\))+\\))) (?<shared>(?:NIL|\\((?:\\(.+\\))+\\))))|(?:(?<type>FLAGS) \\((?<flags>.*)\\))|(?:(?<type>LIST|LSUB|XLIST) \\((?<flags>.*)\\) (?<delimiter>"[^"]+"|NIL) (?<mailbox>.+))|(?:(?<type>(SEARCH|SORT))(?: (?<results>.*))?)|(?:(?<type>STATUS) (?<mailbox>.+) \\((?<attributes>.*)\\))|(?:(?<type>CAPABILITY) (?<capabilities>.+))|(?:(?<type>BYE) (?:\\[(?<code>.+)\\] )?(?<message>.+)))[ \t]*(?:\r\n|$)', 'i'), REX_UNRESPDATA = XRegExp('^\\* (?:(?:(?<type>NAMESPACE) (?<personal>(?:NIL|\\((?:\\(.+\\))+\\))) (?<other>(?:NIL|\\((?:\\(.+\\))+\\))) (?<shared>(?:NIL|\\((?:\\(.+\\))+\\))))|(?:(?<type>FLAGS) \\((?<flags>.*)\\))|(?:(?<type>LIST|LSUB|XLIST) \\((?<flags>.*)\\) (?<delimiter>"[^"]+"|NIL) (?<mailbox>.+))|(?:(?<type>(SEARCH|SORT))(?: (?<results>.*))?)|(?:(?<type>STATUS) (?<mailbox>.+) \\((?<attributes>.*)\\))|(?:(?<type>CAPABILITY) (?<capabilities>.+))|(?:(?<type>BYE) (?:\\[(?<code>.+)\\] )?(?<message>.+)))[ \t]*(?:$|\r\n)', 'i'),
REX_UNRESPNUM = XRegExp('^\\* (?<num>\\d+) (?:(?<type>EXISTS)|(?<type>RECENT)|(?<type>EXPUNGE)|(?:(?<type>FETCH) \\((?<info>.*)\\)))[ \t]*(?:\r\n|$)', 'i'); REX_UNRESPNUM = XRegExp('^\\* (?<num>\\d+) (?:(?<type>EXISTS)|(?<type>RECENT)|(?<type>EXPUNGE)|(?:(?<type>FETCH) \\((?<info>.*)\\)))[ \t]*(?:$|\r\n)', 'i');
// extension constants // extension constants
var IDLE_NONE = 1, var IDLE_NONE = 1,
IDLE_WAIT = 2, IDLE_WAIT = 2,
IDLE_READY = 3, IDLE_IDLING = 3,
IDLE_DONE = 4; IDLE_DONE = 4;
function ImapConnection(options) { function ImapConnection(options) {
@ -47,7 +54,9 @@ function ImapConnection(options) {
password: options.password || '', password: options.password || '',
host: options.host || 'localhost', host: options.host || 'localhost',
port: options.port || 143, port: options.port || 143,
secure: options.secure || false, secure: options.secure === true ? { // secure = true means default behavior
rejectUnauthorized: false // Force pre-node-0.9.2 behavior
} : (options.secure || false),
connTimeout: options.connTimeout || 10000, // connection timeout in msecs connTimeout: options.connTimeout || 10000, // connection timeout in msecs
xoauth: options.xoauth, xoauth: options.xoauth,
xoauth2: options.xoauth2 xoauth2: options.xoauth2
@ -87,9 +96,8 @@ function ImapConnection(options) {
ext: { ext: {
// Capability-specific state info // Capability-specific state info
idle: { idle: {
MAX_WAIT: 1740000, // 29 mins in ms MAX_WAIT: 300000, // 5 mins in ms
state: IDLE_NONE, state: IDLE_NONE,
reIDLE: false,
timeStarted: undefined timeStarted: undefined
} }
} }
@ -124,10 +132,14 @@ ImapConnection.prototype.connect = function(loginCb) {
socket.setTimeout(0); socket.setTimeout(0);
if (this._options.secure) { if (this._options.secure) {
var tlsOptions = {};
for (var k in this._options.secure)
tlsOptions[k] = this._options.secure[k];
tlsOptions.socket = state.conn;
if (process.version.indexOf('v0.6.') > -1) if (process.version.indexOf('v0.6.') > -1)
socket = tls.connect(null, { socket: state.conn }, onconnect); socket = tls.connect(null, tlsOptions, onconnect);
else else
socket = tls.connect({ socket: state.conn }, onconnect); socket = tls.connect(tlsOptions, onconnect);
} else } else
state.conn.once('connect', onconnect); state.conn.once('connect', onconnect);
@ -156,7 +168,7 @@ ImapConnection.prototype.connect = function(loginCb) {
self.emit('close', had_error); self.emit('close', had_error);
}); });
state.conn.on('error', function(err) { socket.on('error', function(err) {
clearTimeout(state.tmrConn); clearTimeout(state.tmrConn);
err.level = 'socket'; err.level = 'socket';
if (state.status === STATES.NOCONNECT) if (state.status === STATES.NOCONNECT)
@ -174,7 +186,7 @@ ImapConnection.prototype.connect = function(loginCb) {
return loginCb(err); return loginCb(err);
} }
// Next, get the list of available namespaces if supported (RFC2342) // Next, get the list of available namespaces if supported (RFC2342)
if (!checkedNS && self._serverSupports('NAMESPACE')) { if (!checkedNS && self.serverSupports('NAMESPACE')) {
// Re-enter this function after we've obtained the available // Re-enter this function after we've obtained the available
// namespaces // namespaces
checkedNS = true; checkedNS = true;
@ -299,9 +311,9 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
if (indata.line[0] === '*') { // Untagged server response if (indata.line[0] === '*') { // Untagged server response
var isUnsolicited = var isUnsolicited = (requests[0] && requests[0].cmd === 'NOOP')
(requests[0] && requests[0].cmd === 'NOOP') || (state.isIdle && state.ext.idle.state !== IDLE_NONE)
|| (state.isIdle && state.ext.idle.state === IDLE_READY); || !requests.length;
if (m = XRegExp.exec(indata.line, REX_UNRESPNUM)) { if (m = XRegExp.exec(indata.line, REX_UNRESPNUM)) {
// m.type = response type (numeric-based) // m.type = response type (numeric-based)
m.type = m.type.toUpperCase(); m.type = m.type.toUpperCase();
@ -311,12 +323,24 @@ ImapConnection.prototype.connect = function(loginCb) {
// m.info = message details // m.info = message details
var data, parsed, headers, f, lenf, body, lenb, msg, bodies, var data, parsed, headers, f, lenf, body, lenb, msg, bodies,
details, val; details, val;
isUnsolicited = isUnsolicited
|| (requests[0]
&& !RE_CMD_FETCH.test(requests[0].cmdstr));
if (!isUnsolicited) if (!isUnsolicited)
bodies = parsers.parseFetchBodies(m.info, indata.literals); bodies = parsers.parseFetchBodies(m.info, indata.literals);
details = new ImapMessage(); details = new ImapMessage();
parsers.parseFetch(m.info, indata.literals, details); parsers.parseFetch(m.info, indata.literals, details);
details.seqno = parseInt(m.num, 10); details.seqno = parseInt(m.num, 10);
if (typeof details['x-gm-labels'] !== undefined) {
var labels = details['x-gm-labels'];
for (var i=0, len=labels.length; i<len; ++i)
labels[i] = labels[i].replace(RE_ESCAPE, '\\');
}
if (isUnsolicited) if (isUnsolicited)
self.emit('msgupdate', details); self.emit('msgupdate', details);
else { else {
@ -531,23 +555,31 @@ ImapConnection.prototype.connect = function(loginCb) {
self.debug&&self.debug('[parsing incoming] saw untagged ' + m[1]); self.debug&&self.debug('[parsing incoming] saw untagged ' + m[1]);
switch (m[1]) { switch (m[1]) {
case 'OK': case 'OK':
var code = m[2]; if (m[2] === undefined && m[3] === undefined)
break;
var code, codeval;
if (m[2]) {
code = RE_TEXT_CODE.exec(m[2]);
codeval = code[2];
code = code[1].toUpperCase();
}
if (state.status === STATES.NOAUTH) { if (state.status === STATES.NOAUTH) {
if (!state.isReady) { if (!state.isReady) {
clearTimeout(state.tmrConn); clearTimeout(state.tmrConn);
state.isReady = true; state.isReady = true;
state.conn.emit('ready'); state.conn.emit('ready');
} }
} else if (/^ALERT$/i.test(code)) } else if (code === 'ALERT')
self.emit('alert', m[3]); self.emit('alert', m[3]);
else if (state.status === STATES.BOXSELECTING) { else if (state.status === STATES.BOXSELECTING) {
if (m = /^UIDVALIDITY (\d+)/i.exec(code)) if (code === 'UIDVALIDITY')
state.box.uidvalidity = parseInt(m[1], 10); state.box.uidvalidity = parseInt(codeval, 10);
else if (m = /^UIDNEXT (\d+)/i.exec(code)) else if (code === 'UIDNEXT')
state.box.uidnext = parseInt(m[1], 10); state.box.uidnext = parseInt(codeval, 10);
else if (m = /^PERMANENTFLAGS \((.*)\)/i.exec(code)) { else if (code === 'PERMANENTFLAGS') {
var idx, permFlags, keywords; var idx, permFlags, keywords;
state.box.permFlags = permFlags = m[1].split(' '); codeval = codeval.substr(1, codeval.length - 2);
state.box.permFlags = permFlags = codeval.split(' ');
if ((idx = state.box.permFlags.indexOf('\\*')) > -1) { if ((idx = state.box.permFlags.indexOf('\\*')) > -1) {
state.box.newKeywords = true; state.box.newKeywords = true;
permFlags.splice(idx, 1); permFlags.splice(idx, 1);
@ -562,8 +594,8 @@ ImapConnection.prototype.connect = function(loginCb) {
}); });
} }
} else if (state.status === STATES.BOXSELECTED) { } else if (state.status === STATES.BOXSELECTED) {
if (m = /^UIDVALIDITY (\d+)/i.exec(code)) { if (code === 'UIDVALIDITY') {
state.box.uidvalidity = parseInt(m[1], 10); state.box.uidvalidity = parseInt(codeval, 10);
self.emit('uidvalidity', state.box.uidvalidity); self.emit('uidvalidity', state.box.uidvalidity);
} }
} }
@ -608,18 +640,21 @@ ImapConnection.prototype.connect = function(loginCb) {
indata.temp = undefined; indata.temp = undefined;
indata.streaming = false; indata.streaming = false;
indata.expect = -1; indata.expect = -1;
self.debug&&self.debug(line[0] === 'A' self.debug&&self.debug(line[0] === 'A'
? '[parsing incoming] saw tagged response' ? '[parsing incoming] saw tagged response'
: '[parsing incoming] saw continuation response'); : '[parsing incoming] saw continuation response');
clearTimeout(state.tmrKeepalive);
if (line[0] === '+' && state.ext.idle.state === IDLE_WAIT) { if (line[0] === '+' && state.ext.idle.state === IDLE_WAIT) {
state.ext.idle.state = IDLE_READY; state.ext.idle.state = IDLE_IDLING;
state.ext.idle.timeStarted = Date.now(); state.ext.idle.timeStarted = Date.now();
doKeepaliveTimer();
return process.nextTick(function() { self._send(); }); return process.nextTick(function() { self._send(); });
} }
var sendBox = false; var sendBox = false;
clearTimeout(state.tmrKeepalive);
if (state.status === STATES.BOXSELECTING) { if (state.status === STATES.BOXSELECTING) {
if (/^A\d+ OK/i.test(line)) { if (/^A\d+ OK/i.test(line)) {
sendBox = true; sendBox = true;
@ -630,7 +665,6 @@ ImapConnection.prototype.connect = function(loginCb) {
self._resetBox(); self._resetBox();
} }
} }
if (requests[0].cmd === 'RENAME') { if (requests[0].cmd === 'RENAME') {
if (state.box._newName) { if (state.box._newName) {
state.box.name = state.box._newName; state.box.name = state.box._newName;
@ -640,18 +674,24 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
if (typeof requests[0].callback === 'function') { if (typeof requests[0].callback === 'function') {
m = RE_TAGGED_RESP.exec(line);
var err = null; var err = null;
var args = requests[0].cbargs, var args = requests[0].cbargs,
cmdstr = requests[0].cmdstr; cmdstr = requests[0].cmdstr;
if (line[0] === '+') { if (!m) {
if (requests[0].cmd !== 'APPEND') { if (requests[0].cmd === 'APPEND')
err = new Error('Unexpected continuation'); return requests[0].callback();
else {
var isXOAuth2 = (cmdstr.indexOf('AUTHENTICATE XOAUTH2') === 0),
msg = (isXOAuth2
? new Buffer(line.substr(2), 'base64').toString('utf8')
: 'Unexpected continuation');
err = new Error(msg);
err.level = 'protocol'; err.level = 'protocol';
err.type = 'continuation'; err.type = (isXOAuth2 ? 'failure' : 'continuation');
err.request = cmdstr; err.request = cmdstr;
} else }
return requests[0].callback(); } else if (m[1] !== 'OK') {
} else if (m = /^A\d+ (NO|BAD) (?:\[(.+?)\] )?(.+)$/i.exec(line)) {
// m[1]: error type // m[1]: error type
// m[2]: resp-text-code // m[2]: resp-text-code
// m[3]: message // m[3]: message
@ -672,45 +712,50 @@ ImapConnection.prototype.connect = function(loginCb) {
) && args.length === 0) ) && args.length === 0)
args.unshift([]); args.unshift([]);
} }
if (m) {
var msg = m[3], info;
if (m[2]) {
m = RE_TEXT_CODE.exec(m[2]);
info = {
code: m[1].toUpperCase(),
codeval: m[2],
message: msg
};
} else
info = { message: msg };
args.push(info);
}
args.unshift(err); args.unshift(err);
requests[0].callback.apply(self, args); requests[0].callback.apply(self, args);
} }
var recentCmd = requests[0].cmdstr; var recentCmd = requests[0].cmd;
requests.shift(); requests.shift();
if (requests.length === 0 && recentCmd !== 'LOGOUT') {
if (state.status >= STATES.AUTH && self._serverSupports('IDLE')) { if (!requests.length && recentCmd !== 'LOGOUT')
// According to RFC 2177, we should re-IDLE at least every 29 doKeepalive();
// minutes to avoid disconnection by the server else
self._send('IDLE', undefined, true);
}
state.tmrKeepalive = setTimeout(function idleHandler() {
if (state.isIdle) {
if (state.ext.idle.state === IDLE_READY) {
state.tmrKeepalive = setTimeout(idleHandler, state.tmoKeepalive);
var timeDiff = Date.now() - state.ext.idle.timeStarted;
if (timeDiff >= state.ext.idle.MAX_WAIT)
self._send('IDLE', undefined, true); // restart IDLE
} else if (!self._serverSupports('IDLE'))
self._noop();
}
}, state.tmoKeepalive);
} else
process.nextTick(function() { self._send(); }); process.nextTick(function() { self._send(); });
state.isIdle = true; state.isIdle = true;
} else if (/^IDLE /i.test(indata.line)) { } else if (RE_RES_IDLE.test(indata.line)) {
self.debug&&self.debug('[parsing incoming] saw IDLE'); self.debug&&self.debug('[parsing incoming] saw IDLE');
if (requests.length) requests.shift(); // remove IDLE request
process.nextTick(function() { self._send(); });
state.isIdle = false;
state.ext.idle.state = IDLE_NONE;
state.ext.idle.timeStated = undefined;
indata.line = undefined; indata.line = undefined;
if (state.ext.idle.reIDLE) { state.ext.idle.state = IDLE_NONE;
state.ext.idle.reIDLE = false; state.ext.idle.timeStarted = undefined;
self._send('IDLE', undefined, true); if (requests.length) {
} state.isIdle = false;
self._send();
} else
doKeepalive();
} else if (RE_RES_NOOP.test(indata.line)) {
self.debug&&self.debug('[parsing incoming] saw NOOP');
requests.shift(); // remove NOOP request
if (!requests.length)
doKeepaliveTimer();
else
self._send();
} else { } else {
// unknown response // unknown response
self.debug&&self.debug('[parsing incoming] saw unexpected response: ' self.debug&&self.debug('[parsing incoming] saw unexpected response: '
@ -719,6 +764,31 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
} }
function doKeepalive() {
if (state.status >= STATES.AUTH) {
if (self.serverSupports('IDLE'))
self._send('IDLE');
else
self._noop();
}
}
function doKeepaliveTimer() {
state.tmrKeepalive = setTimeout(function idleHandler() {
if (state.isIdle) {
if (state.ext.idle.state === IDLE_IDLING) {
var timeDiff = Date.now() - state.ext.idle.timeStarted;
if (timeDiff >= state.ext.idle.MAX_WAIT) {
state.ext.idle.state = IDLE_DONE;
self._send('DONE');
} else
state.tmrKeepalive = setTimeout(idleHandler, state.tmoKeepalive);
} else if (!self.serverSupports('IDLE'))
doKeepalive();
}
}, state.tmoKeepalive);
}
state.conn.connect(this._options.port, this._options.host); state.conn.connect(this._options.port, this._options.host);
state.tmrConn = setTimeout(function() { state.tmrConn = setTimeout(function() {
@ -796,7 +866,7 @@ ImapConnection.prototype.getBoxes = function(namespace, cb) {
cb = namespace; cb = namespace;
namespace = ''; namespace = '';
} }
this._send((!this._serverSupports('XLIST') ? 'LIST' : 'XLIST') this._send((!this.serverSupports('XLIST') ? 'LIST' : 'XLIST')
+ ' "' + utils.escape(utf7.encode(''+namespace)) + '" "*"', cb); + ' "' + utils.escape(utf7.encode(''+namespace)) + '" "*"', cb);
}; };
@ -837,6 +907,7 @@ ImapConnection.prototype.append = function(data, options, cb) {
if (options.flags) { if (options.flags) {
if (!Array.isArray(options.flags)) if (!Array.isArray(options.flags))
options.flags = [options.flags]; options.flags = [options.flags];
if (options.flags.length > 0)
cmd += " (\\" + options.flags.join(' \\') + ")"; cmd += " (\\" + options.flags.join(' \\') + ")";
} }
if (options.date) { if (options.date) {
@ -863,9 +934,9 @@ ImapConnection.prototype.append = function(data, options, cb) {
cmd += (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data)); cmd += (Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data));
cmd += '}'; cmd += '}';
var self = this, step = 1; var self = this, step = 1;
this._send(cmd, function(err) { this._send(cmd, function(err, info) {
if (err || step++ === 2) if (err || step++ === 2)
return cb(err); return cb(err, info);
self._state.conn.write(data); self._state.conn.write(data);
self._state.conn.write(CRLF); self._state.conn.write(CRLF);
self.debug&&self.debug('\n==> ' + inspect(data.toString()) + '\n'); self.debug&&self.debug('\n==> ' + inspect(data.toString()) + '\n');
@ -896,7 +967,7 @@ ImapConnection.prototype._sort = function(which, sorts, options, cb) {
throw new Error('Expected array with at least one sort criteria'); throw new Error('Expected array with at least one sort criteria');
if (!Array.isArray(options)) if (!Array.isArray(options))
throw new Error('Expected array for search options'); throw new Error('Expected array for search options');
if (!this._serverSupports('SORT')) if (!this.serverSupports('SORT'))
return cb(new Error('Sorting is not supported on the server')); return cb(new Error('Sorting is not supported on the server'));
var criteria = sorts.map(function(criterion) { var criteria = sorts.map(function(criterion) {
@ -991,9 +1062,9 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
for (var i = 0, wp, pprefix, len = what.length; i < len; ++i) { for (var i = 0, wp, pprefix, len = what.length; i < len; ++i) {
wp = what[i]; wp = what[i];
parse = true; parse = true;
if (wp.id !== undefined && !/^(?:[\d]+[\.]{0,1})*[\d]+$/.test(''+wp.id)) if (wp.id !== undefined && !RE_PARTID.test(''+wp.id))
throw new Error('Invalid part id: ' + wp.id); throw new Error('Invalid part id: ' + wp.id);
if (( (wp.headers if (( (typeof wp.headers === 'object'
&& (!wp.headers.fields && (!wp.headers.fields
|| (Array.isArray(wp.headers.fields) || (Array.isArray(wp.headers.fields)
&& wp.headers.fields.length === 0) && wp.headers.fields.length === 0)
@ -1001,7 +1072,7 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
&& wp.headers.parse === false && wp.headers.parse === false
) )
|| ||
(wp.headersNot (typeof wp.headersNot === 'object'
&& (!wp.headersNot.fields && (!wp.headersNot.fields
|| (Array.isArray(wp.headersNot.fields) || (Array.isArray(wp.headersNot.fields)
&& wp.headersNot.fields.length === 0) && wp.headersNot.fields.length === 0)
@ -1155,7 +1226,7 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
} }
// always fetch GMail-specific bits of information when on GMail // always fetch GMail-specific bits of information when on GMail
if (this._serverSupports('X-GM-EXT-1')) if (this.serverSupports('X-GM-EXT-1'))
extensions = 'X-GM-THRID X-GM-MSGID X-GM-LABELS '; extensions = 'X-GM-THRID X-GM-MSGID X-GM-LABELS ';
var cmd = which; var cmd = which;
@ -1233,7 +1304,7 @@ ImapConnection.prototype.delLabels = function(uids, labels, cb) {
}; };
ImapConnection.prototype._storeLabels = function(which, uids, labels, mode, cb) { ImapConnection.prototype._storeLabels = function(which, uids, labels, mode, cb) {
if (!this._serverSupports('X-GM-EXT-1')) if (!this.serverSupports('X-GM-EXT-1'))
throw new Error('Server must support X-GM-EXT-1 capability'); throw new Error('Server must support X-GM-EXT-1 capability');
if (this._state.status !== STATES.BOXSELECTED) if (this._state.status !== STATES.BOXSELECTED)
throw new Error('No mailbox is currently selected'); throw new Error('No mailbox is currently selected');
@ -1284,46 +1355,47 @@ ImapConnection.prototype._move = function(which, uids, boxTo, cb) {
throw new Error('Cannot move message: ' throw new Error('Cannot move message: '
+ 'server does not allow deletion of messages'); + 'server does not allow deletion of messages');
} else { } else {
this._copy(which, uids, boxTo, function ccb(err, reentryCount, deletedUIDs, this._copy(which, uids, boxTo,
counter) { function ccb(err, info, reentryCount, deletedUIDs, counter) {
if (err) if (err)
return cb(err); return cb(err, info);
counter = counter || 0; counter = counter || 0;
// Make sure we don't expunge any messages marked as Deleted except the // Make sure we don't expunge any messages marked as Deleted except the
// one we are moving // one we are moving
if (reentryCount === undefined) { if (reentryCount === undefined) {
self.search(['DELETED'], function(e, result) { self.search(['DELETED'], function(e, result) {
ccb(e, 1, result); ccb(e, info, 1, result);
}); });
} else if (reentryCount === 1) { } else if (reentryCount === 1) {
if (counter < deletedUIDs.length) { if (counter < deletedUIDs.length) {
self.delFlags(deletedUIDs[counter], 'Deleted', function(e) { self.delFlags(deletedUIDs[counter], 'Deleted', function(e) {
process.nextTick(function() { process.nextTick(function() {
ccb(e, reentryCount, deletedUIDs, counter + 1); ccb(e, info, reentryCount, deletedUIDs, counter + 1);
}); });
}); });
} else } else
ccb(err, reentryCount + 1, deletedUIDs); ccb(err, info, reentryCount + 1, deletedUIDs);
} else if (reentryCount === 2) { } else if (reentryCount === 2) {
self.addFlags(uids, 'Deleted', function(e) { self.addFlags(uids, 'Deleted', function(e) {
ccb(e, reentryCount + 1, deletedUIDs); ccb(e, info, reentryCount + 1, deletedUIDs);
}); });
} else if (reentryCount === 3) { } else if (reentryCount === 3) {
self.removeDeleted(function(e) { self.removeDeleted(function(e) {
ccb(e, reentryCount + 1, deletedUIDs); ccb(e, info, reentryCount + 1, deletedUIDs);
}); });
} else if (reentryCount === 4) { } else if (reentryCount === 4) {
if (counter < deletedUIDs.length) { if (counter < deletedUIDs.length) {
self.addFlags(deletedUIDs[counter], 'Deleted', function(e) { self.addFlags(deletedUIDs[counter], 'Deleted', function(e) {
process.nextTick(function() { process.nextTick(function() {
ccb(e, reentryCount, deletedUIDs, counter + 1); ccb(e, info, reentryCount, deletedUIDs, counter + 1);
}); });
}); });
} else } else
cb(); cb(err, info);
} }
}); }
);
} }
}; };
@ -1372,7 +1444,7 @@ ImapConnection.prototype.__defineGetter__('seq', function() {
// Private/Internal Functions // Private/Internal Functions
ImapConnection.prototype._serverSupports = function(capability) { ImapConnection.prototype.serverSupports = function(capability) {
return (this.capabilities.indexOf(capability) > -1); return (this.capabilities.indexOf(capability) > -1);
}; };
@ -1435,13 +1507,13 @@ ImapConnection.prototype._login = function(cb) {
}; };
if (this._state.status === STATES.NOAUTH) { if (this._state.status === STATES.NOAUTH) {
if (this._serverSupports('LOGINDISABLED')) if (this.serverSupports('LOGINDISABLED'))
return cb(new Error('Logging in is disabled on this server')); return cb(new Error('Logging in is disabled on this server'));
if (this._serverSupports('AUTH=XOAUTH') && this._options.xoauth) { if (this.serverSupports('AUTH=XOAUTH') && this._options.xoauth) {
this._send('AUTHENTICATE XOAUTH ' + utils.escape(this._options.xoauth), this._send('AUTHENTICATE XOAUTH ' + utils.escape(this._options.xoauth),
fnReturn); fnReturn);
} else if (this._serverSupports('AUTH=XOAUTH2') && this._options.xoauth2) { } else if (this.serverSupports('AUTH=XOAUTH2') && this._options.xoauth2) {
this._send('AUTHENTICATE XOAUTH2 ' + utils.escape(this._options.xoauth2), this._send('AUTHENTICATE XOAUTH2 ' + utils.escape(this._options.xoauth2),
fnReturn); fnReturn);
} else if (this._options.username && this._options.password) { } else if (this._options.username && this._options.password) {
@ -1467,7 +1539,6 @@ ImapConnection.prototype._reset = function() {
this._state.tmrConn = null; this._state.tmrConn = null;
this._state.ext.idle.state = IDLE_NONE; this._state.ext.idle.state = IDLE_NONE;
this._state.ext.idle.timeStarted = undefined; this._state.ext.idle.timeStarted = undefined;
this._state.ext.idle.reIDLE = false;
this._state.indata.literals = []; this._state.indata.literals = [];
this._state.indata.line = undefined; this._state.indata.line = undefined;
@ -1503,47 +1574,59 @@ ImapConnection.prototype._noop = function() {
this._send('NOOP'); this._send('NOOP');
}; };
ImapConnection.prototype._send = function(cmdstr, cb, bypass) { ImapConnection.prototype._send = function(cmdstr, cb) {
if (!this._state.conn.writable) if (!this._state.conn.writable)
return; return;
if (cmdstr !== undefined && !bypass) { var reqs = this._state.requests, idle = this._state.ext.idle;
this._state.requests.push({
if (cmdstr !== undefined) {
var info = {
cmd: cmdstr.match(RE_CMD)[1], cmd: cmdstr.match(RE_CMD)[1],
cmdstr: cmdstr, cmdstr: cmdstr,
callback: cb, callback: cb,
cbargs: [] cbargs: []
}); };
if (cmdstr === 'IDLE' || cmdstr === 'DONE' || cmdstr === 'NOOP')
reqs.unshift(info);
else
reqs.push(info);
}
if (idle.state !== IDLE_NONE && cmdstr !== 'DONE') {
if ((cmdstr !== undefined || reqs.length > 1)
&& idle.state === IDLE_IDLING) {
idle.state = IDLE_DONE;
this._send('DONE');
} }
if (this._state.ext.idle.state === IDLE_WAIT
|| (this._state.ext.idle.state === IDLE_DONE && cmdstr !== 'DONE'))
return; return;
if ((cmdstr === undefined && this._state.requests.length) }
|| this._state.requests.length === 1 || bypass) {
var prefix = '', if ((cmdstr === undefined && reqs.length) || reqs.length === 1
cmd = (bypass ? cmdstr : this._state.requests[0].cmdstr); || cmdstr === 'DONE') {
var prefix = '', curReq = reqs[0];
cmdstr = curReq.cmdstr;
clearTimeout(this._state.tmrKeepalive); clearTimeout(this._state.tmrKeepalive);
if (this._state.ext.idle.state === IDLE_READY && cmd !== 'DONE') {
this._state.ext.idle.state = IDLE_DONE; if (cmdstr === 'IDLE') {
if (cmd === 'IDLE')
this._state.ext.idle.reIDLE = true;
return this._send('DONE', undefined, true);
} else if (cmd === 'IDLE') {
// we use a different prefix to differentiate and disregard the tagged // we use a different prefix to differentiate and disregard the tagged
// response the server will send us when we issue DONE // response the server will send us when we issue DONE
prefix = 'IDLE '; prefix = 'IDLE ';
this._state.ext.idle.state = IDLE_WAIT; this._state.ext.idle.state = IDLE_WAIT;
} } else if (cmdstr === 'NOOP')
if (cmd !== 'IDLE' && cmd !== 'DONE') prefix = 'NOOP ';
else if (cmdstr !== 'DONE')
prefix = 'A' + (++this._state.curId) + ' '; prefix = 'A' + (++this._state.curId) + ' ';
this._state.conn.write(prefix);
this._state.conn.write(cmd); this._state.conn.write(prefix + cmdstr + CRLF);
this._state.conn.write(CRLF); this.debug&&this.debug('\n==> ' + prefix + cmdstr + '\n');
this.debug&&this.debug('\n==> ' + prefix + cmd + '\n');
if (this._state.requests[0] if (curReq.cmd === 'EXAMINE' || curReq.cmd === 'SELECT')
&& (this._state.requests[0].cmd === 'EXAMINE'
|| this._state.requests[0].cmd === 'SELECT'))
this._state.status = STATES.BOXSELECTING; this._state.status = STATES.BOXSELECTING;
else if (cmdstr === 'DONE')
reqs.shift();
} }
}; };

Loading…
Cancel
Save