"use strict";
/*
 * nodejs-license-file
 * https://github.com/bushev/nodejs-license-file
 *
 * Copyright (c) 2016 Yury Bushev
 * Licensed under the MIT license.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.LicenseFile = void 0;
const fs = require("fs");
const crypto = require("crypto");
const stringify = require("json-stable-stringify");
const Config_1 = require("../Config");
const openflow_api_1 = require("@openiap/openflow-api");
/**
 * LicenseFile Class
 */
class LicenseFile {
    constructor() {
        this.template_v1 = [
            '====BEGIN LICENSE====',
            '{{&licenseVersion}}',
            '{{&email}}',
            '{{&expirationDate}}',
            '{{&serial}}',
            '=====END LICENSE====='
        ].join('\n');
        this.template_v2 = [
            '====BEGIN LICENSE====',
            '{{&licenseVersion}}',
            '{{&email}}',
            '{{&expirationDate}}',
            '{{&domain}}',
            '{{&serial}}',
            '=====END LICENSE====='
        ].join('\n');
        this.license_public_key = Config_1.Config.getEnv("license_public_key", "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFyVVJncVpLcHpmbTJISEUxTUJ3YwpmZW5oMk1PZVZQbEJMS2dUM3l2TXIvQlpTSHVPb3pkaFprZ3JoNVVOSVlXZ1JYNVR1SDVoeVZ1ODJzOFhoR2RLCnZ5ajFGWGt2SHVubWp4ZFpGWEhDcjJCVE12Y09VTnZsTk5MWFpTOXh0YXE1cHdBemh0SlpjbmZLdTVHcjZNR1MKbVFxcHpVeDRWRVIyN3hSaVo0QlhXUkQ4MGNVaHAvTHF3UGswdHYwU0FTNTNZVXVRNkJIcWp1MDdHd3lFYzZaUQpJNjlLRVpLaHMvbk5GRHlVOHhwT0JoL0EwR1dMT0wrb01JeU9NSkZ6a2RPZURDQzlnVzNabmJBOHo0dHZUc3VHCmI3c2ZidTJlSE5MK09ucWlzamdFYStrZWlIQXZXaTlCa2dGbWdZWnNaWk5ENnovWkpHUi84MElzekV4THRqL3YKNFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==");
        this.privateKey = Config_1.Config.getEnv("license_public_key", "");
        this.validlicense = false;
        this.licenserror = "Not validated yet";
        this.data = null;
        this._ofid = null;
    }
    ofid(force) {
        if (!openflow_api_1.NoderedUtil.IsNullEmpty(this._ofid) && !force)
            return this._ofid;
        var crypto = require('crypto');
        if (force)
            Config_1.Config.openflow_uniqueid = null;
        const openflow_uniqueid = Config_1.Config.openflow_uniqueid || crypto.createHash('md5').update(Config_1.Config.domain).digest("hex");
        Config_1.Config.openflow_uniqueid = openflow_uniqueid;
        this._ofid = openflow_uniqueid;
        return openflow_uniqueid;
    }
    validate() {
        this.validlicense = false;
        if (openflow_api_1.NoderedUtil.IsNullEmpty(this.license_public_key)) {
            this.licenserror = "Missing license_public_key";
            throw new Error("Missing license_public_key");
        }
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Config_1.Config.license_key)) {
            this.licenserror = "Missing license_key";
            throw new Error("Missing license_key");
        }
        const license_public_key = Buffer.from(this.license_public_key, "base64").toString("utf8");
        const license_key = Buffer.from(Config_1.Config.license_key, "base64").toString("ascii");
        try {
            let data = null;
            try {
                data = this.parse({
                    publicKey: license_public_key,
                    licenseFile: license_key,
                    template: this.template_v2
                });
            }
            catch (error) {
            }
            if (data == null) {
                data = this.parse({
                    publicKey: license_public_key,
                    licenseFile: license_key,
                    template: this.template_v1
                });
            }
            if (data.valid !== true) {
                this.licenserror = "license is not valid";
            }
            else {
                this.data = data.data;
                this.data.expirationDate = new Date(this.data.expirationDate);
                this.data.licenseVersion = parseInt(this.data.licenseVersion);
                const now = new Date();
                this.data.expirationDate = new Date(data.data.expirationDate);
                if (this.data.expirationDate < now) {
                    this.licenserror = "License has expired at " + this.data.expirationDate;
                }
                else {
                    if (!openflow_api_1.NoderedUtil.IsNullEmpty(this.data.domain)) {
                        if (this.data.domain != Config_1.Config.domain) {
                            this.licenserror = "License is not valid for this openflow instance!";
                        }
                        else {
                            this.licenserror = "";
                            this.validlicense = true;
                        }
                    }
                    else {
                        this.licenserror = "";
                        this.validlicense = true;
                    }
                }
            }
        }
        catch (error) {
            this.licenserror = "license validation failed " + error;
        }
        if (!this.validlicense) {
            if (!openflow_api_1.NoderedUtil.IsNullUndefinded(this.validateTimer))
                clearInterval(this.validateTimer);
            throw new Error(this.licenserror);
        }
        else {
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(this.validateTimer)) {
                this.validateTimer = setInterval(() => {
                    try {
                        this.validate();
                    }
                    catch (error) {
                    }
                }, 3600000);
            }
        }
    }
    shutdown() {
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(this.validateTimer)) {
            clearInterval(this.validateTimer);
        }
    }
    /**
     *  Generate license file
     *
     * @param options
     * @param options.data {object|string} - data to sign
     * @param options.template - custom license file template
     * @param [options.privateKeyPath] {string} - path to private key
     * @param [options.privateKey] {string} - private key content
     */
    generate(options) {
        let privateKey;
        if (!options.data) {
            throw new Error('LicenseFile::generate: options.data is required');
        }
        if (!options.template) {
            throw new Error('LicenseFile::generate: options.template is required');
        }
        if (typeof options.privateKey === 'string') {
            privateKey = options.privateKey;
        }
        else if (typeof options.privateKeyPath === 'string') {
            privateKey = fs.readFileSync(options.privateKeyPath, 'utf8');
        }
        else {
            throw new Error('LicenseFile::generate: privateKeyPath or privateKey is required');
        }
        const serial = this._generateSerial(Object.assign({}, options, { privateKey }));
        if (typeof options.data === 'string') {
            options.data = { string: options.data };
        }
        options.data.serial = serial;
        return this._render(options.template, this._prepareDataObject(options.data));
    }
    /**
     * Parse license file
     *
     * @param options
     * @param options.template - custom license file template
     * @param [options.publicKeyPath] {string} - path to public key
     * @param [options.publicKey] {string} - path to public key
     * @param [options.licenseFilePath] {string} - path to license file
     * @param [options.licenseFile] {string} - license file content
     */
    parse(options) {
        let publicKey;
        let licenseFile;
        if (!options.template) {
            throw new Error('LicenseFile::parse: options.template is required');
        }
        if (typeof options.publicKey === 'string') {
            publicKey = options.publicKey;
        }
        else if (typeof options.publicKeyPath === 'string') {
            publicKey = fs.readFileSync(options.publicKeyPath, 'utf8');
        }
        else {
            throw new Error('LicenseFile::parse: publicKeyPath or publicKey is required');
        }
        if (typeof options.licenseFile === 'string') {
            licenseFile = options.licenseFile;
        }
        else if (typeof options.licenseFilePath === 'string') {
            licenseFile = fs.readFileSync(options.licenseFilePath, 'utf8');
        }
        else {
            throw new Error('LicenseFile::parse: licenseFilePath or licenseFile is required');
        }
        const tokens = [];
        const regExpString = options.template.replace(/{{&(\w+)}}/g, (match, token) => {
            tokens.push(token);
            return '(.*)';
        });
        const result = licenseFile.match(new RegExp(regExpString));
        if (!result) {
            throw new Error(`License file corrupted`);
        }
        if (result.length - tokens.length !== 1) {
            throw new Error(`License file corrupted, tokens expected: ${tokens.length}, actual: ${result.length - 1}`);
        }
        const dataObj = {
            data: {}, serial: ""
        };
        for (let i = 0; i < tokens.length; i++) {
            if (tokens[i] === 'serial') {
                dataObj[tokens[i]] = result[i + 1];
            }
            else {
                dataObj['data'][tokens[i]] = result[i + 1];
            }
        }
        const verify = crypto.createVerify('RSA-SHA256');
        verify.update(stringify(dataObj.data));
        const valid = verify.verify(publicKey, dataObj.serial, 'base64');
        return {
            valid: valid,
            serial: dataObj.serial,
            data: dataObj.data
        };
    }
    /**
     *
     * @param options
     * @param options.data
     * @param options.privateKey
     * @private
     */
    _generateSerial(options) {
        const sign = crypto.createSign('RSA-SHA256');
        let data = options.data;
        if (typeof options.data === 'object') {
            data = stringify(this._prepareDataObject(options.data));
        }
        sign.update(data);
        return sign.sign(options.privateKey, 'base64');
    }
    _render(template, data) {
        for (let property in data) {
            if (data.hasOwnProperty(property)) {
                const regExp = new RegExp(`{{&${property}}}`, `g`);
                template = template.replace(regExp, data[property]);
            }
        }
        return template;
    }
    _prepareDataObject(data) {
        const result = {};
        for (let property in data) {
            if (data.hasOwnProperty(property)) {
                result[property] = typeof data[property] === 'string' ? data[property] : data[property] + '';
            }
        }
        return result;
    }
}
exports.LicenseFile = LicenseFile;
//# sourceMappingURL=license-file.js.map