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
fork
Brian White 13 years ago
parent ab6403735c
commit b23bcb877a

@ -169,7 +169,7 @@ ImapConnection.prototype.connect = function(loginCb) {
if (self._state.curData.length) if (self._state.curData.length)
data = self._state.curData + data; data = self._state.curData + data;
// add any additional k/v pairs that appear after the literal 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); parseFetch(fetchdesc, curReq._msgheaders, curReq._msg);
data = data.substr(data.indexOf(CRLF)+2); data = data.substr(data.indexOf(CRLF)+2);
self._state.curExpected = 0; 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 && if (self._state.status === STATES.BOXSELECTED &&
self._state.capabilities.indexOf('IDLE') > -1) { self._state.capabilities.indexOf('IDLE') > -1) {
// According to RFC 2177, we should re-IDLE at least every 29 // According to RFC 2177, we should re-IDLE at least every 29
// minutes to avoid disconnection by the server // minutes to avoid disconnection by the server
self._send('IDLE', undefined, true); self._send('IDLE', self._send, true);
} }
self._state.tmrKeepalive = setTimeout(function() { self._state.tmrKeepalive = setTimeout(function() {
if (self._state.isIdle) { if (self._state.isIdle) {
if (self._state.ext.idle.sentIdle) { if (self._state.ext.idle.sentIdle) {
self._state.ext.idle.timeWaited += self._state.tmoKeepalive; self._state.ext.idle.timeWaited += self._state.tmoKeepalive;
if (self._state.ext.idle.timeWaited >= self._state.ext.idle.MAX_WAIT) 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 } else
self._noop(); self._noop();
} }
@ -401,6 +402,9 @@ ImapConnection.prototype.connect = function(loginCb) {
self._state.requests.shift(); self._state.requests.shift();
process.nextTick(function() { self._send(); }); process.nextTick(function() { self._send(); });
self._state.isIdle = true; self._state.isIdle = true;
} else if (data[0] === 'IDLE') {
process.nextTick(function() { self._send(); });
self._state.isIdle = false;
} else { } else {
// unknown response // unknown response
} }
@ -623,7 +627,7 @@ ImapConnection.prototype.move = function(uids, boxTo, cb) {
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');
if (self._state.box.permFlags.indexOf('Deleted') === -1) 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 { else {
self.copy(uids, boxTo, function(err, reentryCount, deletedUIDs, counter) { self.copy(uids, boxTo, function(err, reentryCount, deletedUIDs, counter) {
if (err) { if (err) {
@ -758,11 +762,11 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) {
this._state.requests.length === 1 || bypass) { this._state.requests.length === 1 || bypass) {
var prefix = '', cmd = (bypass ? cmdstr : this._state.requests[0].command); var prefix = '', cmd = (bypass ? cmdstr : this._state.requests[0].command);
clearTimeout(this._state.tmrKeepalive); clearTimeout(this._state.tmrKeepalive);
this._state.isIdle = false;
if (this._state.ext.idle.sentIdle && cmd !== 'DONE') { if (this._state.ext.idle.sentIdle && cmd !== 'DONE') {
this._send('DONE', undefined, true); this._send('DONE', undefined, true);
this._state.ext.idle.sentIdle = false; this._state.ext.idle.sentIdle = false;
this._state.ext.idle.timeWaited = 0; this._state.ext.idle.timeWaited = 0;
return;
} else if (cmd === 'IDLE') { } 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
@ -935,59 +939,34 @@ function parseNamespaces(str, namespaces) {
} }
function parseFetch(str, literalData, fetchData) { function parseFetch(str, literalData, fetchData) {
// str === "... {xxxx}" or "... {xxxx} ..." or just "..." var key, idxNext, result = parseExpr(str);
// where ... is any number of key-value pairs for (var i=0,len=result.length; i<len; i+=2) {
// and {xxxx} is the byte count for the literalData describing the preceding item (almost always "BODY") if (result[i] === 'UID')
var key, idxNext; fetchData.id = parseInt(result[i+1], 10);
while (str.length > 0) { else if (result[i] === 'INTERNALDATE')
key = (str.substr(0, 5) === 'BODY[' ? fetchData.date = result[i+1];
str.substring(0, else if (result[i] === 'FLAGS')
(str.indexOf('>') > -1 ? str.indexOf('>') : str.indexOf(']'))+1) fetchData.flags = result[i+1].filter(isNotEmpty);
: str.substring(0, str.indexOf(' '))); else if (result[i] === 'BODYSTRUCTURE')
str = str.substring(str.indexOf(' ')+1); fetchData.structure = parseBodyStructure(result[i+1]);
if (str.substr(0, 3) === 'NIL') else if (Array.isArray(result[i]) && typeof result[i][0] === 'string' &&
idxNext = 3; result[i][0].indexOf('HEADER') === 0 && literalData) {
else { var headers = literalData.split(/\r\n(?=[\w])/), header;
switch (key) { fetchData.headers = {};
case 'UID': for (var j=0,len2=headers.length; j<len2; ++j) {
idxNext = str.indexOf(' ')+1; header = headers[j].substring(0, headers[j].indexOf(': ')).toLowerCase();
fetchData.id = parseInt(str.substring(0, idxNext-1)); if (!fetchData.headers[header])
break; fetchData.headers[header] = [];
case 'INTERNALDATE': fetchData.headers[header].push(headers[j].substr(headers[j].indexOf(': ')+2).replace(/\r\n/g, '').trim());
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<len; i++) {
header = headers[i].substr(0, headers[i].indexOf(': ')).toLowerCase();
if (!fetchData.headers[header])
fetchData.headers[header] = [];
fetchData.headers[header].push(headers[i].substr(headers[i].indexOf(': ')+2).replace(/\r\n/g, '').trim());
}
}
} }
} }
str = str.substr(idxNext).trim();
} }
} }
function parseBodyStructure(cur, prefix, partID) { function parseBodyStructure(cur, prefix, partID) {
var ret = []; var ret = [];
if (typeof cur === 'string') { if (typeof prefix === 'undefined') {
var result = parseExpr(cur); var result = (Array.isArray(cur) ? cur : parseExpr(cur));
if (result.length) if (result.length)
ret = parseBodyStructure(result, '', 1); ret = parseBodyStructure(result, '', 1);
} else { } else {
@ -1004,6 +983,7 @@ function parseBodyStructure(cur, prefix, partID) {
part.params[cur[next][i].toLowerCase()] = cur[next][i+1]; part.params[cur[next][i].toLowerCase()] = cur[next][i+1];
} else } else
part.params = cur[next]; part.params = cur[next];
++next;
} }
} else { // single part } else { // single part
next = 7; next = 7;
@ -1064,7 +1044,7 @@ function parseBodyStructure(cur, prefix, partID) {
else if (i === 3) else if (i === 3)
part.envelope.sender = val; part.envelope.sender = val;
else if (i === 4) else if (i === 4)
part.envelope.replyTo = val; part.envelope['reply-to'] = val;
else if (i === 5) else if (i === 5)
part.envelope.to = val; part.envelope.to = val;
else if (i === 6) else if (i === 6)
@ -1072,9 +1052,9 @@ function parseBodyStructure(cur, prefix, partID) {
else if (i === 7) else if (i === 7)
part.envelope.bcc = val; part.envelope.bcc = val;
} else if (i === 8) } else if (i === 8)
part.envelope.inReplyTo = cur[next][i]; // message ID being replied to part.envelope['in-reply-to'] = cur[next][i]; // message ID being replied to
else if (i === 9) else if (i === 9)
part.envelope.messageID = cur[next][i]; part.envelope['message-id'] = cur[next][i];
else else
break; break;
} }
@ -1193,13 +1173,13 @@ function parseExpr(o, result, start) {
if (!inQuote) { if (!inQuote) {
if (o.str[i] === '"') if (o.str[i] === '"')
inQuote = true; inQuote = true;
else if (o.str[i] === ' ' || o.str[i] === ')') { else if (o.str[i] === ' ' || o.str[i] === ')' || o.str[i] === ']') {
if (i - (lastPos+1) > 0) if (i - (lastPos+1) > 0)
result.push(convStr(o.str.substring(lastPos+1, i))); result.push(convStr(o.str.substring(lastPos+1, i)));
if (o.str[i] === ')') if (o.str[i] === ')' || o.str[i] === ']')
return i; return i;
lastPos = i; lastPos = i;
} else if (o.str[i] === '(') { } else if (o.str[i] === '(' || o.str[i] === '[') {
var innerResult = []; var innerResult = [];
i = parseExpr(o, innerResult, i+1); i = parseExpr(o, innerResult, i+1);
lastPos = i; lastPos = i;

Loading…
Cancel
Save