Source: server/interface.js

//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 ) {
        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;
    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;