diff --git a/lib/imap.js b/lib/imap.js index 6572e34..2174fcd 100644 --- a/lib/imap.js +++ b/lib/imap.js @@ -85,31 +85,7 @@ util.inherits(ImapConnection, EventEmitter); exports.ImapConnection = ImapConnection; ImapConnection.prototype.connect = function(loginCb) { - var self = this, - fnInit = function() { - // First get pre-auth capabilities, including server-supported auth - // mechanisms - self._send('CAPABILITY', function() { - // Next, attempt to login - self._login(function(err, reentry) { - if (err) { - loginCb(err); - return; - } - // Next, get the list of available namespaces if supported (RFC2342) - if (!reentry && self._supports('NAMESPACE')) { - var fnMe = arguments.callee; - // Re-enter this function after we've obtained the available - // namespaces - self._send('NAMESPACE', function(e) { fnMe.call(this, e, true); }); - return; - } - // Lastly, get the top-level mailbox hierarchy delimiter used by the - // server - self._send('LIST "" ""', loginCb); - }); - }); - }; + var self = this; this._reset(); @@ -154,7 +130,28 @@ ImapConnection.prototype.connect = function(loginCb) { self.emit('close', had_error); }); - this._state.conn.on('ready', fnInit); + this._state.conn.on('ready', function() { + // First get pre-auth capabilities, including server-supported auth + // mechanisms + self._send('CAPABILITY', function() { + // Next, attempt to login + var checkedNS = false; + self._login(function redo(err) { + if (err) + return loginCb(err); + // Next, get the list of available namespaces if supported (RFC2342) + if (!checkedNS && self._supports('NAMESPACE')) { + // Re-enter this function after we've obtained the available + // namespaces + checkedNS = true; + return self._send('NAMESPACE', redo); + } + // Lastly, get the top-level mailbox hierarchy delimiter used by the + // server + self._send('LIST "" ""', loginCb); + }); + }); + }); this._state.conn.cleartext.on('data', function(data) { if (data.length === 0) return; @@ -268,12 +265,11 @@ ImapConnection.prototype.connect = function(loginCb) { }); } } - self._state.conn.cleartext.emit('data', data.slice(idxCRLF + 2)); - return; + return self._state.conn.cleartext.emit('data', data.slice(idxCRLF + 2)); } - if (data.length === 0) - return; + if (data.length === 0) return; + var endsInCRLF = (data[data.length-2] === 13 && data[data.length-1] === 10); data = utils.bufferSplit(data, CRLF); @@ -286,12 +282,12 @@ ImapConnection.prototype.connect = function(loginCb) { b = new Buffer(needsCRLF ? line.length + 2 : line.length); line.copy(b, 0, 0); if (needsCRLF) { - b[b.length-2] = 13; - b[b.length-1] = 10; + b[b.length - 2] = 13; + b[b.length - 1] = 10; } self._state.conn.cleartext.emit('data', b); }); - })(data[i], i === len-1); + })(data[i], i === len - 1); } } @@ -303,17 +299,14 @@ ImapConnection.prototype.connect = function(loginCb) { self._state.status = STATES.AUTH; if (self._state.numCapRecvs === 0) self._state.numCapRecvs = 1; - } else if (data[1] === 'NO' || data[1] === 'BAD' || data[1] === 'BYE') { - self._state.conn.end(); - return; - } + } else if (data[1] === 'NO' || data[1] === 'BAD' || data[1] === 'BYE') + return self._state.conn.end(); if (!self._state.isReady) { self._state.isReady = true; self._state.conn.emit('ready'); } // Restrict the type of server responses when unauthenticated - if (data[1] !== 'CAPABILITY' && data[1] !== 'ALERT') - return; + if (data[1] !== 'CAPABILITY' && data[1] !== 'ALERT') return; } switch (data[1]) { case 'CAPABILITY': @@ -326,8 +319,9 @@ ImapConnection.prototype.connect = function(loginCb) { break; case 'FLAGS': if (self._state.status === STATES.BOXSELECTING) { - self._state.box._flags = data[2].substr(1, data[2].length-2) - .split(' ').map(function(flag) { + self._state.box._flags = data[2].substr(1, data[2].length - 2) + .split(' ') + .map(function(flag) { return flag.substr(1); }); } @@ -371,10 +365,11 @@ ImapConnection.prototype.connect = function(loginCb) { case 'LIST': case 'XLIST': var result; - if (self.delim === null - && (result = /^\(\\No[sS]elect\) (.+?) .*$/.exec(data[2]))) + if (self.delim === null && + (result = /^\(\\No[sS]elect(?:[^)]*)\) (.+?) .*$/.exec(data[2]))) self.delim = (result[1] === 'NIL' - ? false : result[1].substring(1, result[1].length-1)); + ? false + : result[1].substring(1, result[1].length - 1)); else if (self.delim !== null) { if (self._state.requests[0].args.length === 0) self._state.requests[0].args.push({}); @@ -384,12 +379,14 @@ ImapConnection.prototype.connect = function(loginCb) { return attrib.substr(1).toUpperCase(); }), delim: (result[2] === 'NIL' - ? false : result[2].substring(1, result[2].length-1)), + ? false + : result[2].substring(1, result[2].length-1)), children: null, parent: null }, name = result[3], curChildren = self._state.requests[0].args[0]; + if (name[0] === '"' && name[name.length-1] === '"') name = name.substring(1, name.length - 1); @@ -727,7 +724,8 @@ ImapConnection.prototype._fetch = function(which, uids, options) { headers: true, body: false } - }, toFetch, useParser = false, bodyRange = '', self = this; + }, toFetch, bodyRange, extensions, useParser, self = this; + if (typeof options !== 'object') options = {}; utils.extend(true, opts, options); @@ -775,26 +773,38 @@ ImapConnection.prototype._fetch = function(which, uids, options) { useParser = true; } - var extensions = ''; // always fetch GMail-specific bits of information when on GMail if (this._supports('X-GM-EXT-1')) extensions = 'X-GM-THRID X-GM-MSGID X-GM-LABELS '; - this._send(which + 'FETCH ' + uids.join(',') + ' (' + extensions - + 'UID FLAGS INTERNALDATE' - + (opts.request.struct ? ' BODYSTRUCTURE' : '') - + (typeof toFetch === 'string' ? ' BODY' - + (!opts.markSeen ? '.PEEK' : '') - + '[' + toFetch + ']' + bodyRange : '') + ')', function(e) { - var fetcher = self._state.requests[0]._fetcher; - if (e && fetcher) - fetcher.emit('error', e); - else if (e && !fetcher) - self.emit('error', e); - else if (fetcher) - fetcher.emit('end'); - } - ); + var cmd = which; + cmd += 'FETCH '; + cmd += uids.join(','); + cmd += ' ('; + if (extensions) + cmd += extensions; + cmd += 'UID FLAGS INTERNALDATE'; + if (toFetch !== undefined) { + cmd += ' BODY'; + if (!opts.markSeen) + cmd += '.PEEK'; + cmd += '['; + cmd += toFetch; + cmd += ']'; + if (bodyRange) + cmd += bodyRange; + } + cmd += ')'; + + this._send(cmd, function(e) { + var fetcher = self._state.requests[0]._fetcher; + if (e && fetcher) + fetcher.emit('error', e); + else if (e && !fetcher) + self.emit('error', e); + else if (fetcher) + fetcher.emit('end'); + }); var imapFetcher = new ImapFetch(), req = this._state.requests[this._state.requests.length - 1]; req._fetcher = imapFetcher;