From b23bcb877a2a29dd7e072b7ea51eeff1277432ba Mon Sep 17 00:00:00 2001 From: Brian White Date: Wed, 13 Apr 2011 16:45:05 -0400 Subject: [PATCH] A few fixes and some minor changes Fixes: * markSeen property for imap.fetch works again * Queued commands were being sent before waiting for the server's response when exiting idle mode * Dispositions for multipart subtypes are now set properly Other changes: * Removed the last remnants of the ugly parsing of server responses * Don't attempt to send IDLE to the server if we are about to log out * Some of the message/rfc822 envelope header names were renamed to match what those names that are expected --- imap.js | 96 +++++++++++++++++++++++---------------------------------- 1 file changed, 38 insertions(+), 58 deletions(-) diff --git a/imap.js b/imap.js index d1309e4..26d6d8a 100644 --- a/imap.js +++ b/imap.js @@ -169,7 +169,7 @@ ImapConnection.prototype.connect = function(loginCb) { if (self._state.curData.length) data = self._state.curData + data; // add any additional k/v pairs that appear after the literal data - var fetchdesc = curReq._fetchdesc + data.substring(0, data.indexOf(CRLF)-1).trim(); + var fetchdesc = curReq._fetchdesc + ' ' + data.substring(0, data.indexOf(CRLF)-1).trim(); parseFetch(fetchdesc, curReq._msgheaders, curReq._msg); data = data.substr(data.indexOf(CRLF)+2); self._state.curExpected = 0; @@ -353,19 +353,20 @@ ImapConnection.prototype.connect = function(loginCb) { } } - if (self._state.requests.length === 1) { + if (self._state.requests.length === 1 + && self._state.requests[0].command !== 'LOGOUT') { if (self._state.status === STATES.BOXSELECTED && self._state.capabilities.indexOf('IDLE') > -1) { // According to RFC 2177, we should re-IDLE at least every 29 // minutes to avoid disconnection by the server - self._send('IDLE', undefined, true); + self._send('IDLE', self._send, true); } self._state.tmrKeepalive = setTimeout(function() { if (self._state.isIdle) { if (self._state.ext.idle.sentIdle) { self._state.ext.idle.timeWaited += self._state.tmoKeepalive; if (self._state.ext.idle.timeWaited >= self._state.ext.idle.MAX_WAIT) - self._send('IDLE', undefined, true); // restart IDLE + self._send('IDLE', self._send, true); // restart IDLE } else self._noop(); } @@ -401,6 +402,9 @@ ImapConnection.prototype.connect = function(loginCb) { self._state.requests.shift(); process.nextTick(function() { self._send(); }); self._state.isIdle = true; + } else if (data[0] === 'IDLE') { + process.nextTick(function() { self._send(); }); + self._state.isIdle = false; } else { // unknown response } @@ -623,7 +627,7 @@ ImapConnection.prototype.move = function(uids, boxTo, cb) { if (this._state.status !== STATES.BOXSELECTED) throw new Error('No mailbox is currently selected'); if (self._state.box.permFlags.indexOf('Deleted') === -1) - cb(new Error('Cannot move message: server does not allow deletion of messages')); + throw new Error('Cannot move message: server does not allow deletion of messages'); else { self.copy(uids, boxTo, function(err, reentryCount, deletedUIDs, counter) { if (err) { @@ -758,11 +762,11 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) { this._state.requests.length === 1 || bypass) { var prefix = '', cmd = (bypass ? cmdstr : this._state.requests[0].command); clearTimeout(this._state.tmrKeepalive); - this._state.isIdle = false; if (this._state.ext.idle.sentIdle && cmd !== 'DONE') { this._send('DONE', undefined, true); this._state.ext.idle.sentIdle = false; this._state.ext.idle.timeWaited = 0; + return; } else if (cmd === 'IDLE') { // we use a different prefix to differentiate and disregard the tagged // response the server will send us when we issue DONE @@ -935,59 +939,34 @@ function parseNamespaces(str, namespaces) { } function parseFetch(str, literalData, fetchData) { - // str === "... {xxxx}" or "... {xxxx} ..." or just "..." - // where ... is any number of key-value pairs - // and {xxxx} is the byte count for the literalData describing the preceding item (almost always "BODY") - var key, idxNext; - while (str.length > 0) { - key = (str.substr(0, 5) === 'BODY[' ? - str.substring(0, - (str.indexOf('>') > -1 ? str.indexOf('>') : str.indexOf(']'))+1) - : str.substring(0, str.indexOf(' '))); - str = str.substring(str.indexOf(' ')+1); - if (str.substr(0, 3) === 'NIL') - idxNext = 3; - else { - switch (key) { - case 'UID': - idxNext = str.indexOf(' ')+1; - fetchData.id = parseInt(str.substring(0, idxNext-1)); - break; - case 'INTERNALDATE': - idxNext = str.indexOf('"', 1)+1; - fetchData.date = str.substring(1, idxNext-1); - break; - case 'FLAGS': - idxNext = str.indexOf(')')+1; - fetchData.flags = str.substring(1, idxNext-1).split(' ').filter(isNotEmpty); - break; - case 'BODYSTRUCTURE': - idxNext = getNextIdxParen(str)+1; - fetchData.structure = parseBodyStructure(str.substring(1, idxNext-1)); - break; - default: - var result = /^BODY\[(.*)\](?:\<[\d]+\>)?$/.exec(key); - idxNext = str.indexOf("}")+1; - if (result && result[1].indexOf('HEADER') === 0) { // either full or selective headers - var headers = literalData.split(/\r\n(?=[\w])/), header; - fetchData.headers = {}; - for (var i=0,len=headers.length; i 0) result.push(convStr(o.str.substring(lastPos+1, i))); - if (o.str[i] === ')') + if (o.str[i] === ')' || o.str[i] === ']') return i; lastPos = i; - } else if (o.str[i] === '(') { + } else if (o.str[i] === '(' || o.str[i] === '[') { var innerResult = []; i = parseExpr(o, innerResult, i+1); lastPos = i;