Implemented configurable connection timeout.

Update and fix for README too.
fork
Brian White 14 years ago
parent a10f9539f0
commit e45b904800

@ -3,6 +3,9 @@ Description
node-imap is an IMAP module for [node.js](http://nodejs.org/) that provides an asynchronous interface for communicating with an IMAP mail server. node-imap is an IMAP module for [node.js](http://nodejs.org/) that provides an asynchronous interface for communicating with an IMAP mail server.
This module does not perform any magic such as auto-decoding of messages/attachments or parsing of email addresses (node-imap leaves all mail header values as-is).
If you are in need of this kind of extra functionality, check out andris9's [mime.js](http://github.com/andris9/mailparser/blob/master/mime.js) (requires [node-iconv](http://github.com/bnoordhuis/node-iconv)) set of functions, part of his [mailparser](http://github.com/andris9/mailparser) module.
Requirements Requirements
============ ============
@ -23,7 +26,7 @@ This example fetches the 'date', 'from', 'to', 'subject' message headers and the
host: 'imap.gmail.com', host: 'imap.gmail.com',
port: 993, port: 993,
secure: true secure: true
}); });
function die(err) { function die(err) {
console.log('Uh oh: ' + err); console.log('Uh oh: ' + err);
@ -189,8 +192,9 @@ ImapConnection Functions
* **host** - A String representing the hostname or IP address of the IMAP server. **Default:** "localhost" * **host** - A String representing the hostname or IP address of the IMAP server. **Default:** "localhost"
* **port** - An Integer representing the port of the IMAP server. **Default:** 143 * **port** - An Integer representing the port of the IMAP server. **Default:** 143
* **secure** - A Boolean indicating the connection should use SSL/TLS. **Default:** false * **secure** - A Boolean indicating the connection should use SSL/TLS. **Default:** false
* **connTimeout** - An Integer indicating the number of milliseconds to wait for a connection to be established. **Default:** 10000
* **connect**() - _(void)_ - Attempts to connect and log into the IMAP server. * **connect**(Function) - _(void)_ - Attempts to connect and log into the IMAP server. The Function parameter is the callback with one parameter: the error (null if none).
* **logout**(Function) - _(void)_ - Closes the connection to the server. The Function parameter is the callback. * **logout**(Function) - _(void)_ - Closes the connection to the server. The Function parameter is the callback.
@ -252,7 +256,6 @@ TODO
A bunch of things not yet implemented in no particular order: A bunch of things not yet implemented in no particular order:
* Connection timeout
* Support AUTH=CRAM-MD5/AUTH=CRAM_MD5 authentication * Support AUTH=CRAM-MD5/AUTH=CRAM_MD5 authentication
* OR searching ability with () grouping * OR searching ability with () grouping
* HEADER.FIELDS.NOT capability during FETCH using "!" prefix * HEADER.FIELDS.NOT capability during FETCH using "!" prefix

@ -1,13 +1,18 @@
var sys = require('sys'), net = require('net'), EventEmitter = require('events').EventEmitter; var sys = require('sys'), net = require('net'), EventEmitter = require('events').EventEmitter;
var empty = function() {}, CRLF = "\r\n", debug=empty/*sys.debug*/, STATES = { NOCONNECT: 0, NOAUTH: 1, AUTH: 2, BOXSELECTING: 3, BOXSELECTED: 4 }; var emptyFn = function() {}, CRLF = "\r\n", debug=emptyFn/*sys.debug*/, STATES = { NOCONNECT: 0, NOAUTH: 1, AUTH: 2, BOXSELECTING: 3, BOXSELECTED: 4 };
function ImapConnection (options) { function ImapConnection (options) {
if (!(this instanceof ImapConnection))
return new ImapConnection(options);
EventEmitter.call(this);
this._options = { this._options = {
username: '', username: '',
password: '', password: '',
host: 'localhost', host: 'localhost',
port: 143, port: 143,
secure: false secure: false,
connTimeout: 10000 // connection timeout in msecs
}; };
this._state = { this._state = {
status: STATES.NOCONNECT, status: STATES.NOCONNECT,
@ -25,6 +30,7 @@ function ImapConnection (options) {
box: { _uidnext: 0, _uidvalidity: 0, _flags: [], permFlags: [], name: null, messages: { total: 0, new: 0 }} box: { _uidnext: 0, _uidvalidity: 0, _flags: [], permFlags: [], name: null, messages: { total: 0, new: 0 }}
}; };
this._capabilities = []; this._capabilities = [];
this._tmrConn = null;
this._options = extend(true, this._options, options); this._options = extend(true, this._options, options);
}; };
@ -32,34 +38,49 @@ sys.inherits(ImapConnection, EventEmitter);
exports.ImapConnection = ImapConnection; exports.ImapConnection = ImapConnection;
ImapConnection.prototype.connect = function(loginCb) { ImapConnection.prototype.connect = function(loginCb) {
var self = this; var self = this,
var fnInit = function() { skipSetup = (this._state.conn !== null),
// First get pre-auth capabilities, including server-supported auth mechanisms fnInit = function() {
self._send('CAPABILITY', function() { // First get pre-auth capabilities, including server-supported auth mechanisms
// Next attempt to login self._send('CAPABILITY', function() {
self._login(function(err) { // Next attempt to login
if (err) { self._login(function(err) {
loginCb(err); if (err) {
return; loginCb(err);
} return;
// Lastly, get the mailbox hierarchy delimiter/separator used by the server }
self._send('LIST "" ""', loginCb); // Lastly, get the mailbox hierarchy delimiter/separator used by the server
}); self._send('LIST "" ""', loginCb);
}); });
}; });
};
loginCb = loginCb || emptyFn;
this._reset(); this._reset();
this._state.conn = net.createConnection(this._options.port, this._options.host); if (!this._state.conn)
this._state.conn = net.createConnection(this._options.port, this._options.host);
else
this._state.conn.connect(this._options.port, this._options.host);
if (this._options.secure) { if (this._options.secure) {
this._state.conn.setSecure(); this._state.conn.setSecure();
this._state.conn.on('secure', function() { if (this._state.conn.listeners('secure').length === 0) {
debug('Secure connection made.'); this._state.conn.on('secure', function() {
}); debug('Secure connection made.');
} });
}
} else
this._state.conn.secure = false;
this._tmrConn = setTimeout(this._fnTmrConn, this._options.connTimeout, loginCb);
if (skipSetup)
return;
this._state.conn.setKeepAlive(true); this._state.conn.setKeepAlive(true);
this._state.conn.setEncoding('utf8'); this._state.conn.setEncoding('utf8');
this._state.conn.on('connect', function() { this._state.conn.on('connect', function() {
clearTimeout(self._tmrConn);
debug('Connected to host.'); debug('Connected to host.');
self._state.conn.write(''); self._state.conn.write('');
self._state.status = STATES.NOAUTH; self._state.status = STATES.NOAUTH;
@ -182,7 +203,6 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
} }
} else if (data[0].indexOf('A') === 0) { // Tagged server response } else if (data[0].indexOf('A') === 0) { // Tagged server response
//var id = data[0].substr(1);
clearTimeout(self._state.tmrKeepalive); clearTimeout(self._state.tmrKeepalive);
self._state.tmrKeepalive = setTimeout(self._idleCheck.bind(self), self._state.tmoKeepalive); self._state.tmrKeepalive = setTimeout(self._idleCheck.bind(self), self._state.tmoKeepalive);
@ -223,15 +243,15 @@ ImapConnection.prototype.connect = function(loginCb) {
// unknown response // unknown response
} }
}); });
this._state.conn.on('error', function(err) {
debug('Encountered error: ' + err);
});
this._state.conn.on('end', function() { this._state.conn.on('end', function() {
self._reset(); self._reset();
debug('FIN packet received. Disconnecting...'); debug('FIN packet received. Disconnecting...');
self.emit('end'); self.emit('end');
}); });
this._state.conn.on('error', function(err) { this._state.conn.on('error', function(err) {
clearTimeout(self._tmrConn);
if (self._state.status === STATES.NOCONNECT)
loginCb(new Error('Unable to connect. Reason: ' + err));
self.emit('error', err); self.emit('error', err);
debug('Error occurred: ' + err); debug('Error occurred: ' + err);
}); });
@ -447,6 +467,11 @@ ImapConnection.prototype.delFlags = function(uid, flags, cb) {
/****** Private Functions ******/ /****** Private Functions ******/
ImapConnection.prototype._fnTmrConn = function(loginCb) {
loginCb(new Error('Connection timed out'));
this._state.conn.destroy();
}
ImapConnection.prototype._storeFlag = function(uid, flags, isAdding, cb) { ImapConnection.prototype._storeFlag = function(uid, flags, isAdding, cb) {
if (this._state.status !== STATES.BOXSELECTED) if (this._state.status !== STATES.BOXSELECTED)
throw new Error('No mailbox is currently selected'); throw new Error('No mailbox is currently selected');
@ -494,6 +519,7 @@ ImapConnection.prototype._login = function(cb) {
}; };
ImapConnection.prototype._reset = function() { ImapConnection.prototype._reset = function() {
clearTimeout(this._state.tmrKeepalive); clearTimeout(this._state.tmrKeepalive);
clearTimeout(this._tmrConn);
this._state.status = STATES.NOCONNECT; this._state.status = STATES.NOCONNECT;
this._state.numCapRecvs = 0; this._state.numCapRecvs = 0;
this._state.requests = []; this._state.requests = [];

Loading…
Cancel
Save