<?php
if (!class_exists('crm_hardlock')) require_once("crm_hardlock.class.php");

class crm_test_API extends Database
{
    private $code;
    private $password;
    private $isid;
    private $actDate;
    private $actCode;
    private $sellDate;
    private $hlStat;
    private $installCode;

    public static $programYears = array(
        '19', // 2019
        '20', // 2020
        '21', // 2021
        '22', // 2022
        '23' // 2023
    );

    public function __construct($_code, $_password)
    {
        error_reporting(E_ERROR);
        parent::__construct();

        $this->code     = $_code;
        $this->password = $_password;
    }

    public function setIsid($_isid)
    {
        $this->isid           = $_isid;
    }
    public function setActDate($_actDate)
    {
        $this->actDate        = $_actDate;
    }
    public function setActCode($_actCode)
    {
        $this->actCode        = $_actCode;
    }
    public function setHlStat($_hlStat)
    {
        $this->hlStat         = $_hlStat;
    }
    public function setSellDate($_sellDate)
    {
        $this->sellDate       = $_sellDate;
    }
    public function SetInstallCode($_installCode)
    {
        $this->installCode    = $_installCode;
    }

    private function encode()
    {
        $date   = new DateTime();
        $date   = $date->format('Y-m-d');
        $number = explode("-", $date);
        $code   = ((($number[0] * 4) * 555) + (($number[1] * 5) * 333) + ($number[2] * 72)) / 90;

        return (int)$code;
    }

    public function checkCode()
    {
        return $this->code == $this->encode();
    }

    public function updateFizetve()
    {
        $updateQuery = "UPDATE `" . CRMADATBAZIS . "`.`h_programs` SET prFizetve = ?, prActCode = NULL WHERE prPass = ?";
        $this->query($updateQuery, array('ss', $this->actDate, $this->password));
        $query = "SELECT prFizetve FROM `" . CRMADATBAZIS . "`.`h_programs` WHERE prPass = ?;";
        $this->query($query, array('s', $this->password));
        $res = $this->fetchNext();

        return $res['prFizetve'];
    }

    public function countFloating($floatings, &$message)
    {
        $floatingsCount = count($floatings);

        $query = "SELECT * FROM `" . SYNCADATBAZIS . "`.`floating_licenses` WHERE hlNum = ?";
        $this->query($query, array('s', substr($this->password, 0, 6)));
        $floatingLicenses = $this->fetchAssoc();

        if (count($floatingLicenses) != $floatingsCount) {
            $message = "error: nem egyeznek meg az active floating futo licencek";
            return false;
        }

        for ($i = 0; $i < count($floatingLicenses); $i++) {
            if (!in_array($floatingLicenses[$i]['seatID'], $floatings)) {
                $message = "error: nincs benne a futo program az aktiv floating licencek kozott: " . $floatingLicenses[$i]['seatID'];
                return false;
            }
        }

        return true;
    }

    public function countRegistration($action)
    {
        $query = "SELECT id FROM `" . CRMADATBAZIS . "`.`p_serial_isid` WHERE serial = ?";

        if ($action == 'active') $query .= " and aktiv = 1";
        elseif ($action == 'inactive') $query .= " and aktiv = 0";

        $this->query($query, array('s', $this->password));
        $res = $this->fetchAssoc();

        return count($res);
    }

    public function countActivationCode()
    {
        $query = "SELECT * FROM `" . CRMADATBAZIS . "`.`h_programs` WHERE prPass = ? AND prActCode != '' AND prActCode IS NOT NULL";
        $this->query($query, array('s', $this->password));
        $res = $this->fetchAssoc();

        return count($res);
    }

    public function checkActivationCode()
    {
        $query = "SELECT * FROM `" . CRMADATBAZIS . "`.`h_programs` WHERE prPass = ? AND prActCode LIKE ?";
        $this->query($query, array('ss', $this->password, "%-{$this->isid}-%"));
        $res = $this->fetchNext();

        return !empty($res);
    }

    public function isFloating()
    {
        $hlNum  = crm_hardlock::GetHlnumFromPassword($this->password);

        $this->query("SELECT isFloating FROM cl_hlusers.hardlock WHERE hlNum = ?", array('s', $hlNum));
        $hardlock = $this->fetchNext();

        $ver8and9 = crm_hardlock::GetVer8And9FromPassword($this->password);

        return !empty($hardlock) && $hardlock['isFloating'] == 1 && $ver8and9 >= 40;
    }

    public function clearProgram($clr_info = false)
    {
        $this->delete(CRMADATBAZIS . '.p_serial_isid', array('serial' => $this->password), 's');
        $this->delete(CRMADATBAZIS . '.userstats2013', array('usPwd' => $this->password), 's');
        $this->query("UPDATE `" . CRMADATBAZIS . "`.`h_programs` SET prActCode = '' WHERE prPass = ?", array('s', $this->password));

        $this->query("SELECT * FROM `" . CRMADATBAZIS . "`.`p_serial_isid` WHERE serial = ?", array('s', $this->password));
        $res = $this->fetchAssoc();

        $hlNum  = crm_hardlock::GetHlnumFromPassword($this->password);

        if ($clr_info)
            $this->delete(CRMADATBAZIS . '.h_history', array('hiHlNum' => $hlNum), 's');

        $this->query("SELECT isFloating FROM cl_hlusers.hardlock WHERE hlNum = ?", array('s', $hlNum));
        $hardlock = $this->fetchNext();

        $ver8and9 = crm_hardlock::GetVer8And9FromPassword($this->password);

        if (!empty($hardlock) && $hardlock['isFloating'] == 1 && $ver8and9 >= 40)
            $this->delete(SYNCADATBAZIS . '.floating_licenses', array('hlNum' => $hlNum), 's');

        return empty($res);
    }

    public function setDisabled($set_disabled)
    {
        $hlNum  = crm_hardlock::GetHlnumFromPassword($this->password);
        $date   = date('Y-m-d');

        if ($set_disabled) return crm_hardlock::DeactivateHardlock($hlNum, 36, "Test deaktiválás", $date);
        else return crm_hardlock::ActivateHardlock($hlNum, 36, "Test aktiválás");
    }

    public function generateTimeCodeWithInstCode($isid, $isTwTw)
    {
        if ($this->actDate == '2050-01-01') $dayInterval = 3583;
        else {
            if (Codegen::IsSoftwareKey($this->password))
                $installDate = crm_hardlock::GetInstallationDateFromInstallationCode($this->installCode, $isTwTw);
            else {
                if ($isTwTw)
                    $installDate = crm_hardlock::getTwTwRootTimeString();
                else
                    $installDate = $this->sellDate;
            }
            $dayInterval = Codegen::GetDayInterval($installDate, $this->actDate);
        }
        return Codegen::GenerateTimeCode($this->password, $isid, $dayInterval);
    }

    public function registerUnregisterTest(&$error_string, $hlNum, $verID)
    {
        $this->actDate  = Date('Y-m-d', strtotime('+10 days'));
        $isid           = 'A001';
        $npid           = 'Automata_test_00000000';
        $comp_name      = 'CRM Automata Test';
        $datas          = array();

        $programs = crm_hardlock::getProgramsFromHardlock($hlNum, $verID);
        if (empty($programs)) {
            $error_string .= "error: nincs ilyen program (verzio: {$verID}) - hardlock: {$hlNum}";
            return false;
        }

        $this->query("SELECT * FROM `cl_hlusers`.`hardlock` WHERE hlNum = ?", array('s', $hlNum));
        $res = $this->fetchNext();
        $this->hlStat = $res['hlStat'];

        foreach ($programs as $program) {
            $this->password = $program['prPass'];
            $this->clearProgram(true);
            $this->updateFizetve();

            // TODO: A crm_hardlock::startQuery() fgv-be az elejen belefut a $crackedProgram-ba!
            /*if (substr($this->password, 0, 6) == '371001' || substr($this->password, 0, 6) == '370001')
                continue;*/

            if (!Codegen::IsSoftwareKey($this->password)) $isid = crm_hardlock::GetIsidFromPasswordForHardwareKeys($this->password);

            $softwarekey        = Codegen::IsSoftwareKey($this->password);
            $this->installCode  = Codegen::GenerateTimeCode($this->password, $isid, 1);

            $register_result = crm_hardlock::RegisterProgram($this->password, $isid, $this->installCode, $npid, $comp_name, '');
            if (substr($register_result, 0, 6) == "error:") {
                $error_string .= "RegisterProgram(): {$register_result} - {$this->password} <br/>";
                return false;
            }

            $aktkod = crm_hardlock::GetOrGenerateTimeCode($this->password, $isid, $this->installCode, $npid, $comp_name);
            if (substr($aktkod, 0, 6) == "error:") {
                $error_string .= "GetOrGenerateTimeCode(): {$aktkod} - {$this->password} <br/>";
                return false;
            }

            $check_date = crm_hardlock::checkActDate($this->password, $isid);
            if (!$check_date) {
                $error_string .= "checkActDate(): {$check_date} - {$this->password} <br/>";
                return false;
            }

            $this->sellDate = $program['prSellDate'];
            $isTwTw         = ($program['twtw'] == 1 || $verID >= 21) ? 'true' : '';
            $date           = new DateTime();

            for ($i = 0; $i <= 3; $i++) {
                $tip_result = '';

                if ($i <= 2 && $i != 0) {
                    $date->modify('+1 month');
                    $this->actDate = $date->format('Y-m') . '-01';
                }

                if ($i == 3) $this->actDate = '2050-01-01';

                $this->updateFizetve();
                $this->actCode = $this->generateTimeCodeWithInstCode($isid, $isTwTw);

                if ($verID >= 21) $tip_result = $this->launchStartQuery($error_string, $verID, $isid, $npid);
                else $tip_result = $this->launchTips($error_string, $verID, $isid, $npid, $isTwTw);

                if ($tip_result == false) return false;
            }

            if ($softwarekey) {
                $unregister_result = crm_hardlock::UnRegisterProgram($this->password, $isid, '', $comp_name);
                if ($unregister_result == false) {
                    $activationError = crm_hardlock::GetActivationError(crm_hardlock::UNREGISTER_FAILED);
                    $error_string   .= "UnRegisterProgram(): {$activationError} - {$this->password} <br/>";
                    return false;
                }
            }
        }
        return true;
    }

    public function launchRegisterTest(&$error_string, $verID)
    {
        $result = $this->registerUnregisterTest($error_string, '371001', $verID);
        if ($result == false) return $result;

        $result = $this->registerUnregisterTest($error_string, '370001', $verID);
        if ($result == false) return $result;

        return true;
    }

    public function generateStartQueryUrl($isid, $npid)
    {
        $this->query("SELECT prTypeID FROM `cl_hlusers`.`h_programs` WHERE prPass = ?", array('s', $this->password));
        $program = $this->fetchNext();

        $ver8and9   = crm_hardlock::GetVer8And9FromPassword($this->password);
        $launchYear = crm_hardlock::GetLaunchYearFromVer8And9($ver8and9);

        if ($program['prTypeID'] == 76) // LIVE
            $this->query("SELECT pkgid FROM `www_archline_hu`.`version` WHERE ev = ? AND projectid LIKE '%Live' ORDER BY pkgid DESC", array('i', $launchYear));
        else // Prof.
            $this->query("SELECT pkgid FROM `www_archline_hu`.`version` WHERE ev = ? AND projectid NOT LIKE '%Live' AND projectid NOT LIKE '%Namirial%' ORDER BY pkgid DESC", array('i', $launchYear));

        $version            = $this->fetchNext();
        $this->installCode  = Codegen::GenerateTimeCode($this->password, $isid, 1);
        $session            = 'MHV5Z242S29VaEk5VyRzZWV5UE9TUQ';
        $decodedSession     = base64_decode($session);
        $privatekey         = Webform::GetWebformPrivateKey();

        $param  = 'serialNumber=' . $this->password;
        $param .= '&winchesterAdatai=1793730914';
        $param .= '&build=' . $version['pkgid'];
        $param .= '&countryPhoneID=36';
        $param .= '&installationCode=' . $this->installCode;
        $param .= '&osMajorMinorBuild=10.0.19041';
        $param .= '&vidMan=';
        $param .= '&vidType=';
        $param .= '&vidDrv=';
        $param .= '&installationID=' . $npid;
        $param .= '&partnerID=';
        $param .= '&clientName=';
        $param .= '&clientEmail=';
        $param .= '&clientCompany=';
        $param .= '&clientTelephone=';
        $param .= '&newsletter=false';
        $param .= '&reenabledCounter=';
        $param .= '&twentyTwenty=true';
        $param .= '&languageID=hun';
        $param .= '&wentToGray=false';

        $encoded_param = Webform::encode($privatekey, $decodedSession, $param);

        return "https://www.archline.hu/maintenance/webform/startQuery.php?session={$session}&param={$encoded_param}";
    }

    public function generateTipUrl($isid, $npid, $isTwTw)
    {
        if ($isTwTw) $isTwTw = 'true';

        $this->query("SELECT prTypeID FROM `cl_hlusers`.`h_programs` WHERE prPass = ?", array('s', $this->password));
        $program = $this->fetchNext();

        $ver8and9   = crm_hardlock::GetVer8And9FromPassword($this->password);
        $launchYear = crm_hardlock::GetLaunchYearFromVer8And9($ver8and9);

        if ($program['prTypeID'] == 76) // LIVE
            $this->query("SELECT pkgid FROM `www_archline_hu`.`version` WHERE ev = ? AND projectid LIKE '%Live' ORDER BY pkgid DESC", array('i', $launchYear));
        else // Prof.
            $this->query("SELECT pkgid FROM `www_archline_hu`.`version` WHERE ev = ? AND projectid NOT LIKE '%Live' AND projectid NOT LIKE '%Namirial%' ORDER BY pkgid DESC", array('i', $launchYear));

        $version            = $this->fetchNext();
        $this->installCode  = Codegen::GenerateTimeCode($this->password, $isid, 1);
        $build              = $version['pkgid'];
        $ctrID              = 36;
        $compID             = 1793730914; //tipeknel nem hasznalt
        $osData             = "10.0.18362.1.green.848"; // tekinthetjuk konstansnak
        $strCollectedInfo   = "#{$this->password}#{$compID}#{$build}#{$ctrID}#{$this->installCode}#{$osData}####{$npid}######0#0#{$isTwTw}";

        $keyDec     = "0052";
        $encoderKey = hexdec($keyDec);
        $encoded    = $keyDec;

        $strHex         = bin2hex($strCollectedInfo);
        $hexArray       = str_split($strHex, 2);
        $arrayKeys      = array_keys($hexArray);
        $lastArrayKey   = array_pop($arrayKeys);

        for ($i = 0; $i < count($hexArray); $i++) {
            if ($i % 2 == 0) {
                $a = $hexArray[$i]; // "23"
                $b = $hexArray[$i + 1]; // "33"

                if ($i == $lastArrayKey) $b = '00';

                sscanf($a, '%x', $low);
                sscanf($b, '%x', $high);
                $value = $high * 256 + $low; //0x3323
                $encoded .= sprintf('%04X', $value ^ $encoderKey);
            }
        }
        $encodedSession = base64_encode($encoded);
        return "https://www.archlinexp.com/index.php?menu=tips2012&lang=eng&version=0&session={$encodedSession}&launched=test";
    }

    public function launchStartQuery(&$error_string, $verID, $isid, $npid)
    {
        $arrContextOptions = stream_context_create(
            array(
                "ssl" => array(
                    "verify_peer"       => false,
                    "verify_peer_name"  => false
                )
            )
        );

        $tip_url    = $this->generateStartQueryUrl($isid, $npid);
        $result     = file_get_contents($tip_url, false, $arrContextOptions);
        $result     = explode('session', $result);
        $result     = '?session=' . $result[1];

        $session        = Webform::get_valueFromStringUrl($result, "session");
        $result_ok      = Webform::get_valueFromStringUrl($result, "result_ok");
        $decodedSession = base64_decode($session);
        $privateKey     = Webform::GetWebformPrivateKey();
        $paramAML       = Webform::decode($privateKey, $decodedSession, $result_ok);

        $actCode        = AML::getAttribute('AC', $paramAML);
        $virtualization = AML::getAttribute('VIR', $paramAML) ? 1 : 2;
        $tipRegnr       = AML::getAttribute('REG', $paramAML);
        $utst           = time();

        if ($actCode == '') {
            $error_string .= "launchTips(): error: nincs aktivalokod! Serial: " . $this->password;
            return false;
        }

        /*if ($actCode != $this->actCode) {
            $error_string .= "launchTips(): error: a program aktivalo kodja nem egyezik a tip aktivalokoddal! (Tip: {$actCode} || program: {$this->actCode})";
            return false;
        }*/

        if ($virtualization != $this->hlStat) {
            $error_string .= "launchTips(): error: a program letiltas erteke nem az elvart";
            return false;
        }

        $timeCode = $this->generateTimeCodeWithInstCode($isid, true);

        if ($timeCode != $actCode && $_SERVER['REMOTE_ADDR'] == '176.241.31.192') {
            $error_string .= "launchTips(): error: nem jol legeneralt idokod! (Tips: {$actCode} || helyes: {$timeCode})";
            return false;
        }

        if (Codegen::IsSoftwareKey($this->password)) {
            if (crm_hardlock::IsRegistrationActive($this->password, $isid, $npid) == false && $tipRegnr == crm_hardlock::Yes($utst, 39)) {
                $error_string .= "launchTips(): error: a program fut, de nem kene!";
                return false;
            }

            if (crm_hardlock::IsRegistrationActive($this->password, $isid, $npid) && $tipRegnr == crm_hardlock::No($utst, 39)) {
                $error_string .= "launchTips(): error: a program nem fut, de futnia kene!";
                return false;
            }
        }
        return true;
    }

    public function launchTips(&$error_string, $verID, $isid, $npid, $isTwTw)
    {
        $arrContextOptions = stream_context_create(
            array(
                "ssl" => array(
                    "verify_peer"       => false,
                    "verify_peer_name"  => false
                )
            )
        );

        $tip_url    = $this->generateTipUrl($isid, $npid, $isTwTw);
        $result     = file_get_contents($tip_url, false, $arrContextOptions);
        if (strstr($result, '<Tips>') === false || strstr($result, '</Tips>') === false) {
            $error_string .= 'Tip test failed! Not XML returned (probably syntax error)';
            return false;
        }

        libxml_set_streams_context($arrContextOptions);

        $xml            = simplexml_load_file($tip_url, "SimpleXMLElement", LIBXML_NOCDATA);
        $data           = json_decode(json_encode($xml), true);
        $actCode        = $data['actcode'];
        $virtualization = $data['Virtualization'] ? 1 : 2;
        $tipRegnr       = $data['regnr'];
        $utst           = time();

        if ($actCode == '') {
            $error_string .= "launchTips(): error: nincs aktivalokod!";
            return false;
        }

        if ($actCode != $this->actCode) {
            $error_string .= "launchTips(): error: a program aktivalo kodja nem egyezik a tip aktivalokoddal! (Tip: {$actCode} || program: {$this->actCode})";
            return false;
        }

        if ($virtualization != $this->hlStat) {
            $error_string .= "launchTips(): error: a program letiltas erteke nem az elvart";
            return false;
        }

        $timeCode = $this->generateTimeCodeWithInstCode($isid, $isTwTw);

        if ($timeCode != $actCode) {
            $error_string .= "launchTips(): error: nem jol legeneralt idokod! (Tips: {$actCode} || helyes: {$timeCode})";
            return false;
        }

        if (Codegen::IsSoftwareKey($this->password)) {
            if (crm_hardlock::IsRegistrationActive($this->password, $isid, $npid) == false && $tipRegnr == crm_hardlock::Yes($utst, 39)) {
                $error_string .= "launchTips(): error: a program fut, de nem kene!";
                return false;
            }

            if (crm_hardlock::IsRegistrationActive($this->password, $isid, $npid) && $tipRegnr == crm_hardlock::No($utst, 39)) {
                $error_string .= "launchTips(): error: a program nem fut, de futnia kene!";
                return false;
            }
        }
        return true;
    }

    public function launchTests()
    {
        $error_string   = '';
        $test_result    = true;

        for ($i = 0; $test_result && $i < count(self::$programYears); $i++) $test_result = $this->launchRegisterTest($error_string, self::$programYears[$i]);

        if ($test_result) $error_string = "A teszt sikeresen lefutott, hiba nelkul!<br/> {$error_string}";

        return $error_string;
    }
}

class Authenticate extends Database
{
    private $username;
    private $password;
    private $authCode;
    private $authSession;

    private $users = array();

    public function __construct()
    {
        parent::__construct();
    }

    public function setAuthCode($_authCode)
    {
        $this->authCode       = $_authCode;
    }
    public function setAuthSession($_authSession)
    {
        $this->authSession    = $_authSession;
    }
    public function setUsername($_username)
    {
        $this->username       = $_username;
    }
    public function setPassword($_password)
    {
        $this->password       = $_password;
    }

    public function setUsers($_users)
    {
        foreach ($_users as $user) $this->users[$user['username']] = $user['password'];
    }
    private function checkPassword()
    {
        return (sha1($this->password) == $this->users[$this->username]);
    }

    public function auth()
    {
        if ($_SERVER['HTTP_HOST'] != 'dev.cadline.hu') return true;

        $sql = "SELECT m.strMail AS username, a.admPass AS password FROM `" . CRMADATBAZIS . "`.`h_admins` a
            INNER JOIN `" . CRMADATBAZIS . "`.`managers` m ON m.admID = a.admID";
        $this->query($sql);
        $admins = $this->fetchAssoc();
        $this->setUsers($admins);

        $this->setUsername($_SERVER['PHP_AUTH_USER']);
        $this->setPassword($_SERVER['PHP_AUTH_PW']);

        if (!$this->validate() && !$this->checkCode()) {
            header('WWW-Authenticate: Basic realm="Restricted area"');
            header('HTTP/1.0 401 Unauthorized');
            die("Not authorized");
        }

        return true;
    }

    private function checkCode()
    {
        if ($this->authSession == '' || $this->authCode == '') return false;

        $privateKey = Webform::GetWebformPrivateKey();
        $decoded    = base64_decode($this->authCode);
        $decoded    = Webform::decode($privateKey, $this->authSession, $decoded);

        return (base64_decode($decoded) == date('Y-m-d'));
    }

    private function validate()
    {
        $exist = array_key_exists($this->username, $this->users);
        if ($exist && $this->checkPassword()) return true;
        else return false;
    }
}
