/*
 * Copyright (C) 2016 ~ 2017 Deepin Technology Co., Ltd.
 *
 * Author:     Iceyer <me@iceyer.net>
 *
 * Maintainer: Iceyer <me@iceyer.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "licensekernel.h"

#include <QDebug>
#include <QTemporaryFile>

#include "rsa_crypt.h"
#include "cbc_ase_crypt.h"
#include "license_key.h"
#include "license_file.h"

#include "licensedbus.h"
#include "systeminfo.h"

class LicenseKernelPrivate
{
public:
    LicenseKernelPrivate(LicenseKernel *parent) : q_ptr(parent)
    {
        memset(&m_license, 0, sizeof(m_license));
    }

    struct license_file m_license;

    LicenseKernel *q_ptr;
    Q_DECLARE_PUBLIC(LicenseKernel)
};


LicenseKernel::LicenseKernel(QObject *parent):
    QObject(parent), d_ptr(new LicenseKernelPrivate(this))
{
}

LicenseKernel::~LicenseKernel()
{

}

void LicenseKernel::loadFile(char *keyData)
{
    Q_D(LicenseKernel);
    license_file *lf = (license_file *)keyData;
    if (lf->header.crypt_type == CryptAes) {
        encrypt_init();
        auto padsize = 32 - lf->header.data_size % 32;
        auto decrypt_data = (u8 *) malloc(lf->header.data_size + padsize);
//        qDebug() << padsize << lf->header.data_size ;
        license_aes_decrypt((u8 *)&lf->license, lf->header.data_size + padsize, decrypt_data);
        memcpy((u8 *)&lf->license, decrypt_data, lf->header.data_size);
        free((u8 *)decrypt_data);
        encrypt_cleanup();
        lf->header.crypt_type = CryptNone;
    }

    memcpy(&d->m_license, keyData, sizeof(d->m_license));
}

void LicenseKernel::loadKey(const char *serialkey)
{
    Q_D(LicenseKernel);
    auto SerialkeySize = 20;
    memset(&d->m_license, 0, sizeof(d->m_license));

    auto lf = &d->m_license;
    memset(lf, 0, LICENSE_FILE_SIZE);
    lf->header.version = LICENSE_VERSION;
    lf->header.type = KeyLicense;
    lf->header.crypt_type = CryptNone;
    lf->header.crypt_key = CRYPT_KEY_ASEV1;
    lf->header.data_size = LICENSE_FILE_SIZE - LICENSE_FILE_HEADER_SIZE;

    memcpy(lf->license.serialkey, serialkey, SerialkeySize);

    memset(lf->license.username, '\0', ARRAY_SIZE(lf->license.username));
    memcpy(lf->license.machine_code, SystemInfo::getSystemMachineCode().toStdString().c_str(),
           ARRAY_SIZE(lf->license.machine_code));
    memcpy(lf->license.copyright, SystemInfo::getSystemCopyright().toStdString().c_str(),
           ARRAY_SIZE(lf->license.copyright));
    memcpy(lf->license.edition, SystemInfo::getSystemEdition().toStdString().c_str(),
           ARRAY_SIZE(lf->license.edition));
    memcpy(lf->license.version, SystemInfo::getSystemMajorVersion().toStdString().c_str(),
           ARRAY_SIZE(lf->license.version));
}

void LicenseKernel::deactive()
{
    Q_D(LicenseKernel);
    memset(&d->m_license, 0, sizeof(d->m_license));
}

static bool verifyLicenseSerialKey(const license_file &lf)
{
    char identification[2048] = {0};

    auto licenseEdition = QString::fromLatin1((const char *)lf.license.edition);
    auto systemEdition = SystemInfo::getSystemEdition();
    qDebug() << "verify edition"
             << "license:" << licenseEdition
             << "system:" << systemEdition;
    if (!licenseEdition.isEmpty() && licenseEdition != systemEdition) {
        qDebug() << "edition code not match" << licenseEdition << systemEdition;
        return false;
    }

    // TODO: verify copyright and version

    strcat(strcat(strcat(identification,
                         (const char *)lf.license.copyright),
                  (const char *)lf.license.edition),
           (const char *)lf.license.version);

    return verify_licese_serialkey((const char *)lf.license.serialkey, identification);
}

static bool verifyLicenseRSA(const license_file &lf)
{
    // verfiy rsa
    // TODO: must write pub key to file
    QTemporaryFile tmpFile;
    tmpFile.open();
    QFile rsa(":/license_rsa.pub");
    rsa.open(QIODevice::ReadOnly);
    tmpFile.write(rsa.readAll(), rsa.size());
    tmpFile.close();
    rsa.close();

    encrypt_init();

    EVP_PKEY *vkey = rsa_read_publice_key(tmpFile.fileName().toStdString().c_str());

    if (0 != rsa_verify((unsigned char *)&lf.license,
                        sizeof(struct license_info),
                        lf.signature.rsa,
                        256,
                        vkey)) {
        qWarning() << "RSA verfiy failed";
        encrypt_cleanup();
        return false;
    }

    encrypt_cleanup();
    return true;
}

static bool verifyLicenseFile(const license_file &lf)
{
    auto edition = reinterpret_cast<char *>(const_cast<u8 *>(lf.license.edition));
    auto licenseEdition = QString::fromLatin1(edition);
    auto systemEdition = SystemInfo::getSystemEdition();
    if (licenseEdition != systemEdition) {
        qWarning() << "edition not match."
                   << "File Edition:" << licenseEdition
                   << "System Edition:" << systemEdition;
        return false;
    }

    auto copyright = reinterpret_cast<char *>(const_cast<u8 *>(lf.license.copyright));
    auto licenseCopyright = QString::fromLatin1(copyright);
    auto systemCopyright = SystemInfo::getSystemCopyright();
    if (licenseCopyright != systemCopyright) {
        qWarning() << "copyright not match."
                   << "File Copyright:" << licenseCopyright
                   << "System Copyright:" << systemCopyright;
        return false;
    }

    auto machineCode = reinterpret_cast<char *>(const_cast<u8 *>(lf.license.machine_code));
    auto licenseMachineCode = QString::fromLatin1(machineCode);
    auto systemMachineCode = SystemInfo::getSystemMachineCode();
    if (!licenseMachineCode.isEmpty() && licenseMachineCode != systemMachineCode) {
        qWarning() << "machine code not match."
                   << "File machine code:" << licenseMachineCode
                   << "System machine code:" << systemMachineCode;
        return false;
    }

//    unsigned char digest[512];
//    hmac_sha256_hash((u8 *)&lf->license, sizeof(struct license_info), digest);
//    if (!signature_check(digest, lf->signature.ahs, 32)) {
//        lic_warning("license file signature_check failed");
//        return false;
//    }

    return verifyLicenseRSA(lf);
}

bool LicenseKernel::isActivated() const
{
    Q_D(const LicenseKernel);
    bool actived = false;
    qDebug() << "license type:" << QString("0x%1").arg(d->m_license.header.type, 0, 16);
    switch (d->m_license.header.type) {
    case KeyLicense:
        actived = verifyLicenseSerialKey(d->m_license);
        break;
    case FileLicense:
        actived = verifyLicenseFile(d->m_license);
        break;
    }
    return actived;
}

LicenseKernel::LicenseType LicenseKernel::type() const
{
    Q_D(const LicenseKernel);
    return static_cast<LicenseType>(d->m_license.header.type);
}

QString LicenseKernel::organization() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.username));
}

QString LicenseKernel::product() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.product));
}

QString LicenseKernel::copyright() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.copyright));
}

QString LicenseKernel::edition() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.edition));
}

QString LicenseKernel::authorizeCode() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.authorize_code));
}

QString LicenseKernel::signatureRSA() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.signature.rsa));
}

QString LicenseKernel::machineCode() const
{
    Q_D(const LicenseKernel);
    return reinterpret_cast<char *>(const_cast< u8 *>(d->m_license.license.machine_code));
}

quint64 LicenseKernel::validityBegin() const
{
    Q_D(const LicenseKernel);
    return d->m_license.license.validity_begin;
}

quint64 LicenseKernel::validityEnd() const
{
    Q_D(const LicenseKernel);
    return d->m_license.license.validity_end;
}

QByteArray LicenseKernel::licenseData() const
{
    Q_D(const LicenseKernel);
    license_file lf = d->m_license;

    if (lf.header.crypt_type == CryptNone) {
        u8 *ecrypt_data = reinterpret_cast<u8 *>(malloc(lf.header.data_size * 2));
        license_aes_encrypt(reinterpret_cast<u8 *>(&lf.license),
                            static_cast<int>(lf.header.data_size),
                            ecrypt_data);
        memcpy(reinterpret_cast<void *>(&lf.license), ecrypt_data, lf.header.data_size);
        free(ecrypt_data);
        lf.header.crypt_type = CryptAes;
    }
    return QByteArray::fromRawData(reinterpret_cast<const char *>(&lf), sizeof(lf));
}

qint64 LicenseKernel::licenseSize()
{
    return sizeof(license_file);
}
