"use strict";
/**
 * penguins-eggs
 * class: pxe.ts
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const utils_1 = require("../lib/utils");
const distro_1 = tslib_1.__importDefault(require("./distro"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const http_1 = tslib_1.__importDefault(require("http"));
const node_static_1 = tslib_1.__importDefault(require("node-static"));
const node_path_1 = tslib_1.__importDefault(require("node:path"));
const settings_1 = tslib_1.__importDefault(require("./settings"));
const utils_2 = tslib_1.__importDefault(require("./utils"));
// @ts-ignore
const tftp_1 = tslib_1.__importDefault(require("tftp"));
// @ts-ignore
const etrick_1 = tslib_1.__importDefault(require("etrick"));
/**
* Pxe:
*/
class Pxe {
    constructor() {
        this.verbose = false;
        this.echo = {};
        this.settings = {};
        this.bootLabel = '';
        this.nest = ''; // cuckoo's nest
        this.pxeRoot = '';
        this.eggRoot = '';
        this.vmlinuz = '';
        this.initrdImg = '';
        this.isos = [];
    }
    /**
     * fertilization()
     *
     * cuckoo's nest
     */
    async fertilization() {
        this.settings = new settings_1.default();
        await this.settings.load();
        if (utils_2.default.isLive()) {
            this.eggRoot = this.settings.distro.liveMediumPath;
            if (
            // ArchisoCompatibles
            this.settings.distro.distroId === 'Arch' ||
                this.settings.distro.distroId === 'ArcoLinux' ||
                this.settings.distro.distroId === 'blendOS' ||
                this.settings.distro.distroId === 'EndeavourOS' ||
                this.settings.distro.distroId === 'Garuda' ||
                this.settings.distro.distroId === 'phyOS' ||
                this.settings.distro.distroId === 'RebornOS') {
                this.eggRoot = '/run/archiso/bootmnt/';
                await (0, utils_1.exec)(`mkdir ${this.eggRoot} -p`);
                await (0, utils_1.exec)(`mount /dev/sr0 ${this.eggRoot}`);
            }
        }
        else {
            //this.eggRoot = path.dirname(this.settings.config.snapshot_mnt) + '/iso/'
            this.eggRoot = this.settings.config.snapshot_mnt + 'iso/';
        }
        if (!utils_2.default.isLive() && !fs_1.default.existsSync(this.settings.config.snapshot_mnt)) {
            console.log('no image available, build an image with: sudo eggs produce');
            process.exit();
        }
        this.nest = this.settings.config.snapshot_mnt;
        this.pxeRoot = this.nest + '/pxe';
        /**
        * se pxeRoot non esiste viene creato
        */
        if (!fs_1.default.existsSync(this.pxeRoot)) {
            await (0, utils_1.exec)(`mkdir ${this.pxeRoot} -p`);
        }
        /**
        * Ricerca delle ISOs
        */
        const isos = [];
        /*
        if (!Utils.isLive()) {
          const isos = fs.readdirSync(this.nest)
          for (const iso of isos) {
            if (path.extname(iso) === '.iso') {
              this.isos.push(iso)
            }
    
            this.isos = this.isos.sort()
          }
        }
        */
        /**
        * installed: /home/eggs/mnt/iso/live
        * live: this.iso/live
        */
        const pathFiles = this.eggRoot + 'live';
        const files = fs_1.default.readdirSync(pathFiles);
        for (const file of files) {
            if (this.settings.distro.familyId === 'debian') {
                if (node_path_1.default.basename(file).slice(0, 7) === 'vmlinuz') {
                    this.vmlinuz = node_path_1.default.basename(file);
                }
                if (node_path_1.default.basename(file).slice(0, 6) === 'initrd') {
                    this.initrdImg = node_path_1.default.basename(file);
                }
            }
            else if (this.settings.distro.familyId === 'archlinux') {
                if (node_path_1.default.basename(file).slice(0, 7) === 'vmlinuz') {
                    this.vmlinuz = node_path_1.default.basename(file);
                }
                if (node_path_1.default.basename(file).slice(0, 9) === 'initramfs') {
                    this.initrdImg = node_path_1.default.basename(file);
                }
            }
        }
        /**
        * bootLabel
        */
        this.bootLabel = 'not found';
        if (fs_1.default.existsSync(this.eggRoot + '/.disk/mkisofs')) {
            const a = fs_1.default.readFileSync(this.eggRoot + '/.disk/mkisofs', 'utf-8');
            const b = a.slice(Math.max(0, a.indexOf('-o ') + 3));
            const c = b.slice(0, Math.max(0, b.indexOf(' ')));
            this.bootLabel = c.slice(Math.max(0, c.lastIndexOf('/') + 1));
        }
        console.log(`bootLabel: ${this.bootLabel}`);
        console.log(`vmlinuz: ${this.vmlinuz}`);
        console.log(`initrd: ${this.initrdImg}`);
    }
    /**
     * build
     */
    async build() {
        if (fs_1.default.existsSync(this.pxeRoot)) {
            await this.tryCatch(`rm ${this.pxeRoot} -rf`);
        }
        await this.tryCatch(`mkdir ${this.pxeRoot} -p`);
        await this.tryCatch(`mkdir ${this.pxeRoot} -p`);
        await this.tryCatch(`ln -s ${this.eggRoot}live ${this.pxeRoot}/live`);
        await this.tryCatch(`ln -s ${this.nest}.disk ${this.pxeRoot}/.disk`);
        if (this.settings.distro.distroId === 'ManjaroLinux') {
            await this.tryCatch(`ln -s ${this.eggRoot}manjaro ${this.pxeRoot}/manjaro`);
        }
        else if (this.settings.distro.distroId === 'Arch' || this.settings.distro.distroId === 'RebornOS') {
            await this.tryCatch(`ln -s ${this.eggRoot}arch ${this.pxeRoot}/arch`);
        }
        if (fs_1.default.existsSync(this.eggRoot)) {
            await this.tryCatch(`cp ${this.eggRoot}live/${this.vmlinuz} ${this.pxeRoot}/vmlinuz`, true);
            await this.tryCatch(`chmod 777 ${this.pxeRoot}/vmlinuz`);
            await this.tryCatch(`cp ${this.eggRoot}live/${this.initrdImg} ${this.pxeRoot}/initrd`, true);
            await this.tryCatch(`chmod 777 ${this.pxeRoot}/initrd`);
        }
        // link iso images in pxe
        for (const iso of this.isos) {
            await this.tryCatch(`ln -s ${this.nest}/${iso} ${this.pxeRoot}/${iso}`);
        }
        await this.bios();
        await this.ipxe();
        await this.http();
    }
    /**
     * configure PXE bios
     */
    async bios() {
        console.log('creating cuckoo configuration pxe: BIOS');
        await this.tryCatch(`cp ${__dirname}/../../addons/eggs/theme/livecd/isolinux.theme.cfg ${this.pxeRoot}/isolinux.theme.cfg`);
        await this.tryCatch(`cp ${__dirname}/../../addons/eggs/theme/livecd/splash.png ${this.pxeRoot}/splash.png`);
        /**
             * ipxe.efi
             */
        await this.tryCatch(`ln -s ${__dirname}/../../ipxe/ipxe.efi ${this.pxeRoot}/ipxe.efi`);
        // pxe
        const distro = new distro_1.default();
        await this.tryCatch(`ln -s ${distro.pxelinuxPath}pxelinux.0 ${this.pxeRoot}/pxelinux.0`);
        await this.tryCatch(`ln -s ${distro.pxelinuxPath}lpxelinux.0 ${this.pxeRoot}/lpxelinux.0`);
        // syslinux
        await this.tryCatch(`ln -s ${distro.syslinuxPath}ldlinux.c32 ${this.pxeRoot}/ldlinux.c32`);
        await this.tryCatch(`ln -s ${distro.syslinuxPath}vesamenu.c32 ${this.pxeRoot}/vesamenu.c32`);
        await this.tryCatch(`ln -s ${distro.syslinuxPath}libcom32.c32 ${this.pxeRoot}/libcom32.c32`);
        await this.tryCatch(`ln -s ${distro.syslinuxPath}libutil.c32 ${this.pxeRoot}/libutil.c32`);
        await this.tryCatch(`ln -s ${distro.memdiskPath}memdisk ${this.pxeRoot}/memdisk`);
        await this.tryCatch(`mkdir ${this.pxeRoot}/pxelinux.cfg`);
        let content = '';
        content += '# eggs: pxelinux.cfg/default\n';
        content += '# search path for the c32 support libraries (libcom32, libutil etc.)\n';
        content += 'path\n';
        content += 'include isolinux.theme.cfg\n';
        content += 'UI vesamenu.c32\n';
        content += '\n';
        content += `menu title cuckoo: when you need a flying PXE server! ${utils_2.default.address()}\n`;
        content += 'PROMPT 0\n';
        content += 'TIMEOUT 200\n';
        content += '\n';
        content += 'label egg\n';
        content += `menu label ${this.bootLabel.replace('.iso', '')}\n`;
        if (this.settings.distro.familyId === 'debian') {
            /**
            * DEBIAN
            */
            const clid = this.settings.distro.codenameLikeId;
            if (clid === 'bionic' || clid === 'stretch' || clid === 'jessie') {
                content += 'kernel vmlinuz\n';
                content += `append initrd=initrd boot=live config noswap noprompt fetch=http://${utils_2.default.address()}/live/filesystem.squashfs\n`;
            }
            else {
                content += `kernel http://${utils_2.default.address()}/vmlinuz\n`;
                content += `append initrd=http://${utils_2.default.address()}/initrd boot=live config noswap noprompt fetch=http://${utils_2.default.address()}/live/filesystem.squashfs\n`;
            }
        }
        else if (distro.familyId === 'archlinux') {
            /**
            * ARCH LINUX
            */
            let tool = 'archiso';
            if (distro.distroId === 'ManjaroLinux') {
                tool = 'miso';
            }
            content += `kernel http://${utils_2.default.address()}/vmlinuz\n`;
            content += `append initrd=http://${utils_2.default.address()}/initrd boot=live config noswap noprompt ${tool}_http_srv=http://${utils_2.default.address()}/\n`;
            content += 'sysappend 3\n';
            content += '\n';
        }
        if (this.isos.length > 0) {
            content += 'menu separator\n';
            for (const iso of this.isos) {
                content += '\n';
                content += `label ${iso}\n`;
                content += `menu label ${iso}\n`;
                content += `kernel http://${utils_2.default.address()}/memdisk\n`;
                content += `initrd http://${utils_2.default.address()}/${iso}\n`;
                content += 'append iso raw sysappend 3\n';
            }
        }
        const file = `${this.pxeRoot}/pxelinux.cfg/default`;
        fs_1.default.writeFileSync(file, content);
    }
    /**
    *
    */
    async ipxe() {
        console.log('creating cuckoo configuration pxe: UEFI');
        let content = '#!ipxe\n';
        content += 'dhcp\n';
        content += 'set net0/ip=dhcp\n';
        content += `console --picture http://${utils_2.default.address()}/splash.png -x 1024 -y 768\n`;
        content += 'goto start ||\n';
        content += '\n';
        content += ':start\n';
        content += `set server_root http://${utils_2.default.address()}:80/\n`;
        const serverRootVars = '${server_root}';
        content += `menu cuckoo: when you need a flying PXE server! ${utils_2.default.address()}\n`;
        content += 'item --gap boot from ovarium\n';
        content += `item egg-menu \${space} ${this.bootLabel.replaceAll('.iso', '')}\n\n`;
        if (this.isos.length > 0) {
            content += 'item --gap boot iso images\n';
            for (const iso of this.isos) {
                const menu = iso;
                const label = menu;
                content += `item ${menu} \${space} ${label}\n\n`;
            }
        }
        content += 'item --gap boot from internet\n';
        content += 'item netboot ${space} netboot\n';
        content += 'choose target || goto start\n';
        content += 'goto ${target}\n';
        content += '\n';
        content += ':egg-menu\n';
        content += `kernel http://${utils_2.default.address()}/vmlinuz\n`;
        content += `initrd http://${utils_2.default.address()}/initrd\n`;
        /**
             * CORRECT:
             * content += `imgargs vmlinuz fetch=http://${Utils.address()}/live/filesystem.squashfs boot=live dhcp initrd=initrd ro\n`
             */
        if (this.settings.distro.familyId === 'debian') {
            /**
            * DEBIAN
            */
            content += `imgargs vmlinuz fetch=http://${utils_2.default.address()}/live/filesystem.squashfs boot=live dhcp initrd=initrd ro\n`;
        }
        else if (this.settings.distro.familyId === 'archlinux') {
            /**
                     * ARCH LINUX
                     */
            let tool = 'archiso';
            if (this.settings.distro.codenameId === 'Qonos' || this.settings.distro.codenameId === 'Ruah' || this.settings.distro.codenameId === 'Sikaris' || this.settings.distro.codenameId === 'UltimaThule') {
                tool = 'miso';
            }
            content += `imgargs vmlinuz ${tool}_http_srv=http://${utils_2.default.address()}/ boot=live dhcp initrd=initrd ro\n`;
            // content += 'ipappend 3\n'
        }
        content += 'sleep 5\n';
        content += 'boot || goto start\n\n';
        if (this.isos.length > 0) {
            for (const iso of this.isos) {
                const menu = iso.replace('.iso', '');
                content += `:${menu}\n`;
                content += `sanboot ${serverRootVars}/${iso}\n`;
                content += 'boot || goto start\n\n';
            }
        }
        /**
             * netboot.xyz
             */
        content += ':netboot\n';
        content += 'ifopen net0\n';
        content += 'set conn_type https\n';
        content += 'chain --autofree https://boot.netboot.xyz/menu.ipxe || echo HTTPS failed... attempting HTTP...\n';
        content += 'set conn_type http\n';
        content += 'chain --autofree http://boot.netboot.xyz/menu.ipxe || echo HTTP failed, localbooting...\n';
        content += 'goto start\n\n';
        const file = `${this.pxeRoot}/autoexec.ipxe`;
        fs_1.default.writeFileSync(file, content);
    }
    /**
     * configure PXE http server
     */
    async http() {
        console.log('creating cuckoo configuration: PXE html');
        const file = `${this.pxeRoot}/index.html`;
        let content = '';
        content += '<html><title>Penguin\'s eggs PXE server</title>';
        content += '<div style="background-image:url(\'/splash.png\');background-repeat:no-repeat;width: 640;height:480;padding:5px;border:1px solid black;">';
        content += '<h1>Cucko PXE server</h1>';
        content += `<body>address: <a href=http://${utils_2.default.address()}>${utils_2.default.address()}</a><br/>`;
        if (!utils_2.default.isLive()) {
            content += 'Serving:<li>';
            for (const iso of this.isos) {
                content += `<ul><a href='http://${utils_2.default.address()}/${iso}'>${iso}</a></ul>`;
            }
            content += '</li>';
        }
        else {
            content += 'started from live iso image<br/>';
        }
        content += 'source: <a href=\'https://github.com/pieroproietti/penguins-eggs\'>https://github.com/pieroproietti/penguins-eggs</a><br/>';
        content += 'manual: <a href=\'https://penguins-eggs.net/book/italiano9.2.html\'>italiano</a>, <a href=\'https://penguins--eggs-net.translate.goog/book/italiano9.2?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en\'>translated</a><br/>';
        content += 'discuss: <a href=\'https://t.me/penguins_eggs\'>Telegram group<br/></body</html>';
        fs_1.default.writeFileSync(file, content);
    }
    /**
     *
     * @param dhcpOptions
     */
    dhcpStart(dhcpOptions) {
        new etrick_1.default(dhcpOptions);
    }
    /**
    * start http server for images
    *
    */
    async httpStart() {
        const port = 80;
        const httpRoot = this.pxeRoot + '/';
        console.log('http listening: 0.0.0.0:' + port);
        const file = new (node_static_1.default.Server)(httpRoot);
        http_1.default.createServer(function (req, res) {
            file.serve(req, res);
        }).listen(port);
    }
    /**
     * start tftp
     */
    async tftpStart(tftpOptions) {
        const tftpServer = tftp_1.default.createServer(tftpOptions);
        tftpServer.on('error', function (error) {
            // Errors from the main socket
            // The current transfers are not aborted
            console.error(error);
        });
        tftpServer.on('request', function (req, res) {
            req.on('error', function (error) {
                // Error from the request
                // The connection is already closed
                console.error('[' + req.stats.remoteAddress + ':' + req.stats.remotePort + '] (' + req.file + ') ' + error.message);
            });
        });
        tftpServer.listen();
    }
    /**
     *
     * @param cmd
     */
    async tryCatch(cmd = '', echo = false) {
        try {
            if (echo) {
                console.log(cmd);
            }
            await (0, utils_1.exec)(cmd, this.echo);
        }
        catch (error) {
            console.log(`Error: ${error}`);
            await utils_2.default.pressKeyToExit(cmd);
        }
    }
}
exports.default = Pxe;
