ftpd.js
Version:
A FTP Server made in node.js
272 lines (252 loc) • 7.37 kB
JavaScript
//Imports
var net = require( "net" ),
tls = require( "tls" ),
fs = require( "fs" ),
util = require( "util" ),
EventEmitter = require( "events" ).EventEmitter,
path = require( "path" ),
User = require( path.join( "..", "user", "index.js" ) );
/**
*
* @access public
* @constructor
* @param {Server} server the server utilizing the interface
* @param {Object} options the interface options
* @since 0.6.0
*/
function Interface( server, options ) {
this.server = server;
this.options = options;
this.issetup = false;
this.connections = [];
EventEmitter.call( this );
}
util.inherits( Interface, EventEmitter );
/**
*
* This callback is used when the interface have finished setting up
*
* @callback Interface~didSetup
* @param {Interface} the interface that finished setting up
* @since 0.6.0
*/
/**
*
* This function is used to setup the interface in this order:
* - Check for tls certificate
* - Read certificate into memory
* - Create socket
* - Setup listeners
*
* @access public
* @param {Interface~didSetup} callback the callback to invoke when finished
* @since 0.6.0
*/
Interface.prototype.setup = function ( callback ) {
var that = this,
createServerOptions = this.getOptions().createServer || {},
parent = net;
this.getServer().getFtpdjs().log( this.getBaseMessage() + ": Setting up" );
if ( (undefined !== createServerOptions.key) || (undefined !== this.getOptions().key) ) {
parent = tls;
}
if ( undefined !== this.getOptions().key ) {
//Read the SSL Certificate from storage
createServerOptions.key = fs.readFileSync( this.getOptions().key );
createServerOptions.cert = fs.readFileSync( this.getOptions().cert );
if ( undefined !== this.getOptions().ca ) {
createServerOptions.ca = [ fs.readFileSync( this.getOptions().ca ) ];
}
}
this.socket = parent.createServer( createServerOptions, function ( connection ) {
that.onConnection( connection );
} );
this.getSocket().on( "error", function ( error ) {
that.onError( error );
} );
this.issetup = true;
try {
this.getServer().getFtpdjs().log( this.getBaseMessage() + ": Finished setting up" );
callback( this );
} catch ( e ) {
}
};
/**
*
* This callback is used to when the interface have successfully bound to the provided address and is now accepting
* connections
*
* @callback Interface~didListen
* @since 0.6.0
*/
/**
*
* This function is used to tell the interface to bind to the address it's been provided and start to liten for
* connections The function will setup the interface if it haven't been done at the time of execution
*
* @access public
* @param {Interface~didListen} callback the callback to invoke when bound and accepting clients
* @since 0.6.0
*/
Interface.prototype.listen = function ( callback ) {
var that = this;
//Check if the interface have been setup
if ( !this.isSetup() ) {
this.setup( function () {
that.listen( callback );
} );
return;
}
this.getSocket().listen( this.getOptions().listen, function () {
var formattedListen = that.getFormattedListen();
that.getServer().getFtpdjs().log( that.getBaseMessage() + ": Listening " + ("/" === formattedListen.charAt( 0 ) ? "at" : "on") + " " + formattedListen );
callback();
} );
};
/**
*
* This function gets invoked when an error occurs with the interface. Usually this is FATAL
*
* @access public
* @param {Error} error the error
* @since 0.6.0
*/
Interface.prototype.onError = function ( error ) {
this.emit( "error", error );
};
/**
*
* This function gets invoked when a new connection have been made to the interface
*
* @access public
* @param {net.Socket} connection the connection object
* @since 0.6.0
*/
Interface.prototype.onConnection = function ( connection ) {
connection = new User( this, connection );
this.emit( "connection", connection );
this.getConnections().push( connection );
};
/**
*
* This callback is used when the interface have closed and finished disconnecting all connections
*
* @callback Interface~finishedClose
* @since 0.6.0
*/
/**
*
* This function is used to close the interface and disconnect all active connections
*
* @access public
* @param {Interface~finishedClose} done the callback to invoke when finished closing
* @since 0.6.0
*/
Interface.prototype.close = function ( done ) {
var that = this;
this.getSocket().close( function () {
that.getServer().getFtpdjs().log( that.getBaseMessage() + ": Closed" );
done();
} );
for ( var i = 0; i < this.getConnections().length; i++ ) {
this.getConnections()[ i ].destroy();
}
};
/**
*
* This function is used to get the interface's ID
*
* @access public
* @returns {int} the interface's id
* @since 0.6.0
*/
Interface.prototype.getID = function () {
return this.getOptions().id;
};
/**
*
* This function is used to get the server utilizing the interface
*
* @access public
* @returns {Server} the server utilizing the interface
* @since 0.6.0
*/
Interface.prototype.getServer = function () {
return this.server;
};
/**
*
* This function is used to get the interface's options
*
* @access public
* @returns {Object} the interface's options
* @since 0.6.0
*/
Interface.prototype.getOptions = function () {
return this.options;
};
/**
*
* This function is used to get the socket the interface is listening for connections on
*
* @access public
* @returns {net.Server} the socket the server is listening on
* @since 0.6.0
*/
Interface.prototype.getSocket = function () {
return this.socket;
};
/**
*
* This function is used to get all of the connections connected to the interface
*
* @access public
* @returns {Array} the connected connections
* @since 0.6.0
*/
Interface.prototype.getConnections = function () {
return this.connections;
};
/**
*
* This function is used to tell wether or not the interface have been setup
*
* @access public
* @returns {boolean} true if the server have been setup, false if not
* @since 0.6.0
*/
Interface.prototype.isSetup = function () {
return this.issetup;
};
/**
*
* This function is used to get the base message for interface logging
*
* @access public
* @returns {string} the base logging message
* @since 0.6.o
*/
Interface.prototype.getBaseMessage = function () {
return this.getServer().getBaseMessage() + " [Interface " + this.getID() + "]";
};
/**
*
* This function is used to get the formatted interface address
*
* @access public
* @returns {string} the formatted address the interface *should* be listening on
* @since 0.6.0
*/
Interface.prototype.getFormattedListen = function () {
//Check that it's not a unix socket/windows pipe
if ( undefined !== this.getOptions().listen.path ) {
return this.getOptions().listen.path;
}
return (undefined !== this.getOptions().key ? "tls://" : "") + this.getOptions().listen.host + ":" + this.getOptions().listen.port;
};
/**
* @access public
* @type {Interface}
* @since 0.6.0
*/
module.exports = Interface;