|
|
|
@ -55,6 +55,13 @@ function ImapConnection (options) {
|
|
|
|
|
tmrKeepalive: null,
|
|
|
|
|
tmoKeepalive: 10000,
|
|
|
|
|
tmrConn: null,
|
|
|
|
|
indata: {
|
|
|
|
|
literals: [],
|
|
|
|
|
line: undefined,
|
|
|
|
|
temp: undefined,
|
|
|
|
|
streaming: false,
|
|
|
|
|
expect: -1
|
|
|
|
|
},
|
|
|
|
|
box: {
|
|
|
|
|
uidnext: 0,
|
|
|
|
|
flags: [],
|
|
|
|
@ -93,7 +100,8 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
|
|
|
|
|
var self = this,
|
|
|
|
|
state = this._state,
|
|
|
|
|
requests = state.requests;
|
|
|
|
|
requests = state.requests,
|
|
|
|
|
indata = state.indata;
|
|
|
|
|
|
|
|
|
|
state.conn = new Socket();
|
|
|
|
|
state.conn.setKeepAlive(true);
|
|
|
|
@ -161,44 +169,44 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
|
|
|
|
|
function read(b) {
|
|
|
|
|
var blen = b.length, origPos = b.p;
|
|
|
|
|
if (ondata.expect <= (blen - b.p)) {
|
|
|
|
|
var left = ondata.expect;
|
|
|
|
|
ondata.expect = 0;
|
|
|
|
|
if (indata.expect <= (blen - b.p)) {
|
|
|
|
|
var left = indata.expect;
|
|
|
|
|
indata.expect = 0;
|
|
|
|
|
b.p += left;
|
|
|
|
|
return b.slice(origPos, origPos + left);
|
|
|
|
|
} else {
|
|
|
|
|
ondata.expect -= (blen - b.p);
|
|
|
|
|
indata.expect -= (blen - b.p);
|
|
|
|
|
b.p = blen;
|
|
|
|
|
return origPos > 0 ? b.slice(origPos) : b;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ondata = function(b) {
|
|
|
|
|
function ondata(b) {
|
|
|
|
|
b.p || (b.p = 0);
|
|
|
|
|
if (b.length === 0 || b.p >= b.length) return;
|
|
|
|
|
self.debug&&self.debug('\n<== ' + inspect(b.toString('binary', b.p)) + '\n');
|
|
|
|
|
|
|
|
|
|
var r, m, litType;
|
|
|
|
|
if (ondata.expect > 0) {
|
|
|
|
|
if (indata.expect > 0) {
|
|
|
|
|
r = read(b);
|
|
|
|
|
if (ondata.streaming) {
|
|
|
|
|
if (indata.streaming) {
|
|
|
|
|
if (requests[0]._useParser)
|
|
|
|
|
state.parser.execute(r);
|
|
|
|
|
else
|
|
|
|
|
requests[0]._msg.emit('data', r);
|
|
|
|
|
if (ondata.expect === 0) {
|
|
|
|
|
ondata.streaming = false;
|
|
|
|
|
if (indata.expect === 0) {
|
|
|
|
|
indata.streaming = false;
|
|
|
|
|
if (requests[0]._useParser)
|
|
|
|
|
state.parser.finish();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (ondata.temp)
|
|
|
|
|
ondata.temp += r.toString('binary');
|
|
|
|
|
if (indata.temp)
|
|
|
|
|
indata.temp += r.toString('binary');
|
|
|
|
|
else
|
|
|
|
|
ondata.temp = r.toString('binary');
|
|
|
|
|
if (ondata.expect === 0)
|
|
|
|
|
ondata.literals.push(ondata.temp);
|
|
|
|
|
ondata.temp = undefined;
|
|
|
|
|
indata.temp = r.toString('binary');
|
|
|
|
|
if (indata.expect === 0)
|
|
|
|
|
indata.literals.push(indata.temp);
|
|
|
|
|
indata.temp = undefined;
|
|
|
|
|
}
|
|
|
|
|
if (b.p >= b.length)
|
|
|
|
|
return;
|
|
|
|
@ -208,24 +216,24 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
return;
|
|
|
|
|
else {
|
|
|
|
|
m = RE_LITHEADER.exec(r);
|
|
|
|
|
if (ondata.line)
|
|
|
|
|
ondata.line += r;
|
|
|
|
|
if (indata.line)
|
|
|
|
|
indata.line += r;
|
|
|
|
|
else
|
|
|
|
|
ondata.line = r;
|
|
|
|
|
indata.line = r;
|
|
|
|
|
if (m)
|
|
|
|
|
litType = m[1];
|
|
|
|
|
//assert((litType = m[1]) !== undefined);
|
|
|
|
|
ondata.expect = (m ? parseInt(m[2], 10) : -1);
|
|
|
|
|
if (ondata.expect > -1) {
|
|
|
|
|
if ((m = /\* (\d+) FETCH/i.exec(ondata.line))
|
|
|
|
|
indata.expect = (m ? parseInt(m[2], 10) : -1);
|
|
|
|
|
if (indata.expect > -1) {
|
|
|
|
|
if ((m = /\* (\d+) FETCH/i.exec(indata.line))
|
|
|
|
|
&& /^BODY\[/i.test(litType)) {
|
|
|
|
|
var msg = new ImapMessage();
|
|
|
|
|
msg.seqno = parseInt(m[1], 10);
|
|
|
|
|
msg.length = ondata.expect;
|
|
|
|
|
msg.length = indata.expect;
|
|
|
|
|
requests[0]._msg = msg;
|
|
|
|
|
requests[0]._fetcher.emit('message', msg);
|
|
|
|
|
ondata.streaming = true;
|
|
|
|
|
ondata.literals.push(ondata.expect);
|
|
|
|
|
indata.streaming = true;
|
|
|
|
|
indata.literals.push(indata.expect);
|
|
|
|
|
if (requests[0]._useParser) {
|
|
|
|
|
requests[0]._msg.headers = {};
|
|
|
|
|
if (!state.parser) {
|
|
|
|
@ -242,18 +250,18 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (ondata.expect === 0)
|
|
|
|
|
ondata.literals.push('');
|
|
|
|
|
} else if (indata.expect === 0)
|
|
|
|
|
indata.literals.push('');
|
|
|
|
|
// start reading of the literal or get the rest of the response
|
|
|
|
|
return ondata(b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ondata.line[0] === '*') { // Untagged server response
|
|
|
|
|
if (indata.line[0] === '*') { // Untagged server response
|
|
|
|
|
var isUnsolicited =
|
|
|
|
|
(requests[0] && requests[0].cmd === 'NOOP')
|
|
|
|
|
|| (state.isIdle && state.ext.idle.state === IDLE_READY);
|
|
|
|
|
if (m = XRegExp.exec(ondata.line, REX_UNRESPNUM)) {
|
|
|
|
|
if (m = XRegExp.exec(indata.line, REX_UNRESPNUM)) {
|
|
|
|
|
// m.type = response type (numeric-based)
|
|
|
|
|
m.type = m.type.toUpperCase();
|
|
|
|
|
self.debug&&self.debug('[parsing incoming] saw untagged ' + m.type);
|
|
|
|
@ -263,7 +271,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
var msg = (requests[0] && requests[0]._msg
|
|
|
|
|
? requests[0]._msg
|
|
|
|
|
: new ImapMessage());
|
|
|
|
|
parsers.parseFetch(m.info, ondata.literals, msg);
|
|
|
|
|
parsers.parseFetch(m.info, indata.literals, msg);
|
|
|
|
|
|
|
|
|
|
if (typeof msg.body === 'number') {
|
|
|
|
|
// we streamed a body
|
|
|
|
@ -316,7 +324,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
self.emit('deleted', parseInt(m.num, 10));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (m = XRegExp.exec(ondata.line, REX_UNRESPDATA)) {
|
|
|
|
|
} else if (m = XRegExp.exec(indata.line, REX_UNRESPDATA)) {
|
|
|
|
|
// m.type = response type (data)
|
|
|
|
|
m.type = m.type.toUpperCase();
|
|
|
|
|
self.debug&&self.debug('[parsing incoming] saw untagged ' + m.type);
|
|
|
|
@ -327,11 +335,11 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
m.shared = personal namespaces (or null)
|
|
|
|
|
*/
|
|
|
|
|
self.namespaces.personal =
|
|
|
|
|
parsers.parseNamespaces(m.personal, ondata.literals);
|
|
|
|
|
parsers.parseNamespaces(m.personal, indata.literals);
|
|
|
|
|
self.namespaces.other =
|
|
|
|
|
parsers.parseNamespaces(m.other, ondata.literals);
|
|
|
|
|
parsers.parseNamespaces(m.other, indata.literals);
|
|
|
|
|
self.namespaces.shared =
|
|
|
|
|
parsers.parseNamespaces(m.shared, ondata.literals);
|
|
|
|
|
parsers.parseNamespaces(m.shared, indata.literals);
|
|
|
|
|
break;
|
|
|
|
|
case 'FLAGS':
|
|
|
|
|
// m.flags = list of 0+ flags
|
|
|
|
@ -352,11 +360,11 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
m.mailbox = mailbox name (string)
|
|
|
|
|
*/
|
|
|
|
|
m.flags = (m.flags ? m.flags.toUpperCase().split(' ') : []);
|
|
|
|
|
m.delimiter = parsers.convStr(m.delimiter, ondata.literals);
|
|
|
|
|
m.mailbox = parsers.convStr(m.mailbox, ondata.literals);
|
|
|
|
|
m.delimiter = parsers.convStr(m.delimiter, indata.literals);
|
|
|
|
|
m.mailbox = parsers.convStr(m.mailbox, indata.literals);
|
|
|
|
|
var result;
|
|
|
|
|
if (self.delimiter === undefined)
|
|
|
|
|
self.delimiter = parsers.convStr(m.delimiter, ondata.literals);
|
|
|
|
|
self.delimiter = parsers.convStr(m.delimiter, indata.literals);
|
|
|
|
|
else {
|
|
|
|
|
if (requests[0].cbargs.length === 0)
|
|
|
|
|
requests[0].cbargs.push({});
|
|
|
|
@ -397,7 +405,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
/* m.mailbox = mailbox name (string)
|
|
|
|
|
m.attributes = expression list (k=>v pairs) of mailbox attributes
|
|
|
|
|
*/
|
|
|
|
|
m.mailbox = parsers.convStr(m.mailbox, ondata.literals);
|
|
|
|
|
m.mailbox = parsers.convStr(m.mailbox, indata.literals);
|
|
|
|
|
var ret = {
|
|
|
|
|
name: m.mailbox,
|
|
|
|
|
uidvalidity: undefined,
|
|
|
|
@ -407,7 +415,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if (m.attributes) {
|
|
|
|
|
m.attributes = parsers.parseExpr(m.attributes, ondata.literals);
|
|
|
|
|
m.attributes = parsers.parseExpr(m.attributes, indata.literals);
|
|
|
|
|
for (var i=0,len=m.attributes.length; i<len; ++i) {
|
|
|
|
|
switch (m.attributes[i].toUpperCase()) {
|
|
|
|
|
case 'RECENT':
|
|
|
|
@ -437,7 +445,7 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
state.conn.end();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (m = RE_UNRESP.exec(ondata.line)) {
|
|
|
|
|
} else if (m = RE_UNRESP.exec(indata.line)) {
|
|
|
|
|
// m[1]: response type
|
|
|
|
|
// m[2]: resp-text-code
|
|
|
|
|
// m[3]: message
|
|
|
|
@ -490,23 +498,23 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
} else {
|
|
|
|
|
self.debug&&self.debug(
|
|
|
|
|
'[parsing incoming] saw unexpected untagged response: '
|
|
|
|
|
+ inspect(ondata.line));
|
|
|
|
|
+ inspect(indata.line));
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
ondata.literals = [];
|
|
|
|
|
ondata.line = undefined;
|
|
|
|
|
ondata.temp = undefined;
|
|
|
|
|
ondata.streaming = false;
|
|
|
|
|
ondata.expect = -1;
|
|
|
|
|
indata.literals = [];
|
|
|
|
|
indata.line = undefined;
|
|
|
|
|
indata.temp = undefined;
|
|
|
|
|
indata.streaming = false;
|
|
|
|
|
indata.expect = -1;
|
|
|
|
|
if (b.p < b.length)
|
|
|
|
|
return ondata(b);
|
|
|
|
|
} else if (ondata.line[0] === 'A' || ondata.line[0] === '+') {
|
|
|
|
|
var line = ondata.line;
|
|
|
|
|
ondata.literals = [];
|
|
|
|
|
ondata.line = undefined;
|
|
|
|
|
ondata.temp = undefined;
|
|
|
|
|
ondata.streaming = false;
|
|
|
|
|
ondata.expect = -1;
|
|
|
|
|
} else if (indata.line[0] === 'A' || indata.line[0] === '+') {
|
|
|
|
|
var line = indata.line;
|
|
|
|
|
indata.literals = [];
|
|
|
|
|
indata.line = undefined;
|
|
|
|
|
indata.temp = undefined;
|
|
|
|
|
indata.streaming = false;
|
|
|
|
|
indata.expect = -1;
|
|
|
|
|
self.debug&&self.debug(line[0] === 'A'
|
|
|
|
|
? '[parsing incoming] saw tagged response'
|
|
|
|
|
: '[parsing incoming] saw continuation response');
|
|
|
|
@ -590,26 +598,22 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
process.nextTick(function() { self._send(); });
|
|
|
|
|
|
|
|
|
|
state.isIdle = true;
|
|
|
|
|
} else if (/^IDLE /i.test(ondata.line)) {
|
|
|
|
|
} else if (/^IDLE /i.test(indata.line)) {
|
|
|
|
|
self.debug&&self.debug('[parsing incoming] saw IDLE');
|
|
|
|
|
if (requests.length)
|
|
|
|
|
process.nextTick(function() { self._send(); });
|
|
|
|
|
state.isIdle = false;
|
|
|
|
|
state.ext.idle.state = IDLE_NONE;
|
|
|
|
|
state.ext.idle.timeWaited = 0;
|
|
|
|
|
ondata.line = undefined;
|
|
|
|
|
indata.line = undefined;
|
|
|
|
|
} else {
|
|
|
|
|
// unknown response
|
|
|
|
|
self.debug&&self.debug('[parsing incoming] saw unexpected response: '
|
|
|
|
|
+ inspect(ondata.line));
|
|
|
|
|
+ inspect(indata.line));
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ondata.literals = [];
|
|
|
|
|
ondata.line = undefined;
|
|
|
|
|
ondata.temp = undefined;
|
|
|
|
|
ondata.streaming = false;
|
|
|
|
|
ondata.expect = -1;
|
|
|
|
|
|
|
|
|
|
state.conn.cleartext.on('data', ondata);
|
|
|
|
|
|
|
|
|
|
state.conn.connect(this._options.port, this._options.host);
|
|
|
|
@ -1115,6 +1119,12 @@ ImapConnection.prototype._reset = function() {
|
|
|
|
|
this._state.ext.idle.state = IDLE_NONE;
|
|
|
|
|
this._state.ext.idle.timeWaited = 0;
|
|
|
|
|
|
|
|
|
|
this._state.indata.literals = [];
|
|
|
|
|
this._state.indata.line = undefined;
|
|
|
|
|
this._state.indata.temp = undefined;
|
|
|
|
|
this._state.indata.streaming = false;
|
|
|
|
|
this._state.indata.expect = -1;
|
|
|
|
|
|
|
|
|
|
this.namespaces = { personal: [], other: [], shared: [] };
|
|
|
|
|
this.delimiter = undefined;
|
|
|
|
|
this.capabilities = [];
|
|
|
|
|