From 1fc2d7a089649e500b52fbedd3ba272f0219af2d Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 27 Jul 2014 21:26:42 -0400 Subject: [PATCH] Connection: wait for continuation before DONE --- lib/Connection.js | 14 ++- test/test-connection-idle-order.js | 138 +++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 test/test-connection-idle-order.js diff --git a/lib/Connection.js b/lib/Connection.js index f6021ec..86ee924 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -205,6 +205,16 @@ Connection.prototype.connect = function() { parser.on('continue', function(info) { var type = self._curReq.type; if (type === 'IDLE') { + if (self._queue.length + && self._curReq + && self._curReq.type === 'IDLE' + && self._sock + && self._sock.writable + && !self._idle.enabled) { + self.debug && self.debug('=> DONE'); + self._sock.write('DONE' + CRLF); + return; + } // now idling self._idle.started = Date.now(); } else if (/^AUTHENTICATE XOAUTH/.test(self._curReq.fullcmd)) { @@ -225,6 +235,7 @@ Connection.prototype.connect = function() { // no longer idling self._idle.enabled = false; self._idle.started = undefined; + clearTimeout(self._tmrKeepalive); self._curReq = undefined; @@ -1726,8 +1737,7 @@ Connection.prototype._enqueue = function(fullcmd, promote, cb) { && this._sock.writable && this._idle.enabled) { this._idle.enabled = false; - this.debug && this.debug('=> DONE'); - this._sock.write('DONE' + CRLF); + clearTimeout(this._tmrKeepalive); } }; diff --git a/test/test-connection-idle-order.js b/test/test-connection-idle-order.js new file mode 100644 index 0000000..8df728f --- /dev/null +++ b/test/test-connection-idle-order.js @@ -0,0 +1,138 @@ +var assert = require('assert'), + net = require('net'), + Imap = require('../lib/Connection'); + +var result, body = '', bodyInfo; + +var CRLF = '\r\n'; + +var RESPONSES = [ + ['* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA CHILDREN', + 'A0 OK Thats all she wrote!', + '' + ].join(CRLF), + ['* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA CHILDREN UIDPLUS MOVE', + 'A1 OK authenticated (Success)', + '' + ].join(CRLF), + ['* NAMESPACE (("" "/")) NIL NIL', + 'A2 OK Success', + '' + ].join(CRLF), + ['* LIST (\\Noselect) "/" "/"', + 'A3 OK Success', + '' + ].join(CRLF), + ['* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen)', + '* OK [PERMANENTFLAGS ()] Flags permitted.', + '* OK [UIDVALIDITY 2] UIDs valid.', + '* 685 EXISTS', + '* 0 RECENT', + '* OK [UIDNEXT 4422] Predicted next UID.', + 'A4 OK [READ-ONLY] INBOX selected. (Success)', + '' + ].join(CRLF), + ['* 1 FETCH (UID 1)', + '* 1 FETCH (INTERNALDATE "05-Sep-2004 00:38:03 +0000" UID 1000)', + '* 1 FETCH (BODY[TEXT] "IMAP is terrible")', + '* 1 FETCH (FLAGS (\\Seen))', + 'A5 OK Success', + '' + ].join(CRLF), + ['* STATUS test (MESSAGES 231 RECENT 0 UNSEEN 0 UIDVALIDITY 123 UIDNEXT 442)', + 'A6 OK STATUS completed', + '' + ].join(CRLF), + ['* BYE LOGOUT Requested', + 'A7 OK good day (Success)', + '' + ].join(CRLF) +]; +var EXPECTED = [ + 'A0 CAPABILITY', + 'A1 LOGIN "foo" "bar"', + 'A2 NAMESPACE', + 'A3 LIST "" ""', + 'A4 EXAMINE "INBOX"', + 'A5 FETCH 1 (UID FLAGS INTERNALDATE BODY.PEEK[TEXT])', + 'IDLE IDLE', + 'DONE', + 'A6 STATUS "test" (MESSAGES RECENT UNSEEN UIDVALIDITY UIDNEXT)', + 'A7 LOGOUT' +]; + +var exp = -1, + res = -1, + sentCont = false; + +var srv = net.createServer(function(sock) { + sock.write('* OK asdf\r\n'); + var buf = '', lines; + sock.on('data', function(data) { + buf += data.toString('utf8'); + if (buf.indexOf(CRLF) > -1) { + lines = buf.split(CRLF); + buf = lines.pop(); + lines.forEach(function(l) { + assert(l === EXPECTED[++exp], 'Unexpected client request: ' + l); + if (l === 'DONE') { + assert(sentCont, 'DONE seen before continuation sent'); + sock.write('IDLE ok\r\n'); + } else if (l === 'IDLE IDLE') { + setTimeout(function() { + sentCont = true; + sock.write('+ idling\r\n'); + }, 100); + } else { + assert(RESPONSES[++res], 'No response for client request: ' + l); + sock.write(RESPONSES[res]); + } + }); + } + }); +}); +srv.listen(0, '127.0.0.1', function() { + var port = srv.address().port; + var imap = new Imap({ + user: 'foo', + password: 'bar', + host: '127.0.0.1', + port: port, + keepalive: true + }); + imap.on('ready', function() { + srv.close(); + imap.openBox('INBOX', true, function() { + var f = imap.seq.fetch(1, { bodies: ['TEXT'] }); + f.on('message', function(m) { + m.on('body', function(stream, info) { + bodyInfo = info; + stream.on('data', function(chunk) { body += chunk.toString('utf8'); }); + }); + m.on('attributes', function(attrs) { + result = attrs; + }); + }); + f.on('end', function() { + imap.status('test', function(err, status) { + imap.end(); + }); + }); + }); + }); + imap.connect(); +}); + +process.once('exit', function() { + assert.deepEqual(result, { + uid: 1, + date: new Date('05-Sep-2004 00:38:03 +0000'), + flags: [ '\\Seen' ] + }); + assert.equal(body, 'IMAP is terrible'); + assert.deepEqual(bodyInfo, { + seqno: 1, + which: 'TEXT', + size: 16 + }); +}); \ No newline at end of file