<?php
/**
 * CSRF Protection Module
 * Generates, outputs, and verifies Cross-Site Request Forgery tokens.
 */

// Ensure this file is not accessed directly
if (!defined('DS')) {
    exit('Direct access not permitted.');
}

/**
 * Generates and stores a CSRF token in the session if one does not exist.
 *
 * @return string The current CSRF token.
 */
function generate_csrf_token() {
    if (empty($_SESSION['csrf_token'])) {
        // Generate 32 bytes of secure random data and convert to hex
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

/**
 * Generates a hidden HTML input field containing the CSRF token.
 * This should be included inside EVERY <form> in the application.
 *
 * @return string HTML input element.
 */
function csrf_field() {
    $token = generate_csrf_token();
    return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars($token, ENT_QUOTES, 'UTF-8') . '">';
}

/**
 * Verifies the CSRF token submitted in a POST request against the session token.
 * Uses hash_equals to prevent timing attacks.
 *
 * @return bool True if valid, False otherwise.
 */
function verify_csrf_token() {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token'])) {
            return false;
        }
        
        // Use hash_equals for secure, constant-time string comparison
        if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
            return true;
        }
        
        return false;
    }
    
    // If not a POST request, no token verification is needed
    return true;
}

/**
 * Enforces CSRF validation on POST requests.
 * Halts script execution with a 403 Forbidden status if validation fails.
 */
function enforce_csrf() {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!verify_csrf_token()) {
            http_response_code(403);
            exit('CSRF token validation failed. Please refresh the page and try again.');
        }
    }
}

// Automatically initialize the token for the current session
generate_csrf_token();