IDLE support

If the IMAP server supports it, the IDLE extension will automatically be used in place of NOOP/polling when a mailbox is open. This means notifications of mailbox changes (e.g. when new mail arrives) will automatically be pushed to the client as they happen. Rejoice!
fork
Brian White 13 years ago
parent 7a9ae9f9bd
commit c8a95325f0

@ -47,6 +47,14 @@ function ImapConnection (options) {
permFlags: [],
name: null,
messages: { total: 0, new: 0 }
},
ext: {
// Capability-specific state stuff
idle: {
MAX_WAIT: 1740000, // 29 mins in ms
sentIdle: false,
timeWaited: 0 // ms
}
}
};
this._options = extend(true, this._options, options);
@ -328,14 +336,12 @@ ImapConnection.prototype.connect = function(loginCb) {
curReq._fetcher.emit('message', msg);
msg.emit('end');
}
break;
}
}
}
} else if (data[0].indexOf('A') === 0) { // Tagged server response
var sendBox = false;
clearTimeout(self._state.tmrKeepalive);
self._state.tmrKeepalive = setTimeout(self._idleCheck.bind(self), self._state.tmoKeepalive);
if (self._state.status === STATES.BOXSELECTING) {
if (data[1] === 'OK') {
@ -347,6 +353,25 @@ ImapConnection.prototype.connect = function(loginCb) {
}
}
if (self._state.requests.length === 1) {
if (self._state.status === STATES.BOXSELECTED &&
self._state.capabilities.indexOf('IDLE') > -1) {
// According to RFC 2177, we should re-IDLE at least every 29
// minutes to avoid disconnection by the server
self._send('IDLE', undefined, true);
}
self._state.tmrKeepalive = setTimeout(function() {
if (self._state.isIdle) {
if (self._state.ext.idle.sentIdle) {
self._state.ext.idle.timeWaited += self._state.tmoKeepalive;
if (self._state.ext.idle.timeWaited >= self._state.ext.idle.MAX_WAIT)
self._send('IDLE', undefined, true); // restart IDLE
} else
self._noop();
}
}, self._state.tmoKeepalive);
}
if (self._state.requests[0].command.indexOf('RENAME') > -1) {
self._state.box.name = self._state.box._newName;
delete self._state.box._newName;
@ -722,23 +747,32 @@ ImapConnection.prototype._resetBox = function() {
this._state.box.messages.total = 0;
this._state.box.messages.new = 0;
};
ImapConnection.prototype._idleCheck = function() {
if (this._state.isIdle)
this._noop();
};
ImapConnection.prototype._noop = function() {
if (this._state.status >= STATES.AUTH)
this._send('NOOP', undefined);
this._send('NOOP');
};
ImapConnection.prototype._send = function(cmdstr, cb, bypass) {
if (arguments.length > 0 && !bypass)
if (typeof cmdstr !== 'undefined' && !bypass)
this._state.requests.push({ command: cmdstr, callback: cb, args: [] });
if ((arguments.length === 0 && this._state.requests.length > 0) || this._state.requests.length === 1 || bypass) {
if ((typeof cmdstr === 'undefined' && this._state.requests.length) ||
this._state.requests.length === 1 || bypass) {
var prefix = '', cmd = (bypass ? cmdstr : this._state.requests[0].command);
clearTimeout(this._state.tmrKeepalive);
this._state.isIdle = false;
var cmd = (bypass ? cmdstr : this._state.requests[0].command);
this._state.conn.cleartext.write('A' + ++this._state.curId + ' ' + cmd + CRLF);
debug('<<SENT>>: A' + this._state.curId + ' ' + cmd);
if (this._state.ext.idle.sentIdle && cmd !== 'DONE') {
this._send('DONE', undefined, true);
this._state.ext.idle.sentIdle = false;
this._state.ext.idle.timeWaited = 0;
} else if (cmd === 'IDLE') {
// we use a different prefix to differentiate and disregard the tagged
// response the server will send us when we issue DONE
prefix = 'IDLE ';
this._state.ext.idle.sentIdle = true;
}
if (cmd !== 'IDLE' && cmd !== 'DONE')
prefix = 'A' + ++this._state.curId + ' ';
this._state.conn.cleartext.write(prefix + cmd + CRLF);
debug('<<SENT>>: ' + prefix + cmd);
}
};

Loading…
Cancel
Save