"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshServer = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh");
const tcpListenerFactory_1 = require("./tcpListenerFactory");
/**
 * Enables accepting SSH sessions on a TCP socket.
 *
 * It's possible to create an `SshServerSession` over any `Stream` instance;
 * this class is merely a convenient helper that manages creating sessions
 * over Node.js TCP `Socket`s from incoming connections.
 */
class SshServer {
    constructor(config) {
        this.config = config;
        this.sessions = [];
        /**
         * Gets or sets a function that handles trace messages associated with the server sessions.
         *
         * By default, no messages are traced. To enable tracing, set this property to a function
         * that routes the message to console.log, a file, or anywhere else.
         *
         * @param level The level of message being traced: error, warning, info, or verbose.
         * @param eventId An integer that identifies the type of event. Normally this is one of
         * the values from `SshTraceEventIds`, but extensions may define additional event IDs.
         * @param msg A description of the event (non-localized).
         * @param err Optional `Error` object associated with the event, often included with
         * warning or error events. While the `Error.message` property is typically included as
         * (part of) the `msg` parameter, the error object may contain additional useful context
         * such as the stack trace.
         */
        this.trace = (level, eventId, msg, err) => { };
        this.errorEmitter = new vscode_jsonrpc_1.Emitter();
        this.onError = this.errorEmitter.event;
        this.sessionOpenedEmitter = new vscode_jsonrpc_1.Emitter();
        this.onSessionOpened = this.sessionOpenedEmitter.event;
        this.credentials = { publicKeys: [] };
        /**
         * Gets or sets a factory for creating TCP listeners.
         *
         * Applications may override this factory to provide custom logic for selecting
         * local port numbers to listen on for port-forwarding.
         */
        this.tcpListenerFactory = new tcpListenerFactory_1.DefaultTcpListenerFactory();
        if (!config)
            throw new TypeError('SshSessionConfiguration is required.');
        if (config.protocolExtensions.includes(dev_tunnels_ssh_1.SshProtocolExtensionNames.sessionReconnect)) {
            this.reconnectableSessions = [];
        }
    }
    async acceptSessions(localPort, localAddress) {
        if (!localAddress) {
            localAddress = '0.0.0.0';
        }
        const portPrefix = localAddress === '0.0.0.0' ? 'port ' : localAddress + ':';
        try {
            this.tcpListener = await this.tcpListenerFactory.createTcpListener(undefined, // remotePort
            localAddress, localPort, false);
        }
        catch (e) {
            if (!(e instanceof Error))
                throw e;
            this.trace(dev_tunnels_ssh_1.TraceLevel.Error, dev_tunnels_ssh_1.SshTraceEventIds.serverListenFailed, `SshServer failed to listen on ${portPrefix}${localPort}: ${e.message}`, e);
            throw e;
        }
        this.tcpListener.addListener('connection', this.acceptSession.bind(this));
        this.trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.serverListening, `SshServer listening on ${portPrefix}${localPort}.`);
    }
    async acceptConnection(socket) {
        socket.setNoDelay(true);
        return new dev_tunnels_ssh_1.NodeStream(socket);
    }
    async acceptSession(socket) {
        this.trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.serverClientConnected, 'SshServer client connected.');
        const stream = await this.acceptConnection(socket);
        const session = new dev_tunnels_ssh_1.SshServerSession(this.config, this.reconnectableSessions);
        session.trace = this.trace;
        session.credentials = this.credentials;
        this.sessions.push(session);
        session.onClosed((e) => {
            const sessionIndex = this.sessions.indexOf(session);
            if (sessionIndex >= 0) {
                this.sessions.splice(sessionIndex, 1);
            }
        });
        this.sessionOpenedEmitter.fire(session);
        try {
            await session.connect(stream);
        }
        catch (e) {
            if (!(e instanceof Error))
                throw e;
            if (e instanceof dev_tunnels_ssh_1.SshConnectionError) {
                await session.close(e.reason || dev_tunnels_ssh_1.SshDisconnectReason.connectionLost, e.message, e);
            }
            else {
                await session.close(dev_tunnels_ssh_1.SshDisconnectReason.protocolError, e.message, e);
            }
            this.errorEmitter.fire(e);
        }
    }
    dispose() {
        var _a;
        (_a = this.tcpListener) === null || _a === void 0 ? void 0 : _a.close();
    }
}
exports.SshServer = SshServer;
//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-824c4c46a288b839f13b24022655329c2aeb9f81/node_modules-@microsoft-dev-tunnels-ssh-tcp-sshServer.js.map