wildduck/lib/pop3-connection.js

159 lines
3.9 KiB
JavaScript
Raw Normal View History

2017-04-08 02:29:14 +08:00
'use strict';
const crypto = require('crypto');
const EventEmitter = require('events');
const SOCKET_TIMEOUT = 60 * 1000;
class POP3Connection extends EventEmitter {
constructor(server, socket) {
super();
this._server = server;
this._socket = socket;
this._closed = false;
this._closing = false;
this.remoteAddress = this._socket.remoteAddress;
this._id = crypto.randomBytes(9).toString('base64');
this.processing = false;
this.queue = [];
this._remainder = '';
}
init() {
this._setListeners();
this._resetSession();
this._server.logger.info({
tnx: 'connection',
cid: this._id,
host: this.remoteAddress
}, 'Connection from %s', this.remoteAddress);
this._socket.write('+OK WDPop ready for requests from ' + this.remoteAddress + '\r\n');
}
_setListeners() {
this._socket.on('close', () => this._onClose());
this._socket.on('error', err => this._onError(err));
this._socket.setTimeout(this._server.options.socketTimeout || SOCKET_TIMEOUT, () => this._onTimeout());
this._socket.on('readable', () => {
if (this.processing) {
return;
}
this.processing = true;
this.read();
});
}
/**
* Fired when the socket is closed
* @event
*/
_onClose( /* hadError */ ) {
if (this._closed) {
return;
}
this.queue = [];
this.processing = false;
this._remainder = '';
this._closed = true;
this._closing = false;
this._server.logger.info({
tnx: 'close',
cid: this._id,
host: this.remoteAddress,
user: this.user
}, 'Connection closed to %s', this.remoteAddress);
this.emit('close');
}
/**
* Fired when an error occurs with the socket
*
* @event
* @param {Error} err Error object
*/
_onError(err) {
if (err.code === 'ECONNRESET' || err.code === 'EPIPE') {
return this.close(); // mark connection as 'closing'
}
this._server.logger.error({
err,
tnx: 'error',
user: this.user
}, '%s', err.message);
this.emit('error', err);
}
/**
* Fired when socket timeouts. Closes connection
*
* @event
*/
_onTimeout() {
this.close();
}
_resetSession() {
this.session = {};
}
close() {
if (!this._socket.destroyed && this._socket.writable) {
this._socket.end();
}
this._closing = true;
}
read() {
let chunk;
let data = this._remainder;
while ((chunk = this._socket.read()) !== null) {
data += chunk.toString('binary');
if (data.indexOf('\n') >= 0) {
let lines = data.split(/\r?\n/).map(line => Buffer.from(line, 'binary').toString());
this._remainder = lines.pop();
if (lines.length) {
if (this.queue.length) {
this.queue = this.queue.concat(lines);
} else {
this.queue = lines;
}
}
return this.processQueue();
}
}
this.processing = false;
}
processQueue() {
if (!this.queue.length) {
this.read(); // see if there's anything left to read
return;
}
let line = this.queue.shift().trim();
let parts = line.split(' ');
let command = parts.shift().toUpperCase();
let args = parts.join(' ');
console.log({
command,
args
});
setImmediate(() => this.processQueue());
}
}
module.exports = POP3Connection;