From f942ede18ad36f33928875ecb37c50c0856432c2 Mon Sep 17 00:00:00 2001 From: mscdex Date: Sat, 6 Jul 2013 23:48:47 -0400 Subject: [PATCH] Connection: add 'update' event for unsolicited, untagged FETCH responses --- README.md | 2 + lib/Connection.js | 121 ++++++++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index c19a464..7a5bbc0 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,8 @@ Connection Events * **deleted**(< _integer_ >seqno) - Emitted when a message is deleted from another IMAP connection's session. `seqno` is the sequence number (instead of the unique UID) of the message that was deleted. If you are caching sequence numbers, all sequence numbers higher than this value **MUST** be decremented by 1 in order to stay synchronized with the server and to keep correct continuity. +* **update**(< _integer_ >seqno, < _object_ >info) - Emitted when message metadata (e.g. flags) changes externally. + * **error**(< _Error_ >err) - Emitted when an error occurs. The 'source' property will be set to indicate where the error originated from. * **close**(< _boolean_ >hadError) - Emitted when the connection has completely closed. diff --git a/lib/Connection.js b/lib/Connection.js index e9892b9..60b6172 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -1073,73 +1073,80 @@ Connection.prototype._resUntagged = function(info) { box.uidvalidity = attrs.uidvalidity; } this._curReq.cbargs.push(box); - } else if (type === 'fetch') { - var msg = this._curReq.fetchCache[info.num], - keys = Object.keys(info.text), - keyslen = keys.length, - toget, msgEmitter, j; + } else if (type === 'fetch') { + if (/^(?:UID )?FETCH/.test(this._curReq.fullcmd)) { + // FETCH response sent as result of FETCH request + var msg = this._curReq.fetchCache[info.num], + keys = Object.keys(info.text), + keyslen = keys.length, + toget, msgEmitter, j; + + if (msg === undefined) { + // simple case -- no bodies were streamed + toget = this._curReq.fetching.slice(0); + if (toget.length === 0) + return; + + msgEmitter = new EventEmitter(); + attrs = {}; + + this._curReq.bodyEmitter.emit('message', msgEmitter, info.num); + } else { + toget = msg.toget; + msgEmitter = msg.msgEmitter; + attrs = msg.attrs; + } - if (msg === undefined) { - // simple case -- no bodies were streamed - toget = this._curReq.fetching.slice(0); - if (toget.length === 0) + i = toget.length; + if (i === 0) { + if (msg && !msg.ended) { + msg.ended = true; + process.nextTick(function() { + msgEmitter.emit('end'); + }); + } return; - - msgEmitter = new EventEmitter(); - attrs = {}; - - this._curReq.bodyEmitter.emit('message', msgEmitter, info.num); - } else { - toget = msg.toget; - msgEmitter = msg.msgEmitter; - attrs = msg.attrs; - } - - i = toget.length; - if (i === 0) { - if (msg && !msg.ended) { - msg.ended = true; - process.nextTick(function() { - msgEmitter.emit('end'); - }); } - return; - } - if (keyslen > 0) { - while (--i >= 0) { - j = keyslen; - while (--j >= 0) { - if (keys[j].toUpperCase() === toget[i]) { - if (!RE_BODYPART.test(toget[i])) { - if (toget[i] === 'X-GM-LABELS') { - var labels = info.text[keys[j]]; - for (var k = 0, lenk = labels.length; k < lenk; ++k) - labels[k] = (''+labels[k]).replace(RE_ESCAPE, '\\'); + if (keyslen > 0) { + while (--i >= 0) { + j = keyslen; + while (--j >= 0) { + if (keys[j].toUpperCase() === toget[i]) { + if (!RE_BODYPART.test(toget[i])) { + if (toget[i] === 'X-GM-LABELS') { + var labels = info.text[keys[j]]; + for (var k = 0, lenk = labels.length; k < lenk; ++k) + labels[k] = (''+labels[k]).replace(RE_ESCAPE, '\\'); + } + attrs[FETCH_ATTR_MAP[toget[i]]] = info.text[keys[j]]; } - attrs[FETCH_ATTR_MAP[toget[i]]] = info.text[keys[j]]; + toget.splice(i, 1); + break; } - toget.splice(i, 1); - break; } } } - } - if (toget.length === 0) { - if (msg) - msg.ended = true; - process.nextTick(function() { - msgEmitter.emit('attributes', attrs); - msgEmitter.emit('end'); - }); - } else if (msg === undefined) { - this._curReq.fetchCache[info.num] = { - msgEmitter: msgEmitter, - toget: toget, - attrs: attrs, - ended: false - }; + if (toget.length === 0) { + if (msg) + msg.ended = true; + process.nextTick(function() { + msgEmitter.emit('attributes', attrs); + msgEmitter.emit('end'); + }); + } else if (msg === undefined) { + this._curReq.fetchCache[info.num] = { + msgEmitter: msgEmitter, + toget: toget, + attrs: attrs, + ended: false + }; + } + } else { + // FETCH response sent as result of STORE request or sent unilaterally, + // treat them as the same for now for simplicity + this.emit('update', info.num, info.text); } } };