<?php
/**
 * Protected Page with Hash-based Access Control and IP Rate Limiting
 * 
 * Features:
 * - Requires 512-character hash in URL (domain.com/hash512/)
 * - Each session creates record with IP
 * - IP can only access 3 times
 * - Blocks IP for 30 minutes after 3 attempts
 * - Page inaccessible without valid hash
 */

error_reporting(0);
session_start();

// Include IP detection
require_once 'detect-ip.php';

// Configuration
define('MAX_ATTEMPTS', 3);
define('BLOCK_DURATION', 30 * 60); // 30 minutes in seconds
define('HASH_LENGTH', 512);
define('STORAGE_DIR', __DIR__ . '/ip-tracking/');

// Create storage directory if it doesn't exist
if (!file_exists(STORAGE_DIR)) {
    mkdir(STORAGE_DIR, 0755, true);
}

/**
 * Generate a 512-character hash
 * 
 * @return string 512-character hash
 */
function generateHash512() {
    // Generate random bytes and hash multiple times to get 512 characters
    $randomString = uniqid('', true) . microtime(true) . mt_rand() . $_SERVER['HTTP_USER_AGENT'];
    $hash = hash('sha256', $randomString);
    
    // Combine multiple hashes to reach 512 characters
    while (strlen($hash) < HASH_LENGTH) {
        $hash .= hash('sha256', $hash . time() . mt_rand());
    }
    
    // Trim to exactly 512 characters
    return substr($hash, 0, HASH_LENGTH);
}

/**
 * Get hash from URL
 * 
 * @return string|false Hash from URL or false
 */
function getHashFromURL() {
    $requestUri = $_SERVER['REQUEST_URI'];
    
    // Remove query string if exists
    $path = parse_url($requestUri, PHP_URL_PATH);
    
    // Extract hash from path (assumes format: /hash512/ or /hash512)
    $pathParts = explode('/', trim($path, '/'));
    
    foreach ($pathParts as $part) {
        if (strlen($part) === HASH_LENGTH && preg_match('/^[a-f0-9]+$/', $part)) {
            return $part;
        }
    }
    
    // Also check if hash is passed as GET parameter (for testing)
    if (isset($_GET['hash']) && strlen($_GET['hash']) === HASH_LENGTH) {
        return $_GET['hash'];
    }
    
    return false;
}

/**
 * Get IP tracking file path
 * 
 * @param string $ip IP address
 * @return string File path
 */
function getIPTrackingFile($ip) {
    $safeIP = preg_replace('/[^a-zA-Z0-9\-\.]/', '-', $ip);
    return STORAGE_DIR . 'ip_' . $safeIP . '.json';
}

/**
 * Get session tracking file path
 * 
 * @param string $sessionId Session ID
 * @return string File path
 */
function getSessionFile($sessionId) {
    return STORAGE_DIR . 'session_' . $sessionId . '.json';
}

/**
 * Check if IP is blocked
 * 
 * @param string $ip IP address
 * @return array ['blocked' => bool, 'until' => timestamp|null, 'attempts' => int]
 */
function checkIPStatus($ip) {
    $trackingFile = getIPTrackingFile($ip);
    
    if (!file_exists($trackingFile)) {
        return [
            'blocked' => false,
            'until' => null,
            'attempts' => 0,
            'access_times' => []
        ];
    }
    
    $data = json_decode(file_get_contents($trackingFile), true);
    if (!$data) {
        return [
            'blocked' => false,
            'until' => null,
            'attempts' => 0,
            'access_times' => []
        ];
    }
    
    $currentTime = time();
    $attempts = isset($data['attempts']) ? count($data['access_times']) : 0;
    $accessTimes = isset($data['access_times']) ? $data['access_times'] : [];
    
    // Check if IP is blocked
    if (isset($data['blocked_until']) && $data['blocked_until'] > $currentTime) {
        return [
            'blocked' => true,
            'until' => $data['blocked_until'],
            'attempts' => $attempts,
            'access_times' => $accessTimes
        ];
    }
    
    // If block expired, reset
    if (isset($data['blocked_until']) && $data['blocked_until'] <= $currentTime) {
        unlink($trackingFile);
        return [
            'blocked' => false,
            'until' => null,
            'attempts' => 0,
            'access_times' => []
        ];
    }
    
    // Clean old access times (older than block duration)
    $validAccessTimes = [];
    foreach ($accessTimes as $accessTime) {
        if ($accessTime > ($currentTime - BLOCK_DURATION)) {
            $validAccessTimes[] = $accessTime;
        }
    }
    
    // Update if we cleaned old entries
    if (count($validAccessTimes) !== count($accessTimes)) {
        $data['access_times'] = $validAccessTimes;
        file_put_contents($trackingFile, json_encode($data));
    }
    
    return [
        'blocked' => false,
        'until' => null,
        'attempts' => count($validAccessTimes),
        'access_times' => $validAccessTimes
    ];
}

/**
 * Record IP access
 * 
 * @param string $ip IP address
 * @param string $hash Hash used
 * @return array Status after recording
 */
function recordIPAccess($ip, $hash) {
    $trackingFile = getIPTrackingFile($ip);
    $currentTime = time();
    
    $data = [];
    if (file_exists($trackingFile)) {
        $data = json_decode(file_get_contents($trackingFile), true) ?: [];
    }
    
    if (!isset($data['access_times'])) {
        $data['access_times'] = [];
    }
    
    // Add current access time
    $data['access_times'][] = $currentTime;
    
    // Clean old access times
    $validAccessTimes = [];
    foreach ($data['access_times'] as $accessTime) {
        if ($accessTime > ($currentTime - BLOCK_DURATION)) {
            $validAccessTimes[] = $accessTime;
        }
    }
    $data['access_times'] = $validAccessTimes;
    
    $attempts = count($validAccessTimes);
    
    // Check if should be blocked
    if ($attempts >= MAX_ATTEMPTS) {
        $data['blocked_until'] = $currentTime + BLOCK_DURATION;
        $data['blocked_at'] = $currentTime;
    }
    
    $data['last_access'] = $currentTime;
    $data['ip'] = $ip;
    $data['hash_used'] = $hash;
    
    file_put_contents($trackingFile, json_encode($data));
    
    return [
        'blocked' => ($attempts >= MAX_ATTEMPTS),
        'until' => $data['blocked_until'] ?? null,
        'attempts' => $attempts
    ];
}

/**
 * Create or update session with IP
 * 
 * @param string $ip IP address
 * @param string $hash Hash used
 */
function createSession($ip, $hash) {
    $sessionId = session_id();
    if (empty($sessionId)) {
        session_start();
        $sessionId = session_id();
    }
    
    $sessionFile = getSessionFile($sessionId);
    
    $sessionData = [
        'session_id' => $sessionId,
        'ip' => $ip,
        'hash' => $hash,
        'created_at' => time(),
        'last_access' => time(),
        'access_count' => 1
    ];
    
    if (file_exists($sessionFile)) {
        $existing = json_decode(file_get_contents($sessionFile), true);
        if ($existing) {
            $sessionData['access_count'] = ($existing['access_count'] ?? 0) + 1;
            $sessionData['created_at'] = $existing['created_at'];
        }
    }
    
    $_SESSION['protected_hash'] = $hash;
    $_SESSION['protected_ip'] = $ip;
    $_SESSION['protected_session'] = true;
    
    file_put_contents($sessionFile, json_encode($sessionData));
}

// Main execution
$ip = detect_ip();
$hash = getHashFromURL();

// Check if hash is provided
if (!$hash || strlen($hash) !== HASH_LENGTH) {
    http_response_code(403);
    die("Access Denied: Valid hash required. URL format: domain.com/{512-character-hash}/");
}

// Check IP status
$ipStatus = checkIPStatus($ip);

// If IP is blocked
if ($ipStatus['blocked']) {
    $blockedUntil = date('Y-m-d H:i:s', $ipStatus['until']);
    $remainingMinutes = round(($ipStatus['until'] - time()) / 60);
    
    http_response_code(429);
    die("Access Denied: IP address blocked. Maximum attempts exceeded. Blocked until: {$blockedUntil} ({$remainingMinutes} minutes remaining)");
}

// Record access
$accessResult = recordIPAccess($ip, $hash);

// If this access triggered a block
if ($accessResult['blocked']) {
    $blockedUntil = date('Y-m-d H:i:s', $accessResult['until']);
    $remainingMinutes = round(($accessResult['until'] - time()) / 60);
    
    http_response_code(429);
    die("Access Denied: IP address blocked. Maximum attempts exceeded. Blocked until: {$blockedUntil} ({$remainingMinutes} minutes remaining)");
}

// Create/update session
createSession($ip, $hash);

// Calculate remaining attempts
$remainingAttempts = MAX_ATTEMPTS - $ipStatus['attempts'] - 1;

// If all attempts used (should not happen, but check anyway)
if ($remainingAttempts < 0) {
    http_response_code(429);
    die("Access Denied: Maximum attempts exceeded.");
}

// Success - Page content starts here
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Protected Page</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background: #f5f5f5;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
        }
        .info {
            background: #e7f3ff;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
            border-left: 4px solid #007bff;
        }
        .warning {
            background: #fff3cd;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
            border-left: 4px solid #ffc107;
        }
        .success {
            background: #d4edda;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
            border-left: 4px solid #28a745;
        }
        .info-item {
            margin: 10px 0;
            padding: 8px;
            background: #f8f9fa;
            border-radius: 3px;
        }
        .info-label {
            font-weight: bold;
            color: #666;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🔒 Protected Page</h1>
        
        <div class="success">
            ✅ Access granted! You have successfully accessed this protected page.
        </div>
        
        <div class="info">
            <h3>📊 Access Information</h3>
            <div class="info-item">
                <span class="info-label">Your IP:</span> <?php echo htmlspecialchars($ip); ?>
            </div>
            <div class="info-item">
                <span class="info-label">Hash Used:</span> <?php echo htmlspecialchars(substr($hash, 0, 32)) . '...'; ?>
            </div>
            <div class="info-item">
                <span class="info-label">Access Attempts:</span> <?php echo ($ipStatus['attempts'] + 1); ?> / <?php echo MAX_ATTEMPTS; ?>
            </div>
            <div class="info-item">
                <span class="info-label">Remaining Attempts:</span> <?php echo $remainingAttempts; ?>
            </div>
            <div class="info-item">
                <span class="info-label">Session ID:</span> <?php echo session_id(); ?>
            </div>
            <div class="info-item">
                <span class="info-label">Access Time:</span> <?php echo date('Y-m-d H:i:s'); ?>
            </div>
        </div>
        
        <?php if ($remainingAttempts <= 1): ?>
        <div class="warning">
            ⚠️ <strong>Warning:</strong> You have <?php echo $remainingAttempts; ?> attempt(s) remaining. After <?php echo MAX_ATTEMPTS; ?> attempts, your IP will be blocked for <?php echo BLOCK_DURATION / 60; ?> minutes.
        </div>
        <?php endif; ?>
        
        <div class="info">
            <h3>ℹ️ Page Information</h3>
            <p>This page is protected with:</p>
            <ul>
                <li>512-character hash validation</li>
                <li>IP-based access tracking</li>
                <li>Rate limiting (<?php echo MAX_ATTEMPTS; ?> attempts per <?php echo BLOCK_DURATION / 60; ?> minutes)</li>
                <li>Session management</li>
            </ul>
        </div>
        
        <hr>
        <p><small>Protected Page System - All access is logged and monitored.</small></p>
    </div>
</body>
</html>
