diff --git a/.DS_Store b/.DS_Store index 15607a8..ae3def8 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/api/v2/get/download_logs.php b/api/v2/get/download_logs.php new file mode 100644 index 0000000..c9be3d4 --- /dev/null +++ b/api/v2/get/download_logs.php @@ -0,0 +1,100 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'user_id') { + $clause .= ' AND dl.user_id = :'.$v[0]; + } + elseif ($v[0] == 'version_id') { + $clause .= ' AND dl.version_id = :'.$v[0]; + } + elseif ($v[0] == 'date_from') { + $clause .= ' AND dl.downloaded_at >= :'.$v[0]; + } + elseif ($v[0] == 'date_to') { + $clause .= ' AND dl.downloaded_at <= :'.$v[0]; + } + elseif ($v[0] == 'search') { + $clause .= ' AND (sv.name LIKE :'.$v[0].' OR u.username LIKE :'.$v[0].' OR dl.ip_address LIKE :'.$v[0].')'; + } + else { + $clause .= ' AND dl.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +//Define Query +if(isset($criterias['totals']) && $criterias['totals']==''){ +//Request for total rows + $sql = 'SELECT count(*) as count FROM download_logs dl LEFT JOIN software_versions sv ON dl.version_id = sv.id LEFT JOIN users u ON dl.user_id = u.id '.$whereclause.''; +} +elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT dl.*, sv.version, sv.name as software_name, u.username FROM download_logs dl LEFT JOIN software_versions sv ON dl.version_id = sv.id LEFT JOIN users u ON dl.user_id = u.id '.$whereclause.' ORDER BY dl.downloaded_at DESC'; +} +else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT dl.*, sv.version, sv.name as software_name, u.username FROM download_logs dl LEFT JOIN software_versions sv ON dl.version_id = sv.id LEFT JOIN users u ON dl.user_id = u.id '.$whereclause.' ORDER BY dl.downloaded_at DESC LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//Execute Query for totals/list +if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; +} +elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/get/download_tokens.php b/api/v2/get/download_tokens.php new file mode 100644 index 0000000..36da1a2 --- /dev/null +++ b/api/v2/get/download_tokens.php @@ -0,0 +1,97 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'user_id') { + $clause .= ' AND dt.user_id = :'.$v[0]; + } + elseif ($v[0] == 'version_id') { + $clause .= ' AND dt.version_id = :'.$v[0]; + } + elseif ($v[0] == 'used') { + $clause .= ' AND dt.used = :'.$v[0]; + } + elseif ($v[0] == 'token') { + $clause .= ' AND dt.token = :'.$v[0]; + } + else { + $clause .= ' AND dt.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +//Define Query +if(isset($criterias['totals']) && $criterias['totals']==''){ +//Request for total rows + $sql = 'SELECT count(*) as count FROM download_tokens dt LEFT JOIN software_versions sv ON dt.version_id = sv.id LEFT JOIN users u ON dt.user_id = u.id '.$whereclause.''; +} +elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT dt.*, sv.version, sv.name as software_name, u.username FROM download_tokens dt LEFT JOIN software_versions sv ON dt.version_id = sv.id LEFT JOIN users u ON dt.user_id = u.id '.$whereclause.' ORDER BY dt.created_at DESC'; +} +else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT dt.*, sv.version, sv.name as software_name, u.username FROM download_tokens dt LEFT JOIN software_versions sv ON dt.version_id = sv.id LEFT JOIN users u ON dt.user_id = u.id '.$whereclause.' ORDER BY dt.created_at DESC LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//Execute Query for totals/list +if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; +} +elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/get/software.php b/api/v2/get/software.php new file mode 100644 index 0000000..af6d1ba --- /dev/null +++ b/api/v2/get/software.php @@ -0,0 +1,170 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'available') { + // Special case: get available upgrades for current user + // This will be handled separately below + } + elseif ($v[0] == 'version_id') { + $clause .= ' AND sv.id = :'.$v[0]; + } + elseif ($v[0] == 'version') { + $clause .= ' AND sv.version = :'.$v[0]; + } + elseif ($v[0] == 'search') { + $clause .= ' AND (sv.name LIKE :'.$v[0].' OR sv.description LIKE :'.$v[0].')'; + } + else { + $clause .= ' AND sv.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +// Special handling for available upgrades +if (isset($criterias['available'])) { + // Include version access logic + require_once './includes/version_access.php'; + + $userId = $user_data['id']; + + // Get all active versions + $stmt = $pdo->prepare(" + SELECT sv.id, sv.version, sv.major_version, sv.minor_version, sv.patch_version, + sv.name, sv.description, sv.file_size, sv.release_date + FROM software_versions sv + WHERE sv.is_active = TRUE + ORDER BY sv.major_version DESC, sv.minor_version DESC, sv.patch_version DESC + "); + $stmt->execute(); + $versions = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Get user's current versions + $ownedVersions = getUserOwnedVersions($userId); + $latestOwned = getLatestOwnedVersion($userId); + + $response = [ + 'current_version' => $latestOwned ? $latestOwned['version'] : null, + 'owned_versions' => array_map(function($v) { + return [ + 'version' => $v['version'], + 'name' => $v['name'], + 'purchased_at' => $v['purchased_at'] + ]; + }, $ownedVersions), + 'available_versions' => [] + ]; + + // Check access for each version + foreach ($versions as $version) { + $accessInfo = checkVersionAccess($userId, $version['id']); + + $versionData = [ + 'id' => $version['id'], + 'version' => $version['version'], + 'name' => $version['name'], + 'description' => $version['description'], + 'file_size' => $version['file_size'], + 'release_date' => $version['release_date'], + 'is_accessible' => $accessInfo['accessible'], + 'requires_payment' => $accessInfo['requires_payment'] ?? false, + 'price' => $accessInfo['price'] ?? 0.00, + 'access_reason' => $accessInfo['reason'] + ]; + + // Add additional info based on access type + if (isset($accessInfo['original_price'])) { + $versionData['original_price'] = $accessInfo['original_price']; + } + if (isset($accessInfo['is_upgrade'])) { + $versionData['is_upgrade'] = $accessInfo['is_upgrade']; + } + if (isset($accessInfo['from_version'])) { + $versionData['upgrade_from'] = $accessInfo['from_version']; + } + if (isset($accessInfo['required_version'])) { + $versionData['required_version'] = $accessInfo['required_version']; + } + + $response['available_versions'][] = $versionData; + } + + $messages = $response; +} +else { + // Regular software versions query + if(isset($criterias['totals']) && $criterias['totals']==''){ + //Request for total rows + $sql = 'SELECT count(*) as count FROM software_versions sv '.$whereclause.''; + } + elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT sv.* FROM software_versions sv '.$whereclause.' ORDER BY sv.major_version DESC, sv.minor_version DESC, sv.patch_version DESC'; + } + else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT sv.* FROM software_versions sv '.$whereclause.' ORDER BY sv.major_version DESC, sv.minor_version DESC, sv.patch_version DESC LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + //Execute Query for totals/list + if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; + } + elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); + } +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/get/software_download.php b/api/v2/get/software_download.php new file mode 100644 index 0000000..7a9229f --- /dev/null +++ b/api/v2/get/software_download.php @@ -0,0 +1,95 @@ +prepare("SELECT * FROM software_versions WHERE id = ?"); +$stmt->execute([$tokenData['version_id']]); +$version = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$version) { + http_response_code(404); + exit('File not found'); +} + +// Invalidate token after use (one-time use) +invalidateToken($pdo, $token); + +// Stream the file +$filePath = $version['file_path']; // e.g., '/var/www/secure_files/update_v2.0.zip' + +if (!file_exists($filePath)) { + http_response_code(404); + exit('File not found on server'); +} + +// Set headers for file download +header('Content-Type: application/octet-stream'); +header('Content-Disposition: attachment; filename="' . basename($version['filename']) . '"'); +header('Content-Length: ' . filesize($filePath)); +header('Cache-Control: no-cache, must-revalidate'); +header('Pragma: no-cache'); +header('Expires: 0'); + +// Stream file in chunks to handle large files +$handle = fopen($filePath, 'rb'); +while (!feof($handle)) { + echo fread($handle, 8192); + flush(); +} +fclose($handle); +exit; + +// Helper functions for token management +function validateDownloadToken($pdo, $token) { + $stmt = $pdo->prepare( + "SELECT user_id, version_id, expires_at, used + FROM download_tokens + WHERE token = ?" + ); + $stmt->execute([$token]); + $tokenData = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$tokenData) { + return false; + } + + // Check if expired + if (strtotime($tokenData['expires_at']) < time()) { + return false; + } + + // Check if already used + if ($tokenData['used']) { + return false; + } + + return $tokenData; +} + +function invalidateToken($pdo, $token) { + $stmt = $pdo->prepare("UPDATE download_tokens SET used = 1 WHERE token = ?"); + $stmt->execute([$token]); +} + +?> \ No newline at end of file diff --git a/api/v2/get/upgrade_paths.php b/api/v2/get/upgrade_paths.php new file mode 100644 index 0000000..c730b79 --- /dev/null +++ b/api/v2/get/upgrade_paths.php @@ -0,0 +1,97 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'from_version_id') { + $clause .= ' AND up.from_version_id = :'.$v[0]; + } + elseif ($v[0] == 'to_version_id') { + $clause .= ' AND up.to_version_id = :'.$v[0]; + } + elseif ($v[0] == 'is_free') { + $clause .= ' AND up.is_free = :'.$v[0]; + } + elseif ($v[0] == 'search') { + $clause .= ' AND (sv1.name LIKE :'.$v[0].' OR sv2.name LIKE :'.$v[0].' OR up.description LIKE :'.$v[0].')'; + } + else { + $clause .= ' AND up.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +//Define Query +if(isset($criterias['totals']) && $criterias['totals']==''){ +//Request for total rows + $sql = 'SELECT count(*) as count FROM upgrade_paths up LEFT JOIN software_versions sv1 ON up.from_version_id = sv1.id LEFT JOIN software_versions sv2 ON up.to_version_id = sv2.id '.$whereclause.''; +} +elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT up.*, sv1.version as from_version, sv1.name as from_name, sv2.version as to_version, sv2.name as to_name FROM upgrade_paths up LEFT JOIN software_versions sv1 ON up.from_version_id = sv1.id LEFT JOIN software_versions sv2 ON up.to_version_id = sv2.id '.$whereclause.' ORDER BY up.id'; +} +else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT up.*, sv1.version as from_version, sv1.name as from_name, sv2.version as to_version, sv2.name as to_name FROM upgrade_paths up LEFT JOIN software_versions sv1 ON up.from_version_id = sv1.id LEFT JOIN software_versions sv2 ON up.to_version_id = sv2.id '.$whereclause.' ORDER BY up.id LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//Execute Query for totals/list +if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; +} +elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/get/user_licenses.php b/api/v2/get/user_licenses.php new file mode 100644 index 0000000..252b99b --- /dev/null +++ b/api/v2/get/user_licenses.php @@ -0,0 +1,97 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'user_id') { + $clause .= ' AND ul.user_id = :'.$v[0]; + } + elseif ($v[0] == 'version_id') { + $clause .= ' AND ul.version_id = :'.$v[0]; + } + elseif ($v[0] == 'status') { + $clause .= ' AND ul.status = :'.$v[0]; + } + elseif ($v[0] == 'search') { + $clause .= ' AND (sv.name LIKE :'.$v[0].' OR ul.license_key LIKE :'.$v[0].')'; + } + else { + $clause .= ' AND ul.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +//Define Query +if(isset($criterias['totals']) && $criterias['totals']==''){ +//Request for total rows + $sql = 'SELECT count(*) as count FROM user_licenses ul LEFT JOIN software_versions sv ON ul.version_id = sv.id '.$whereclause.''; +} +elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT ul.*, sv.version, sv.name as software_name FROM user_licenses ul LEFT JOIN software_versions sv ON ul.version_id = sv.id '.$whereclause.' ORDER BY ul.purchased_at DESC'; +} +else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT ul.*, sv.version, sv.name as software_name FROM user_licenses ul LEFT JOIN software_versions sv ON ul.version_id = sv.id '.$whereclause.' ORDER BY ul.purchased_at DESC LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//Execute Query for totals/list +if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; +} +elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/get/version_access_rules.php b/api/v2/get/version_access_rules.php new file mode 100644 index 0000000..b824cfa --- /dev/null +++ b/api/v2/get/version_access_rules.php @@ -0,0 +1,94 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +$whereclause = ''; + +//NEW ARRAY +$criterias = []; +$clause = ''; + +//Check for $_GET variables and build up clause +if(isset($get_content) && $get_content!=''){ + //GET VARIABLES FROM URL + $requests = explode("&", $get_content); + //Check for keys and values + foreach ($requests as $y){ + $v = explode("=", $y); + //INCLUDE VARIABLES IN ARRAY + $criterias[$v[0]] = $v[1]; + + if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='history'|| $v[0] =='success_msg'){ + //do nothing + } + elseif ($v[0] == 'version_id') { + $clause .= ' AND var.version_id = :'.$v[0]; + } + elseif ($v[0] == 'access_type') { + $clause .= ' AND var.access_type = :'.$v[0]; + } + elseif ($v[0] == 'search') { + $clause .= ' AND (sv.name LIKE :'.$v[0].' OR var.description LIKE :'.$v[0].')'; + } + else { + $clause .= ' AND var.'.$v[0].' = :'.$v[0]; + } + } + if ($whereclause == '' && $clause !=''){ + $whereclause = 'WHERE '.substr($clause, 4); + } else { + $whereclause .= $clause; + } +} + +//Define Query +if(isset($criterias['totals']) && $criterias['totals']==''){ +//Request for total rows + $sql = 'SELECT count(*) as count FROM version_access_rules var LEFT JOIN software_versions sv ON var.version_id = sv.id '.$whereclause.''; +} +elseif (isset($criterias['list']) && $criterias['list']=='') { + //SQL for Paging + $sql = 'SELECT var.*, sv.version, sv.name as software_name FROM version_access_rules var LEFT JOIN software_versions sv ON var.version_id = sv.id '.$whereclause.' ORDER BY var.id'; +} +else { + $current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1; + $sql = 'SELECT var.*, sv.version, sv.name as software_name FROM version_access_rules var LEFT JOIN software_versions sv ON var.version_id = sv.id '.$whereclause.' ORDER BY var.id LIMIT ?, ?'; + $stmt = $pdo->prepare($sql); + $stmt->bindValue(1, ($current_page - 1) * $page_rows_products, PDO::PARAM_INT); + $stmt->bindValue(2, $page_rows_products, PDO::PARAM_INT); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//Execute Query for totals/list +if(isset($criterias['totals']) && $criterias['totals']==''){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetch(); + $messages = $messages[0]; +} +elseif(isset($criterias['list'])){ + $stmt = $pdo->prepare($sql); + $stmt->execute(); + $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +//------------------------------------------ +//JSON_ENCODE +//------------------------------------------ +$messages = json_encode($messages, JSON_UNESCAPED_UNICODE); + +//Send results +echo $messages; + +?> \ No newline at end of file diff --git a/api/v2/post/software.php b/api/v2/post/software.php new file mode 100644 index 0000000..9773300 --- /dev/null +++ b/api/v2/post/software.php @@ -0,0 +1,202 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +list($whereclause,$condition) = getWhereclause('',$permission,$partner,''); + +// Handle different actions +$action = $post_content['action'] ?? ''; + +switch ($action) { + case 'download': + // Handle secure download request + require_once './includes/version_access.php'; + + $versionId = $post_content['version_id'] ?? null; + + if (!$versionId) { + http_response_code(400); + echo json_encode(['error' => 'Missing version_id']); + exit; + } + + $userId = $user_data['id']; + + // Validate user has access to this version + if (!validateUserAccess($userId, $versionId)) { + http_response_code(403); + echo json_encode(['error' => 'Access denied. Payment required or insufficient permissions.']); + exit; + } + + // Get version details + $stmt = $pdo->prepare("SELECT * FROM software_versions WHERE id = ?"); + $stmt->execute([$versionId]); + $version = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$version) { + http_response_code(404); + echo json_encode(['error' => 'Version not found']); + exit; + } + + // Log the download + logDownload($pdo, $userId, $versionId); + + // Generate temporary signed URL + $downloadToken = generateSecureDownloadToken($pdo, $userId, $versionId); + + echo json_encode([ + 'download_url' => '/api/v2/get/software_download.php?token=' . $downloadToken, + 'expires_in' => 300 // 5 minutes + ]); + break; + + case 'purchase': + // Handle purchase/license grant + require_once './includes/version_access.php'; + + $versionId = $post_content['version_id'] ?? null; + $transactionId = $post_content['transaction_id'] ?? null; + + if (!$versionId) { + http_response_code(400); + echo json_encode(['error' => 'Missing version_id']); + exit; + } + + $userId = $user_data['id']; + + // Verify payment was successful (integrate with your payment processor) + $paymentVerified = true; // For testing - integrate with actual payment verification + + if (!$paymentVerified) { + http_response_code(400); + echo json_encode(['error' => 'Payment verification failed']); + exit; + } + + // Check access requirements + $accessInfo = checkVersionAccess($userId, $versionId); + + if ($accessInfo['accessible']) { + // Already has access + echo json_encode([ + 'success' => true, + 'message' => 'You already have access to this version', + 'license_granted' => false + ]); + exit; + } + + if (!$accessInfo['requires_payment']) { + // Shouldn't need payment + http_response_code(400); + echo json_encode(['error' => 'This version does not require payment']); + exit; + } + + // Grant license + $success = grantLicense($pdo, $userId, $versionId, $transactionId); + + if ($success) { + echo json_encode([ + 'success' => true, + 'message' => 'License granted successfully', + 'license_granted' => true + ]); + } else { + http_response_code(500); + echo json_encode(['error' => 'Failed to grant license']); + } + break; + + default: + // Handle CRUD operations for software versions (admin only) + if (!isAllowed('software', $profile, $permission, 'C') && + !isAllowed('software', $profile, $permission, 'U') && + !isAllowed('software', $profile, $permission, 'D')) { + http_response_code(403); + echo json_encode(['error' => 'Insufficient permissions']); + exit; + } + + //SET PARAMETERS FOR QUERY + $id = $post_content['id'] ?? ''; //check for id + $command = ($id == '')? 'insert' : 'update'; //IF id = empty then INSERT + if (isset($post_content['delete'])){$command = 'delete';} //change command to delete + $date = date('Y-m-d H:i:s'); + + //CREATE EMPTY STRINGS + $clause = ''; + $clause_insert =''; + $input_insert = ''; + + //ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE + if ($command == 'update'){ + $post_content['updated'] = $date; + $post_content['updatedby'] = $username; + } + elseif ($command == 'insert'){ + $post_content['created'] = $date; + $post_content['createdby'] = $username; + } + + //BUILD UP CLAUSE + $execute_input = []; + foreach ($post_content as $key => $value) { + if ($key == 'action' || $key == 'id' || $key == 'delete') continue; + + if ($command == 'insert') { + $clause_insert .= $key.','; + $input_insert .= '?,'; + $execute_input[] = $value; + } elseif ($command == 'update') { + $clause .= $key.'=?,'; + $execute_input[] = $value; + } + } + + //CLEAN UP INPUT + $clause = substr($clause, 0, -1); //Clean clause - remove last comma + $clause_insert = substr($clause_insert, 0, -1); //Clean clause - remove last comma + $input_insert = substr($input_insert, 0, -1); //Clean clause - remove last comma + + //QUERY AND VERIFY ALLOWED + if ($command == 'update' && isAllowed('software',$profile,$permission,'U') === 1){ + $sql = 'UPDATE software_versions SET '.$clause.' WHERE id = ?'; + $execute_input[] = $id; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); + } + elseif ($command == 'insert' && isAllowed('software',$profile,$permission,'C') === 1){ + $sql = 'INSERT INTO software_versions ('.$clause_insert.') VALUES ('.$input_insert.')'; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); + } + elseif ($command == 'delete' && isAllowed('software',$profile,$permission,'D') === 1){ + $stmt = $pdo->prepare('DELETE FROM software_versions WHERE id = ?'); + $stmt->execute([$id]); + + //Add deletion to changelog + changelog($dbname,'software_versions',$id,'Delete','Delete',$username); + } else { + http_response_code(403); + echo json_encode(['error' => 'Operation not allowed']); + } + break; +} + +?> \ No newline at end of file diff --git a/api/v2/post/upgrade_paths.php b/api/v2/post/upgrade_paths.php new file mode 100644 index 0000000..32e355c --- /dev/null +++ b/api/v2/post/upgrade_paths.php @@ -0,0 +1,84 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +list($whereclause,$condition) = getWhereclause('',$permission,$partner,''); + +//SET PARAMETERS FOR QUERY +$id = $post_content['id'] ?? ''; //check for id +$command = ($id == '')? 'insert' : 'update'; //IF id = empty then INSERT +if (isset($post_content['delete'])){$command = 'delete';} //change command to delete +$date = date('Y-m-d H:i:s'); + +//CREATE EMPTY STRINGS +$clause = ''; +$clause_insert =''; +$input_insert = ''; + +//ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE +if ($command == 'update'){ + $post_content['updated'] = $date; + $post_content['updatedby'] = $username; +} +elseif ($command == 'insert'){ + $post_content['created'] = $date; + $post_content['createdby'] = $username; +} + +//BUILD UP CLAUSE +$execute_input = []; +foreach ($post_content as $key => $value) { + if ($key == 'id' || $key == 'delete') continue; + + if ($command == 'insert') { + $clause_insert .= $key.','; + $input_insert .= '?,'; + $execute_input[] = $value; + } elseif ($command == 'update') { + $clause .= $key.'=?,'; + $execute_input[] = $value; + } +} + +//CLEAN UP INPUT +$clause = substr($clause, 0, -1); //Clean clause - remove last comma +$clause_insert = substr($clause_insert, 0, -1); //Clean clause - remove last comma +$input_insert = substr($input_insert, 0, -1); //Clean clause - remove last comma + +//QUERY AND VERIFY ALLOWED +if ($command == 'update' && isAllowed('upgrade_paths',$profile,$permission,'U') === 1){ + $sql = 'UPDATE upgrade_paths SET '.$clause.' WHERE id = ?'; + $execute_input[] = $id; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'insert' && isAllowed('upgrade_paths',$profile,$permission,'C') === 1){ + $sql = 'INSERT INTO upgrade_paths ('.$clause_insert.') VALUES ('.$input_insert.')'; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'delete' && isAllowed('upgrade_paths',$profile,$permission,'D') === 1){ + $stmt = $pdo->prepare('DELETE FROM upgrade_paths WHERE id = ?'); + $stmt->execute([$id]); + + //Add deletion to changelog + changelog($dbname,'upgrade_paths',$id,'Delete','Delete',$username); +} else { + http_response_code(403); + echo json_encode(['error' => 'Operation not allowed']); +} + +?> \ No newline at end of file diff --git a/api/v2/post/user_licenses.php b/api/v2/post/user_licenses.php new file mode 100644 index 0000000..dbc75c1 --- /dev/null +++ b/api/v2/post/user_licenses.php @@ -0,0 +1,84 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +list($whereclause,$condition) = getWhereclause('',$permission,$partner,''); + +//SET PARAMETERS FOR QUERY +$id = $post_content['id'] ?? ''; //check for id +$command = ($id == '')? 'insert' : 'update'; //IF id = empty then INSERT +if (isset($post_content['delete'])){$command = 'delete';} //change command to delete +$date = date('Y-m-d H:i:s'); + +//CREATE EMPTY STRINGS +$clause = ''; +$clause_insert =''; +$input_insert = ''; + +//ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE +if ($command == 'update'){ + $post_content['updated'] = $date; + $post_content['updatedby'] = $username; +} +elseif ($command == 'insert'){ + $post_content['created'] = $date; + $post_content['createdby'] = $username; +} + +//BUILD UP CLAUSE +$execute_input = []; +foreach ($post_content as $key => $value) { + if ($key == 'id' || $key == 'delete') continue; + + if ($command == 'insert') { + $clause_insert .= $key.','; + $input_insert .= '?,'; + $execute_input[] = $value; + } elseif ($command == 'update') { + $clause .= $key.'=?,'; + $execute_input[] = $value; + } +} + +//CLEAN UP INPUT +$clause = substr($clause, 0, -1); //Clean clause - remove last comma +$clause_insert = substr($clause_insert, 0, -1); //Clean clause - remove last comma +$input_insert = substr($input_insert, 0, -1); //Clean clause - remove last comma + +//QUERY AND VERIFY ALLOWED +if ($command == 'update' && isAllowed('user_licenses',$profile,$permission,'U') === 1){ + $sql = 'UPDATE user_licenses SET '.$clause.' WHERE id = ?'; + $execute_input[] = $id; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'insert' && isAllowed('user_licenses',$profile,$permission,'C') === 1){ + $sql = 'INSERT INTO user_licenses ('.$clause_insert.') VALUES ('.$input_insert.')'; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'delete' && isAllowed('user_licenses',$profile,$permission,'D') === 1){ + $stmt = $pdo->prepare('DELETE FROM user_licenses WHERE id = ?'); + $stmt->execute([$id]); + + //Add deletion to changelog + changelog($dbname,'user_licenses',$id,'Delete','Delete',$username); +} else { + http_response_code(403); + echo json_encode(['error' => 'Operation not allowed']); +} + +?> \ No newline at end of file diff --git a/api/v2/post/version_access_rules.php b/api/v2/post/version_access_rules.php new file mode 100644 index 0000000..437b322 --- /dev/null +++ b/api/v2/post/version_access_rules.php @@ -0,0 +1,84 @@ +soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';} + +//default whereclause +list($whereclause,$condition) = getWhereclause('',$permission,$partner,''); + +//SET PARAMETERS FOR QUERY +$id = $post_content['id'] ?? ''; //check for id +$command = ($id == '')? 'insert' : 'update'; //IF id = empty then INSERT +if (isset($post_content['delete'])){$command = 'delete';} //change command to delete +$date = date('Y-m-d H:i:s'); + +//CREATE EMPTY STRINGS +$clause = ''; +$clause_insert =''; +$input_insert = ''; + +//ADD STANDARD PARAMETERS TO ARRAY BASED ON INSERT OR UPDATE +if ($command == 'update'){ + $post_content['updated'] = $date; + $post_content['updatedby'] = $username; +} +elseif ($command == 'insert'){ + $post_content['created'] = $date; + $post_content['createdby'] = $username; +} + +//BUILD UP CLAUSE +$execute_input = []; +foreach ($post_content as $key => $value) { + if ($key == 'id' || $key == 'delete') continue; + + if ($command == 'insert') { + $clause_insert .= $key.','; + $input_insert .= '?,'; + $execute_input[] = $value; + } elseif ($command == 'update') { + $clause .= $key.'=?,'; + $execute_input[] = $value; + } +} + +//CLEAN UP INPUT +$clause = substr($clause, 0, -1); //Clean clause - remove last comma +$clause_insert = substr($clause_insert, 0, -1); //Clean clause - remove last comma +$input_insert = substr($input_insert, 0, -1); //Clean clause - remove last comma + +//QUERY AND VERIFY ALLOWED +if ($command == 'update' && isAllowed('version_access_rules',$profile,$permission,'U') === 1){ + $sql = 'UPDATE version_access_rules SET '.$clause.' WHERE id = ?'; + $execute_input[] = $id; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'insert' && isAllowed('version_access_rules',$profile,$permission,'C') === 1){ + $sql = 'INSERT INTO version_access_rules ('.$clause_insert.') VALUES ('.$input_insert.')'; + $stmt = $pdo->prepare($sql); + $stmt->execute($execute_input); +} +elseif ($command == 'delete' && isAllowed('version_access_rules',$profile,$permission,'D') === 1){ + $stmt = $pdo->prepare('DELETE FROM version_access_rules WHERE id = ?'); + $stmt->execute([$id]); + + //Add deletion to changelog + changelog($dbname,'version_access_rules',$id,'Delete','Delete',$username); +} else { + http_response_code(403); + echo json_encode(['error' => 'Operation not allowed']); +} + +?> \ No newline at end of file diff --git a/assets/functions.js b/assets/functions.js new file mode 100644 index 0000000..3a8de4b --- /dev/null +++ b/assets/functions.js @@ -0,0 +1,350 @@ +// Software Upgrade System - Frontend Functions +// Requires: jQuery or modern fetch API + +class UpgradeManager { + constructor(apiBase = '/api.php') { + this.apiBase = apiBase; + this.serviceToken = ''; + this.init(); + } + + init() { + // Get service token from DOM if available + const tokenElement = document.getElementById('servicetoken'); + if (tokenElement) { + this.serviceToken = tokenElement.innerHTML || ''; + } + } + + async makeAPICall(endpoint, method = 'GET', data = null) { + const url = this.apiBase + endpoint; + const bearer = 'Bearer ' + this.serviceToken; + + const options = { + method: method, + headers: { + 'Authorization': bearer, + 'Content-Type': 'application/json' + }, + credentials: 'include' + }; + + if (data && (method === 'POST' || method === 'PUT')) { + options.body = JSON.stringify(data); + } + + const response = await fetch(url, options); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ error: 'Network error' })); + throw new Error(errorData.error || `HTTP ${response.status}`); + } + + return await response.json(); + } + + async getAvailableVersions() { + try { + const data = await this.makeAPICall('/v2/get/software?available'); + return data; + } catch (error) { + console.error('Error fetching available versions:', error); + throw error; + } + } + + async downloadVersion(versionId, onProgress = null) { + try { + // Step 1: Request download token + const downloadRequest = await this.makeAPICall('/v2/post/software', 'POST', { + action: 'download', + version_id: parseInt(versionId) + }); + + if (!downloadRequest.download_url) { + throw new Error('No download URL received'); + } + + // Step 2: Download file using temporary URL + await this.downloadFile(downloadRequest.download_url, onProgress); + + } catch (error) { + console.error('Download error:', error); + throw error; + } + } + + async downloadFile(url, onProgress) { + const response = await fetch(url, { + credentials: 'include' + }); + + if (!response.ok) { + throw new Error('Download failed'); + } + + const contentLength = response.headers.get('Content-Length'); + const total = parseInt(contentLength, 10); + let loaded = 0; + + const reader = response.body.getReader(); + const chunks = []; + + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + chunks.push(value); + loaded += value.length; + + if (onProgress && total) { + onProgress(loaded, total); + } + } + + // Create blob from chunks + const blob = new Blob(chunks); + + // Trigger download + const downloadUrl = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = 'software_upgrade.zip'; // Filename will be set by server + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(downloadUrl); + document.body.removeChild(a); + } + + async purchaseVersion(versionId, transactionId = null) { + try { + const purchaseData = { + action: 'purchase', + version_id: parseInt(versionId) + }; + + if (transactionId) { + purchaseData.transaction_id = transactionId; + } + + const result = await this.makeAPICall('/v2/post/software', 'POST', purchaseData); + return result; + } catch (error) { + console.error('Purchase error:', error); + throw error; + } + } + + formatBytes(bytes) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; + } + + formatPrice(price, currency = 'USD') { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency + }).format(price); + } +} + +// Global upgrade manager instance +let upgradeManager; + +// Initialize upgrade system +function initUpgradeSystem() { + upgradeManager = new UpgradeManager(); +} + +// Display upgrade options in UI +async function showUpgradeOptions(containerId = 'upgrade-container') { + const container = document.getElementById(containerId); + if (!container) { + console.error('Container element not found:', containerId); + return; + } + + try { + const data = await upgradeManager.getAvailableVersions(); + + container.innerHTML = ''; + + // Show current version info + if (data.current_version) { + const currentDiv = document.createElement('div'); + currentDiv.className = 'current-version-info'; + currentDiv.innerHTML = ` +

Your Current Version: ${data.current_version}

+

Owned versions: ${data.owned_versions.map(v => v.version).join(', ')}

+ `; + container.appendChild(currentDiv); + } + + // Show available versions + if (data.available_versions && data.available_versions.length > 0) { + const versionsDiv = document.createElement('div'); + versionsDiv.className = 'available-versions'; + + data.available_versions.forEach(version => { + const versionCard = document.createElement('div'); + versionCard.className = 'version-card'; + versionCard.dataset.versionId = version.id; + + let buttonHTML = ''; + let priceHTML = ''; + let statusHTML = ''; + + if (version.is_accessible) { + statusHTML = 'Owned'; + buttonHTML = ``; + } else if (version.requires_payment) { + if (version.is_upgrade) { + priceHTML = ` +
+ ${upgradeManager.formatPrice(version.price)} + ${upgradeManager.formatPrice(version.original_price)} + Upgrade from v${version.upgrade_from} +
+ `; + } else { + priceHTML = `
${upgradeManager.formatPrice(version.price)}
`; + } + buttonHTML = ``; + } else if (version.access_reason === 'requires_base_version') { + statusHTML = `Requires v${version.required_version}`; + buttonHTML = ``; + } + + versionCard.innerHTML = ` +
+

${version.name} ${statusHTML}

+ v${version.version} +
+
${version.description}
+
+ Size: ${upgradeManager.formatBytes(version.file_size)} + Released: ${new Date(version.release_date).toLocaleDateString()} +
+ ${priceHTML} +
+ ${buttonHTML} +
+ `; + + versionsDiv.appendChild(versionCard); + }); + + container.appendChild(versionsDiv); + } else { + container.innerHTML = '

No software versions available at this time.

'; + } + + } catch (error) { + container.innerHTML = `
Error loading upgrades: ${error.message}
`; + console.error('Error showing upgrade options:', error); + } +} + +// Download version with progress +async function downloadVersion(versionId) { + const button = event.target; + const originalText = button.innerHTML; + + try { + button.disabled = true; + button.innerHTML = 'Preparing Download...'; + + // Create progress indicator + const progressContainer = document.createElement('div'); + progressContainer.className = 'download-progress'; + progressContainer.innerHTML = ` +
+
+
+
0%
+ `; + + button.parentNode.appendChild(progressContainer); + + const progressFill = progressContainer.querySelector('.progress-fill'); + const progressText = progressContainer.querySelector('.progress-text'); + + await upgradeManager.downloadVersion(versionId, (loaded, total) => { + const percent = Math.round((loaded / total) * 100); + progressFill.style.width = percent + '%'; + progressText.textContent = percent + '%'; + }); + + button.innerHTML = 'Download Complete!'; + progressText.textContent = 'Complete'; + + // Remove progress after a delay + setTimeout(() => { + progressContainer.remove(); + button.innerHTML = originalText; + button.disabled = false; + }, 3000); + + } catch (error) { + button.innerHTML = 'Download Failed'; + button.disabled = false; + alert('Download failed: ' + error.message); + + // Remove progress on error + const progressContainer = button.parentNode.querySelector('.download-progress'); + if (progressContainer) { + progressContainer.remove(); + } + } +} + +// Purchase version +async function purchaseVersion(versionId, price) { + const button = event.target; + const originalText = button.innerHTML; + + const confirmed = confirm(`Purchase this software version for ${upgradeManager.formatPrice(price)}?`); + if (!confirmed) return; + + try { + button.disabled = true; + button.innerHTML = 'Processing Purchase...'; + + // Here you would integrate with your payment processor + // For now, we'll simulate with a transaction ID + const transactionId = 'txn_' + Date.now(); + + const result = await upgradeManager.purchaseVersion(versionId, transactionId); + + if (result.success) { + button.innerHTML = 'Purchase Successful!'; + button.className = 'success-btn'; + + // Refresh the upgrade options + setTimeout(() => { + showUpgradeOptions(); + }, 2000); + } else { + throw new Error(result.error || 'Purchase failed'); + } + + } catch (error) { + button.innerHTML = 'Purchase Failed'; + button.disabled = false; + alert('Purchase failed: ' + error.message); + } +} + +// Initialize when DOM is ready +document.addEventListener('DOMContentLoaded', function() { + initUpgradeSystem(); +}); + +// Export for module usage (optional) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { UpgradeManager, upgradeManager }; +} \ No newline at end of file diff --git a/assets/readdevice.js b/assets/readdevice.js index 379b221..8512884 100644 --- a/assets/readdevice.js +++ b/assets/readdevice.js @@ -78,7 +78,13 @@ async function connectSerial() { }; await logCommunication(`Selected USB device - ${JSON.stringify(portDetails)}`, 'connected'); - await port.open({ baudRate: 56700 }); + await port.open({ + baudRate: 56700, + dataBits: 8, + stopBits: 1, + parity: 'none', + flowControl: 'none' + }); listenToPort(); diff --git a/assets/scripts.js b/assets/scripts.js index 12d0961..28f9696 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -88,7 +88,13 @@ async function connectDevice() { }; await logCommunication(`Selected USB device - ${JSON.stringify(portDetails)}`, 'connected'); - await port.open({ baudRate: 56700 }); + await port.open({ + baudRate: 56700, + dataBits: 8, + stopBits: 1, + parity: 'none', + flowControl: 'none' + }); progressBar("10", "Connecting", "#04AA6D"); // Log successful connection with details diff --git a/includes/version_access.php b/includes/version_access.php new file mode 100644 index 0000000..93992f9 --- /dev/null +++ b/includes/version_access.php @@ -0,0 +1,282 @@ +prepare(" + SELECT sv.*, ul.license_key, ul.purchased_at + FROM user_licenses ul + JOIN software_versions sv ON ul.version_id = sv.id + WHERE ul.user_id = ? AND ul.status = 'active' + ORDER BY sv.major_version DESC, sv.minor_version DESC + "); + $stmt->execute([$userId]); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +function getLatestOwnedVersion($userId) { + $versions = getUserOwnedVersions($userId); + return !empty($versions) ? $versions[0] : null; +} + +function checkVersionAccess($userId, $versionId) { + global $pdo; + + // Get version and its access rules + $stmt = $pdo->prepare(" + SELECT sv.*, var.access_type, var.requires_base_version, var.price + FROM software_versions sv + JOIN version_access_rules var ON sv.id = var.version_id + WHERE sv.id = ? + "); + $stmt->execute([$versionId]); + $version = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$version) { + return ['accessible' => false, 'reason' => 'Version not found']; + } + + switch ($version['access_type']) { + case 'free_all': + // Free for everyone (like v0.99) + return [ + 'accessible' => true, + 'reason' => 'free_for_all', + 'price' => 0.00, + 'requires_payment' => false + ]; + + case 'free_for_owners': + // Free for owners of required base version (like v1.1 for v1.0 owners) + if ($version['requires_base_version']) { + $hasBaseVersion = userOwnsVersion($userId, $version['requires_base_version']); + + if ($hasBaseVersion) { + return [ + 'accessible' => true, + 'reason' => 'free_upgrade', + 'price' => 0.00, + 'requires_payment' => false + ]; + } else { + return [ + 'accessible' => false, + 'reason' => 'requires_base_version', + 'required_version' => $version['requires_base_version'], + 'price' => $version['price'], + 'requires_payment' => true + ]; + } + } + return ['accessible' => false, 'reason' => 'invalid_access_rule']; + + case 'paid': + case 'paid_upgrade': + // Check if user already owns this version + if (userOwnsVersionById($userId, $versionId)) { + return [ + 'accessible' => true, + 'reason' => 'already_owned', + 'price' => 0.00, + 'requires_payment' => false + ]; + } + + // Check for upgrade pricing + $upgradeInfo = getUpgradePrice($userId, $versionId); + + return [ + 'accessible' => false, + 'reason' => 'requires_purchase', + 'price' => $upgradeInfo['price'], + 'original_price' => $version['price'], + 'is_upgrade' => $upgradeInfo['is_upgrade'], + 'requires_payment' => true + ]; + + default: + return ['accessible' => false, 'reason' => 'unknown_access_type']; + } +} + +function userOwnsVersion($userId, $version) { + global $pdo; + + $stmt = $pdo->prepare(" + SELECT COUNT(*) + FROM user_licenses ul + JOIN software_versions sv ON ul.version_id = sv.id + WHERE ul.user_id = ? AND sv.version = ? AND ul.status = 'active' + "); + $stmt->execute([$userId, $version]); + + return $stmt->fetchColumn() > 0; +} + +function userOwnsVersionById($userId, $versionId) { + global $pdo; + + $stmt = $pdo->prepare(" + SELECT COUNT(*) + FROM user_licenses + WHERE user_id = ? AND version_id = ? AND status = 'active' + "); + $stmt->execute([$userId, $versionId]); + + return $stmt->fetchColumn() > 0; +} + +function getUpgradePrice($userId, $targetVersionId) { + global $pdo; + + // Get user's owned versions + $ownedVersions = getUserOwnedVersions($userId); + + if (empty($ownedVersions)) { + // No owned versions, return full price + $stmt = $pdo->prepare(" + SELECT var.price + FROM version_access_rules var + WHERE var.version_id = ? + "); + $stmt->execute([$targetVersionId]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + return [ + 'price' => $result['price'] ?? 0.00, + 'is_upgrade' => false + ]; + } + + // Check for upgrade paths + $bestUpgradePrice = null; + $fromVersion = null; + + foreach ($ownedVersions as $ownedVersion) { + $stmt = $pdo->prepare(" + SELECT upgrade_price, is_free + FROM upgrade_paths + WHERE from_version_id = ? AND to_version_id = ? + "); + $stmt->execute([$ownedVersion['id'], $targetVersionId]); + $upgrade = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($upgrade) { + if ($upgrade['is_free']) { + return [ + 'price' => 0.00, + 'is_upgrade' => true, + 'from_version' => $ownedVersion['version'] + ]; + } + + if ($bestUpgradePrice === null || $upgrade['upgrade_price'] < $bestUpgradePrice) { + $bestUpgradePrice = $upgrade['upgrade_price']; + $fromVersion = $ownedVersion['version']; + } + } + } + + if ($bestUpgradePrice !== null) { + return [ + 'price' => $bestUpgradePrice, + 'is_upgrade' => true, + 'from_version' => $fromVersion + ]; + } + + // No upgrade path, return full price + $stmt = $pdo->prepare(" + SELECT var.price + FROM version_access_rules var + WHERE var.version_id = ? + "); + $stmt->execute([$targetVersionId]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + return [ + 'price' => $result['price'] ?? 0.00, + 'is_upgrade' => false + ]; +} + +function grantLicense($pdo, $userId, $versionId, $transactionId = null) { + // Generate unique license key + $licenseKey = generateLicenseKey($userId, $versionId); + + $stmt = $pdo->prepare(" + INSERT INTO user_licenses (user_id, version_id, license_key, transaction_id, status) + VALUES (?, ?, ?, ?, 'active') + ON DUPLICATE KEY UPDATE status = 'active', license_key = ? + "); + + return $stmt->execute([$userId, $versionId, $licenseKey, $transactionId, $licenseKey]); +} + +function generateLicenseKey($userId, $versionId) { + // Generate a unique license key + $data = $userId . '-' . $versionId . '-' . time() . '-' . bin2hex(random_bytes(8)); + return strtoupper(substr(hash('sha256', $data), 0, 29)); // Format: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX +} + +function generateSecureDownloadToken($pdo, $userId, $versionId) { + // Generate random token + $token = bin2hex(random_bytes(32)); + + // Store token with expiration (5 minutes) + $expiresAt = date('Y-m-d H:i:s', time() + 300); + + $stmt = $pdo->prepare( + "INSERT INTO download_tokens (token, user_id, version_id, expires_at, used) + VALUES (?, ?, ?, ?, 0)" + ); + $stmt->execute([$token, $userId, $versionId, $expiresAt]); + + return $token; +} + +function validateUserAccess($userId, $versionId) { + global $pdo; + + // Check if version requires payment + $stmt = $pdo->prepare(" + SELECT var.access_type + FROM version_access_rules var + WHERE var.version_id = ? + "); + $stmt->execute([$versionId]); + $accessRule = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$accessRule) { + return false; + } + + if ($accessRule['access_type'] === 'free_all') { + return true; // Free for everyone + } + + // Check if user has valid license + $stmt = $pdo->prepare( + "SELECT COUNT(*) FROM user_licenses + WHERE user_id = ? AND version_id = ? AND status = 'active'" + ); + $stmt->execute([$userId, $versionId]); + + return $stmt->fetchColumn() > 0; +} + +function logDownload($pdo, $userId, $versionId) { + $stmt = $pdo->prepare(" + INSERT INTO download_logs (user_id, version_id, ip_address, user_agent, downloaded_at) + VALUES (?, ?, ?, ?, NOW()) + "); + $stmt->execute([ + $userId, + $versionId, + $_SERVER['REMOTE_ADDR'] ?? '', + $_SERVER['HTTP_USER_AGENT'] ?? '' + ]); +} + +?> \ No newline at end of file diff --git a/settings/settingsmenu.php b/settings/settingsmenu.php index 22011e2..cb3760b 100644 --- a/settings/settingsmenu.php +++ b/settings/settingsmenu.php @@ -281,6 +281,14 @@ $main_menu = [ "icon" => "fas fa-tachometer-alt", "name" => "menu_profiles" ] + ], + "upgrades" => [ + "main_menu" => [ + "url" => "upgrades", + "selected" => "upgrades", + "icon" => "fas fa-download", + "name" => "menu_upgrades" + ] ] ]; diff --git a/settings/settingsviews.php b/settings/settingsviews.php index daeeb5a..72cc897 100644 --- a/settings/settingsviews.php +++ b/settings/settingsviews.php @@ -65,6 +65,7 @@ $all_views = [ "admin", "partners", "partner", + "upgrades", "users", "user", "user_manage", diff --git a/settings/translations/translations_DE.php b/settings/translations/translations_DE.php index f06f480..091bfef 100644 --- a/settings/translations/translations_DE.php +++ b/settings/translations/translations_DE.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Verträge'; $menu_report_usage = 'Systemnutzung'; $menu_maintenance = 'Maintenance'; $menu_profiles = 'Profiles'; +$menu_upgrades = 'Software Upgrades'; $tab1 = 'Allgemein'; $tab2 = 'Partner'; $tab3 = 'Protokoll'; diff --git a/settings/translations/translations_ES.php b/settings/translations/translations_ES.php index 2dca143..3d53eac 100644 --- a/settings/translations/translations_ES.php +++ b/settings/translations/translations_ES.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Contractos'; $menu_report_usage = 'Uso del Sistema'; $menu_maintenance = 'Mantenimiento'; $menu_profiles = 'Perfiles'; +$menu_upgrades = 'Actualizaciones de Software'; $tab1 = 'General'; $tab2 = 'Socios'; $tab3 = 'Registro'; diff --git a/settings/translations/translations_NL.php b/settings/translations/translations_NL.php index 8c9eef8..d91ea9a 100644 --- a/settings/translations/translations_NL.php +++ b/settings/translations/translations_NL.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Contracten'; $menu_report_usage = 'Systeemgebruik'; $menu_maintenance = 'Maintenance'; $menu_profiles = 'Profielen'; +$menu_upgrades = 'Software Upgrades'; $tab1 = 'Algemeen'; $tab2 = 'Hierarchy'; $tab3 = 'Log'; diff --git a/settings/translations/translations_PL.php b/settings/translations/translations_PL.php index a76d7ee..f07f593 100644 --- a/settings/translations/translations_PL.php +++ b/settings/translations/translations_PL.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Umowy'; $menu_report_usage = 'Użycie systemu'; $menu_maintenance = 'Konserwacja'; $menu_profiles = 'Profile'; +$menu_upgrades = 'Aktualizacje Oprogramowania'; $tab1 = 'Ogólne'; $tab2 = 'Partnerzy'; $tab3 = 'Dziennik'; diff --git a/settings/translations/translations_PT.php b/settings/translations/translations_PT.php index 197d2ea..1f7fd5d 100644 --- a/settings/translations/translations_PT.php +++ b/settings/translations/translations_PT.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Contratos'; $menu_report_usage = 'Uso do Sistema'; $menu_maintenance = 'Manutenção'; $menu_profiles = 'Perfis'; +$menu_upgrades = 'Atualizações de Software'; $tab1 = 'Geral'; $tab2 = 'Parceiros'; $tab3 = 'Registro'; diff --git a/settings/translations/translations_US.php b/settings/translations/translations_US.php index ae0c0c8..51242bc 100644 --- a/settings/translations/translations_US.php +++ b/settings/translations/translations_US.php @@ -27,6 +27,7 @@ $menu_report_contracts_billing = 'Contracts'; $menu_report_usage = 'System usage'; $menu_maintenance = 'Maintenance'; $menu_profiles = 'Profiles'; +$menu_upgrades = 'Software Upgrades'; $tab1 = 'General'; $tab2 = 'Partners'; $tab3 = 'Log'; diff --git a/upgrades.php b/upgrades.php new file mode 100644 index 0000000..9736b37 --- /dev/null +++ b/upgrades.php @@ -0,0 +1,285 @@ + +
+
+
+
+

+ Software Upgrades +

+
+ +
+
+
+
+
+
+ Loading... +
+

Loading available upgrades...

+
+
+
+
+
+
+ + + + + + + + +'; + +//OUTPUT +echo $view; + +template_footer(); \ No newline at end of file