235 lines
7.9 KiB
PHP
235 lines
7.9 KiB
PHP
<?php
|
|
|
|
class LicenseService {
|
|
private static $remote_api_url = null;
|
|
|
|
private static function getApiUrl() {
|
|
if (self::$remote_api_url === null) {
|
|
self::$remote_api_url = getenv('LICENSE_API_URL') ?: 'https://omanapp.cloud/meezan_register';
|
|
}
|
|
return self::$remote_api_url;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of days remaining in the trial.
|
|
*/
|
|
public static function getTrialRemainingDays() {
|
|
require_once __DIR__ . '/../db/config.php';
|
|
$stmt = db()->prepare("SELECT trial_started_at FROM system_license LIMIT 1");
|
|
$stmt->execute();
|
|
$res = $stmt->fetch();
|
|
|
|
if (!$res || !$res['trial_started_at']) {
|
|
self::startTrial();
|
|
return 15;
|
|
}
|
|
|
|
$started = strtotime($res['trial_started_at']);
|
|
$days_elapsed = floor((time() - $started) / (24 * 60 * 60));
|
|
|
|
return (int) max(0, 15 - $days_elapsed);
|
|
}
|
|
|
|
/**
|
|
* Initializes the trial period.
|
|
*/
|
|
private static function startTrial() {
|
|
require_once __DIR__ . '/../db/config.php';
|
|
$stmt = db()->prepare("SELECT COUNT(*) FROM system_license");
|
|
$stmt->execute();
|
|
if ($stmt->fetchColumn() == 0) {
|
|
db()->exec("INSERT INTO system_license (status, trial_started_at) VALUES ('trial', NOW())");
|
|
} else {
|
|
db()->exec("UPDATE system_license SET trial_started_at = NOW() WHERE trial_started_at IS NULL");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a unique fingerprint for this server environment.
|
|
*/
|
|
public static function getFingerprint() {
|
|
$data = [
|
|
php_uname('n'), // Nodename (hostname)
|
|
php_uname('m'), // Machine type
|
|
$_SERVER['SERVER_ADDR'] ?? '127.0.0.1',
|
|
PHP_OS
|
|
];
|
|
return hash('sha256', implode('|', $data));
|
|
}
|
|
|
|
/**
|
|
* Checks if the system is currently activated or within trial period.
|
|
*/
|
|
public static function canAccess() {
|
|
if (self::isActivated()) return true;
|
|
|
|
$daysLeft = self::getTrialRemainingDays();
|
|
return $daysLeft > 0;
|
|
}
|
|
|
|
/**
|
|
* Checks if the system is currently activated with a valid license key.
|
|
*/
|
|
public static function isActivated() {
|
|
require_once __DIR__ . '/../db/config.php';
|
|
$stmt = db()->prepare("SELECT * FROM system_license WHERE status = 'active' LIMIT 1");
|
|
$stmt->execute();
|
|
$license = $stmt->fetch();
|
|
|
|
if (!$license) return false;
|
|
|
|
// 1. Verify fingerprint matches (Physical Protection)
|
|
if ($license['fingerprint'] !== self::getFingerprint()) {
|
|
return false;
|
|
}
|
|
|
|
// 2. Periodic Remote Validation (e.g., every 7 days)
|
|
// This ensures if you disable a key on your server, the client will stop working.
|
|
$last_check = strtotime($license['activated_at']); // In real use, store 'last_verified_at'
|
|
if (time() - $last_check > (7 * 24 * 60 * 60)) {
|
|
// It's been more than 7 days, let's re-verify
|
|
$res = self::callRemoteApi('/verify', [
|
|
'license_key' => $license['license_key'],
|
|
'fingerprint' => $license['fingerprint'],
|
|
'token' => $license['activation_token']
|
|
]);
|
|
|
|
if (!$res['success']) {
|
|
db()->exec("UPDATE system_license SET status = 'suspended'");
|
|
return false;
|
|
}
|
|
|
|
// Update last verified timestamp
|
|
db()->exec("UPDATE system_license SET activated_at = NOW()");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Attempts to activate the product online.
|
|
*/
|
|
public static function activate($license_key) {
|
|
$license_key = trim($license_key);
|
|
$fingerprint = self::getFingerprint();
|
|
|
|
// Call remote API for real validation
|
|
$response = self::callRemoteApi('/activate', [
|
|
'license_key' => $license_key,
|
|
'fingerprint' => $fingerprint,
|
|
'domain' => $_SERVER['HTTP_HOST'] ?? 'unknown',
|
|
'product' => 'Flatlogic Admin Panel'
|
|
]);
|
|
|
|
if (!$response['success']) {
|
|
return ['success' => false, 'error' => $response['error'] ?? 'Remote activation failed.'];
|
|
}
|
|
|
|
require_once __DIR__ . '/../db/config.php';
|
|
|
|
// Clear previous pending/failed attempts
|
|
db()->exec("DELETE FROM system_license");
|
|
|
|
$stmt = db()->prepare("INSERT INTO system_license (license_key, fingerprint, status, activated_at, activation_token) VALUES (?, ?, 'active', NOW(), ?)");
|
|
$token = $response['activation_token'] ?? bin2hex(random_bytes(32));
|
|
$stmt->execute([$license_key, $fingerprint, $token]);
|
|
|
|
return ['success' => true];
|
|
}
|
|
|
|
/**
|
|
* Fetches all licenses from the remote server.
|
|
*/
|
|
public static function listLicenses() {
|
|
return self::callRemoteApi('/list', []);
|
|
}
|
|
|
|
/**
|
|
* Updates an existing license.
|
|
*/
|
|
public static function updateLicense($id, $data) {
|
|
$params = array_merge(['id' => $id, 'secret' => '1485-5215-2578'], $data);
|
|
return self::callRemoteApi('/update', $params);
|
|
}
|
|
|
|
/**
|
|
* Issues a new license.
|
|
*/
|
|
public static function issueLicense($max_activations, $prefix = 'FLAT', $owner = null, $address = null) {
|
|
return self::callRemoteApi('/issue', [
|
|
'secret' => '1485-5215-2578',
|
|
'max_activations' => $max_activations,
|
|
'prefix' => $prefix,
|
|
'owner' => $owner,
|
|
'address' => $address
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Remote API Caller
|
|
*/
|
|
private static function callRemoteApi($endpoint, $params) {
|
|
$action = ltrim($endpoint, '/');
|
|
$url = self::getApiUrl() . '/index.php?action=' . $action;
|
|
|
|
// Check if we are in local development / simulation mode
|
|
if (strpos($url, 'your-domain.com') !== false) {
|
|
return self::simulateApi($endpoint, $params);
|
|
}
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
$resp = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code !== 200) {
|
|
return ['success' => false, 'error' => "Remote server returned error code $http_code."];
|
|
}
|
|
|
|
$data = json_decode($resp, true);
|
|
if (!$data) {
|
|
return ['success' => false, 'error' => "Invalid response from remote server."];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Local Simulation for development purposes
|
|
*/
|
|
private static function simulateApi($endpoint, $params) {
|
|
$clean_key = strtoupper(trim($params['license_key'] ?? ''));
|
|
|
|
// Strict format check: FLAT-XXXX-XXXX-XXXX (where X is hex)
|
|
$pattern = '/^FLAT-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}$/';
|
|
|
|
if (!preg_match($pattern, $clean_key)) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Invalid License Format. Expected FLAT-XXXX-XXXX-XXXX (Simulation Mode).'
|
|
];
|
|
}
|
|
|
|
// In a real server, you would check if this key is already used by a DIFFERENT fingerprint.
|
|
// To simulate a "Max Activations Reached" error, you can use a specific key:
|
|
if ($clean_key === 'FLAT-0000-0000-0000') {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'Activation Failed: This license key is already active on another machine.'
|
|
];
|
|
}
|
|
|
|
if ($endpoint === '/verify') return ['success' => true];
|
|
|
|
return [
|
|
'success' => true,
|
|
'activation_token' => hash('sha256', $params['license_key'] . $params['fingerprint'] . 'DEBUG_SALT')
|
|
];
|
|
}
|
|
}
|