<?php
/**
 * (c) ININOUT & PYXIS. All Rights Reserved.
 * Wszelkie modyfikacje tego pliku wymagaja zgloszenia oraz zgody autorow.
 * Any modifications to this file require notification and consent of the authors.
 */
/**
 * i-BOK 5 - Bezpieczeństwo (CSRF, XSS, rate limiting)
 */

/**
 * Zwróć CSP nonce dla bieżącego requestu
 */
function csp_nonce() {
    return $GLOBALS['_csp_nonce'] ?? '';
}

/**
 * Escape HTML - ochrona przed XSS
 */
function e($str) {
    if ($str === null) return '';
    return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}

/**
 * Generuj token CSRF
 */
function csrf_token() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

/**
 * Pole ukryte CSRF do formularza
 */
function csrf_field() {
    return '<input type="hidden" name="csrf_token" value="' . csrf_token() . '">';
}

/**
 * Waliduj token CSRF
 */
function csrf_validate() {
    if (empty($_POST['csrf_token']) || empty($_SESSION['csrf_token'])) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']);
}

/**
 * Rate limiting - sprawdź czy IP jest zablokowane
 */
function rate_limit_check($ip, $max_attempts = null, $lockout_time = null) {
    if ($max_attempts === null) $max_attempts = defined('IBOK_MAX_LOGIN_ATTEMPTS') ? IBOK_MAX_LOGIN_ATTEMPTS : 5;
    if ($lockout_time === null) $lockout_time = defined('IBOK_LOCKOUT_TIME') ? IBOK_LOCKOUT_TIME : 900;

    $file = (defined('IBOK_LOGS_DIR') ? IBOK_LOGS_DIR : __DIR__ . '/../logs') . '/rate_' . md5($ip) . '.json';

    if (!file_exists($file)) {
        return true; // Brak prób - dozwolone
    }

    $data = json_decode(file_get_contents($file), true);
    if (!is_array($data)) return true;

    // Usuń stare wpisy
    $now = time();
    $data = array_filter($data, function($ts) use ($now, $lockout_time) {
        return ($now - $ts) < $lockout_time;
    });

    return count($data) < $max_attempts;
}

/**
 * Rate limiting - zapisz próbę
 */
function rate_limit_record($ip) {
    $dir = defined('IBOK_LOGS_DIR') ? IBOK_LOGS_DIR : __DIR__ . '/../logs';
    $file = $dir . '/rate_' . md5($ip) . '.json';

    $data = [];
    if (file_exists($file)) {
        $data = json_decode(file_get_contents($file), true);
        if (!is_array($data)) $data = [];
    }

    $data[] = time();

    // Ogranicz do 50 wpisów
    $data = array_slice($data, -50);

    @file_put_contents($file, json_encode($data), LOCK_EX);
}

/**
 * Rate limiting - wyczyść po udanym logowaniu
 */
function rate_limit_clear($ip) {
    $file = (defined('IBOK_LOGS_DIR') ? IBOK_LOGS_DIR : __DIR__ . '/../logs') . '/rate_' . md5($ip) . '.json';
    if (file_exists($file)) {
        @unlink($file);
    }
}

/**
 * Loguj zdarzenie do pliku
 */
function ibok_log($message, $type = 'info') {
    if (function_exists('ibok_is_demo') && ibok_is_demo()) return;
    $dir = defined('IBOK_LOGS_DIR') ? IBOK_LOGS_DIR : __DIR__ . '/../logs';
    $file = $dir . '/ibok_' . date('Y-m') . '.log';
    $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $line = date('Y-m-d H:i:s') . " [{$type}] [{$ip}] {$message}\n";
    @file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
}

/**
 * Bezpieczna walidacja ścieżki pliku (zapobiega path traversal)
 */
function safe_path($base_dir, $filename) {
    $filename = basename($filename);
    $full = realpath($base_dir . '/' . $filename);
    if ($full === false) return false;
    if (strpos($full, realpath($base_dir)) !== 0) return false;
    return $full;
}
