rework handling of both secure and non-secure sockets

fork
mscdex 12 years ago
parent 6303fe2adf
commit 100b7cfba2

@ -1,4 +1,5 @@
var assert = require('assert'), var assert = require('assert'),
tls = require('tls'),
isDate = require('util').isDate, isDate = require('util').isDate,
inspect = require('util').inspect, inspect = require('util').inspect,
inherits = require('util').inherits, inherits = require('util').inherits,
@ -118,26 +119,22 @@ ImapConnection.prototype.connect = function(loginCb) {
requests = state.requests, requests = state.requests,
indata = state.indata; indata = state.indata;
state.conn = new Socket(); var socket = state.conn = new Socket();
state.conn.setKeepAlive(true); socket.setKeepAlive(true);
socket.setTimeout(0);
if (this._options.secure) { if (this._options.secure)
// TODO: support STARTTLS socket = tls.connect({ socket: state.conn }, onconnect);
state.conn.cleartext = utils.setSecure(state.conn); else
state.conn.on('secure', function() { state.conn.once('connect', onconnect);
state.connected = true;
self.debug&&self.debug('[connection] Secure connection made.');
});
} else
state.conn.cleartext = state.conn;
state.conn.on('connect', function() { function onconnect() {
state.conn = socket; // re-assign for secure connections
state.connected = true; state.connected = true;
state.authenticated = false; state.authenticated = false;
self.debug&&self.debug('[connection] Connected to host.'); self.debug&&self.debug('[connection] Connected to host.');
state.conn.cleartext.write('');
state.status = STATES.NOAUTH; state.status = STATES.NOAUTH;
}); };
state.conn.on('end', function() { state.conn.on('end', function() {
state.connected = false; state.connected = false;
@ -146,16 +143,6 @@ ImapConnection.prototype.connect = function(loginCb) {
self.emit('end'); self.emit('end');
}); });
function errorHandler(err) {
clearTimeout(state.tmrConn);
if (state.status === STATES.NOCONNECT)
loginCb(new Error('Unable to connect. Reason: ' + err));
self.emit('error', err);
self.debug&&self.debug('[connection] Error occurred: ' + err);
}
state.conn.cleartext.on('error', errorHandler);
state.conn.on('close', function(had_error) { state.conn.on('close', function(had_error) {
self._reset(); self._reset();
requests = state.requests; requests = state.requests;
@ -165,7 +152,15 @@ ImapConnection.prototype.connect = function(loginCb) {
self.emit('close', had_error); self.emit('close', had_error);
}); });
state.conn.on('ready', function() { state.conn.on('error', function(err) {
clearTimeout(state.tmrConn);
if (state.status === STATES.NOCONNECT)
loginCb(new Error('Unable to connect. Reason: ' + err));
self.emit('error', err);
self.debug&&self.debug('[connection] Error occurred: ' + err);
});
socket.on('ready', function() {
var checkedNS = false; var checkedNS = false;
var reentry = function(err) { var reentry = function(err) {
if (err) { if (err) {
@ -236,6 +231,8 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
} }
socket.on('data', ondata);
function ondata(b) { function ondata(b) {
b.p || (b.p = 0); b.p || (b.p = 0);
if (b.length === 0 || b.p >= b.length) return; if (b.length === 0 || b.p >= b.length) return;
@ -705,24 +702,23 @@ ImapConnection.prototype.connect = function(loginCb) {
} }
} }
state.conn.cleartext.on('data', ondata);
state.conn.connect(this._options.port, this._options.host); state.conn.connect(this._options.port, this._options.host);
state.tmrConn = setTimeout(function() { state.tmrConn = setTimeout(function() {
state.conn.destroy(); state.conn.destroy();
state.conn = undefined;
loginCb(new Error('Connection timed out')); loginCb(new Error('Connection timed out'));
}, this._options.connTimeout); }, this._options.connTimeout);
}; };
ImapConnection.prototype.isAuthenticated = function() {
return this.authenticated;
};
ImapConnection.prototype.logout = function(cb) { ImapConnection.prototype.logout = function(cb) {
var self = this;
if (this._state.status >= STATES.NOAUTH) { if (this._state.status >= STATES.NOAUTH) {
this._send('LOGOUT', cb); this._send('LOGOUT', function(err) {
this._state.conn.end(); self._state.conn.end();
if (typeof cb === 'function')
cb(err);
});
if (cb === true) if (cb === true)
this._state.conn.removeAllListeners(); this._state.conn.removeAllListeners();
} else } else
@ -851,8 +847,8 @@ ImapConnection.prototype.append = function(data, options, cb) {
this._send(cmd, function(err) { this._send(cmd, function(err) {
if (err || step++ === 2) if (err || step++ === 2)
return cb(err); return cb(err);
self._state.conn.cleartext.write(data); self._state.conn.write(data);
self._state.conn.cleartext.write(CRLF); self._state.conn.write(CRLF);
self.debug&&self.debug('\n==> ' + inspect(data.toString()) + '\n'); self.debug&&self.debug('\n==> ' + inspect(data.toString()) + '\n');
}); });
}; };
@ -1484,9 +1480,9 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) {
} }
if (cmd !== 'IDLE' && cmd !== 'DONE') if (cmd !== 'IDLE' && cmd !== 'DONE')
prefix = 'A' + (++this._state.curId) + ' '; prefix = 'A' + (++this._state.curId) + ' ';
this._state.conn.cleartext.write(prefix); this._state.conn.write(prefix);
this._state.conn.cleartext.write(cmd); this._state.conn.write(cmd);
this._state.conn.cleartext.write(CRLF); this._state.conn.write(CRLF);
this.debug&&this.debug('\n==> ' + prefix + cmd + '\n'); this.debug&&this.debug('\n==> ' + prefix + cmd + '\n');
if (this._state.requests[0] if (this._state.requests[0]
&& (this._state.requests[0].cmd === 'EXAMINE' && (this._state.requests[0].cmd === 'EXAMINE'

@ -1,41 +1,6 @@
var tls = require('tls');
exports.MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', exports.MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec']; 'Oct', 'Nov', 'Dec'];
exports.setSecure = function(tcpSocket) {
var pair = tls.createSecurePair(),
cleartext;
pair.encrypted.pipe(tcpSocket);
tcpSocket.pipe(pair.encrypted);
pair.fd = tcpSocket.fd;
cleartext = pair.cleartext;
cleartext.socket = tcpSocket;
cleartext.encrypted = pair.encrypted;
function onerror(e) {
if (cleartext._controlReleased)
cleartext.emit('error', e);
}
function onclose() {
tcpSocket.removeListener('error', onerror);
tcpSocket.removeListener('close', onclose);
}
tcpSocket.on('error', onerror);
tcpSocket.on('close', onclose);
pair.on('secure', function() {
process.nextTick(function() { cleartext.socket.emit('secure'); });
});
cleartext._controlReleased = true;
return cleartext;
};
exports.isNotEmpty = function(str) { exports.isNotEmpty = function(str) {
return str.trim().length > 0; return str.trim().length > 0;
}; };

Loading…
Cancel
Save