"use strict";
/**
 * HTTP server for the PKCS#11 bridge.
 *
 * Runs on localhost:8756 and provides the same REST API as the
 * SignDocs Flutter desktop app's token_http_server.dart.
 *
 * All responses include CORS headers and Chrome Private Network Access support.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.startServer = startServer;
const http_1 = require("http");
const https_1 = require("https");
const middleware_detector_1 = require("./middleware-detector");
const pkcs11_manager_1 = require("./pkcs11-manager");
const localhost_cert_1 = require("./localhost-cert");
const PORT = 8756;
const HTTPS_PORT = 8757;
const VERSION = '1.0.0';
const pkcs11Manager = new pkcs11_manager_1.Pkcs11Manager();
const CORS_HEADERS = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization, Access-Control-Request-Private-Network',
    'Access-Control-Max-Age': '86400',
    'Access-Control-Allow-Private-Network': 'true',
    'Content-Type': 'application/json',
};
function jsonResponse(res, data, statusCode = 200) {
    res.writeHead(statusCode, CORS_HEADERS);
    res.end(JSON.stringify(data));
}
function errorResponse(res, message, statusCode = 500) {
    jsonResponse(res, { error: message }, statusCode);
}
async function readBody(req) {
    return new Promise((resolve, reject) => {
        let body = '';
        req.on('data', chunk => { body += chunk; });
        req.on('end', () => {
            try {
                resolve(JSON.parse(body));
            }
            catch {
                reject(new Error('Invalid JSON'));
            }
        });
        req.on('error', reject);
    });
}
function extractSlotId(tokenId) {
    const match = /_slot_(\d+)$/.exec(tokenId);
    return match ? parseInt(match[1], 10) : null;
}
function slotIdToBuffer(slotId) {
    const buf = Buffer.alloc(4);
    buf.writeUInt32LE(slotId, 0);
    return buf;
}
// ── Route Handlers ────────────────────────────────────────
async function handleStatus(_req, res) {
    const installed = (0, middleware_detector_1.getInstalledMiddleware)();
    jsonResponse(res, {
        running: true,
        version: VERSION,
        middleware: installed.map(m => m.name),
    });
}
async function handleMiddleware(_req, res) {
    const middleware = (0, middleware_detector_1.detectMiddleware)();
    jsonResponse(res, middleware);
}
async function handleTokens(_req, res) {
    try {
        const tokens = [];
        const installed = (0, middleware_detector_1.getInstalledMiddleware)();
        for (const mw of installed) {
            try {
                pkcs11Manager.loadMiddleware(mw.libraryPath);
                const slots = pkcs11Manager.getSlotList(true);
                for (const slotBuf of slots) {
                    try {
                        const token = pkcs11Manager.getTokenInfo(slotBuf, mw.name);
                        tokens.push(token);
                    }
                    catch (e) {
                        console.error(`Error getting token info: ${e}`);
                    }
                }
                pkcs11Manager.unloadMiddleware();
            }
            catch (e) {
                console.error(`Error with middleware ${mw.name}: ${e}`);
            }
        }
        jsonResponse(res, tokens);
    }
    catch (e) {
        errorResponse(res, `Failed to list tokens: ${e}`);
    }
}
async function handleVerifyPin(req, res) {
    try {
        const body = await readBody(req);
        const { tokenId, middlewarePath, pin } = body;
        if (!tokenId || !middlewarePath || !pin) {
            return errorResponse(res, 'Missing required fields: tokenId, middlewarePath, pin', 400);
        }
        const slotId = extractSlotId(tokenId);
        if (slotId === null) {
            return errorResponse(res, 'Invalid tokenId format', 400);
        }
        pkcs11Manager.loadMiddleware(middlewarePath);
        pkcs11Manager.openSession(slotIdToBuffer(slotId));
        const result = pkcs11Manager.login(slotId, pin);
        jsonResponse(res, result);
    }
    catch (e) {
        errorResponse(res, `PIN verification failed: ${e}`);
    }
}
async function handleCertificates(req, res) {
    try {
        const body = await readBody(req);
        const { tokenId, middlewarePath, pin } = body;
        if (!tokenId || !middlewarePath || !pin) {
            return errorResponse(res, 'Missing required fields', 400);
        }
        const slotId = extractSlotId(tokenId);
        if (slotId === null)
            return errorResponse(res, 'Invalid tokenId format', 400);
        ensureLoggedIn(middlewarePath, slotId, pin);
        const certificates = pkcs11Manager.findCertificates(slotId);
        jsonResponse(res, certificates);
    }
    catch (e) {
        errorResponse(res, `Failed to list certificates: ${e}`);
    }
}
async function handleSignData(req, res) {
    try {
        const body = await readBody(req);
        const { tokenId, middlewarePath, certId, pin, dataBase64, algorithm } = body;
        if (!tokenId || !middlewarePath || !certId || !pin || !dataBase64) {
            return errorResponse(res, 'Missing required fields', 400);
        }
        const slotId = extractSlotId(tokenId);
        if (slotId === null)
            return errorResponse(res, 'Invalid tokenId format', 400);
        ensureLoggedIn(middlewarePath, slotId, pin);
        // Find private key
        const keyHandle = pkcs11Manager.findPrivateKeyForCertificate(slotId, certId);
        if (!keyHandle) {
            return jsonResponse(res, { success: false, error: 'Private key not found for certificate' });
        }
        // Sign
        const mechanism = pkcs11Manager.algorithmToMechanism(algorithm ?? 'SHA256withRSA');
        const dataBytes = Buffer.from(dataBase64, 'base64');
        const signature = pkcs11Manager.sign(slotId, keyHandle, dataBytes, mechanism);
        // Get cert chain
        const certificates = pkcs11Manager.findCertificates(slotId);
        const cert = certificates.find(c => c.id === certId);
        const certChainBase64 = [];
        if (cert?.derBase64)
            certChainBase64.push(cert.derBase64);
        jsonResponse(res, {
            success: true,
            signatureBase64: signature.toString('base64'),
            certChainBase64,
        });
    }
    catch (e) {
        jsonResponse(res, { success: false, error: String(e) });
    }
}
async function handleCertificateChain(req, res) {
    try {
        const body = await readBody(req);
        const { tokenId, middlewarePath, certId, pin } = body;
        if (!tokenId || !middlewarePath || !certId || !pin) {
            return errorResponse(res, 'Missing required fields', 400);
        }
        const slotId = extractSlotId(tokenId);
        if (slotId === null)
            return errorResponse(res, 'Invalid tokenId format', 400);
        ensureLoggedIn(middlewarePath, slotId, pin);
        const certificates = pkcs11Manager.findCertificates(slotId);
        const cert = certificates.find(c => c.id === certId);
        const certChainBase64 = [];
        if (cert?.derBase64) {
            certChainBase64.push(cert.derBase64);
            // Find issuer certificates on the token
            for (const otherCert of certificates) {
                if (otherCert.id !== cert.id && otherCert.subjectCN === cert.issuerCN && otherCert.derBase64) {
                    certChainBase64.push(otherCert.derBase64);
                }
            }
        }
        jsonResponse(res, { certChainBase64 });
    }
    catch (e) {
        errorResponse(res, `Failed to get certificate chain: ${e}`);
    }
}
async function handleCloseSession(req, res) {
    try {
        const body = await readBody(req);
        const { tokenId } = body;
        if (tokenId) {
            const slotId = extractSlotId(tokenId);
            if (slotId !== null && pkcs11Manager.isInitialized) {
                pkcs11Manager.logout(slotId);
                pkcs11Manager.closeSession(slotId);
            }
        }
        jsonResponse(res, { success: true });
    }
    catch {
        jsonResponse(res, { success: false });
    }
}
// ── Helpers ────────────────────────────────────────────────
function ensureLoggedIn(middlewarePath, slotId, pin) {
    if (!pkcs11Manager.isInitialized || pkcs11Manager.currentLibraryPath !== middlewarePath) {
        pkcs11Manager.loadMiddleware(middlewarePath);
        pkcs11Manager.openSession(slotIdToBuffer(slotId));
        pkcs11Manager.login(slotId, pin);
    }
}
// ── Router ─────────────────────────────────────────────────
const routes = {
    GET: {
        '/status': handleStatus,
        '/middleware': handleMiddleware,
        '/tokens': handleTokens,
    },
    POST: {
        '/verify-pin': handleVerifyPin,
        '/certificates': handleCertificates,
        '/sign-data': handleSignData,
        '/certificate-chain': handleCertificateChain,
        '/close-session': handleCloseSession,
    },
};
/** Request handler shared between HTTP and HTTPS servers. */
function requestHandler(req, res) {
    const method = req.method ?? 'GET';
    const url = req.url ?? '/';
    // Handle CORS preflight
    if (method === 'OPTIONS') {
        res.writeHead(204, CORS_HEADERS);
        res.end();
        return;
    }
    const handler = routes[method]?.[url];
    if (handler) {
        handler(req, res).catch(e => {
            errorResponse(res, `Internal error: ${e}`);
        });
    }
    else {
        errorResponse(res, 'Not found', 404);
    }
}
/** Start both HTTP and HTTPS servers. */
function startServer() {
    // HTTP server (port 8756)
    const server = (0, http_1.createServer)(requestHandler);
    server.listen(PORT, '127.0.0.1', () => {
        console.log(`HTTP  server running on http://127.0.0.1:${PORT}`);
    });
    // Also try IPv6 for HTTP
    try {
        const serverV6 = (0, http_1.createServer)(requestHandler);
        serverV6.listen(PORT, '::1', () => {
            console.log(`HTTP  server also on http://[::1]:${PORT}`);
        });
        serverV6.on('error', () => { });
    }
    catch { }
    // HTTPS server (port 8757) -- self-signed cert for localhost
    try {
        const tls = (0, localhost_cert_1.getLocalhostTls)();
        const httpsServer = (0, https_1.createServer)({ key: tls.key, cert: tls.cert }, requestHandler);
        httpsServer.listen(HTTPS_PORT, '127.0.0.1', () => {
            console.log(`HTTPS server running on https://127.0.0.1:${HTTPS_PORT}`);
        });
        // IPv6 HTTPS
        try {
            const httpsV6 = (0, https_1.createServer)({ key: tls.key, cert: tls.cert }, requestHandler);
            httpsV6.listen(HTTPS_PORT, '::1', () => {
                console.log(`HTTPS server also on https://[::1]:${HTTPS_PORT}`);
            });
            httpsV6.on('error', () => { });
        }
        catch { }
    }
    catch (e) {
        console.warn(`HTTPS server not available: ${e}`);
        console.warn('The bridge will only run on HTTP. Some browsers may block connections from HTTPS pages.');
    }
    // Graceful shutdown
    process.on('SIGINT', () => {
        console.log('\nShutting down...');
        pkcs11Manager.unloadMiddleware();
        server.close();
        process.exit(0);
    });
    process.on('SIGTERM', () => {
        pkcs11Manager.unloadMiddleware();
        server.close();
        process.exit(0);
    });
}
