HEX
Server: LiteSpeed
System: Linux br-asc-web1311.main-hosting.eu 4.18.0-553.69.1.lve.el8.x86_64 #1 SMP Wed Aug 13 19:53:59 UTC 2025 x86_64
User: u353189757 (353189757)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: /home/u353189757/domains/adaptia.com.br/public_html/api/config.php
<?php
declare(strict_types=1);

const ADAPT_ADMIN_USERNAME = 'hzaccaro';
const ADAPT_ADMIN_PASSWORD_HASH = '$2y$12$6BQ8GaixugZqPs5.hzpHnuszBt01cCnvjhCL.huefn49MCul/AaPS';
const ADAPT_AUTH_RATE_SALT = 'b3c8df99c7252b6f1a63a399fb973c3c';
const ADAPT_AUTH_MAX_ATTEMPTS = 5;
const ADAPT_AUTH_LOCK_SECONDS = 900;
const ADAPT_HEATMAP_RETENTION_DAYS = 90;
const ADAPT_TRACKING_MAX_BODY_BYTES = 131072;
const ADAPT_TRACKING_MAX_EVENTS = 80;

function adapt_public_root(): string
{
    return dirname(__DIR__);
}

function adapt_private_root(): string
{
    $override = getenv('ADAPT_PRIVATE_DIR');

    if (is_string($override) && trim($override) !== '') {
        return rtrim($override, "\\/");
    }

    return dirname(adapt_public_root()) . DIRECTORY_SEPARATOR . 'adaptia-private';
}

function adapt_private_path(string $fileName): string
{
    return adapt_private_root() . DIRECTORY_SEPARATOR . $fileName;
}

function adapt_send_noindex_headers(): void
{
    header('X-Robots-Tag: noindex, nofollow', true);
}

function adapt_json_response(array $payload, int $statusCode = 200): void
{
    http_response_code($statusCode);
    adapt_send_noindex_headers();
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function adapt_ensure_private_storage(): void
{
    $dir = adapt_private_root();

    if (!is_dir($dir) && !mkdir($dir, 0700, true) && !is_dir($dir)) {
        throw new RuntimeException('private_storage_unavailable');
    }

    if (!is_writable($dir)) {
        throw new RuntimeException('private_storage_not_writable');
    }

    $sentinel = $dir . DIRECTORY_SEPARATOR . '.private';

    if (!is_file($sentinel)) {
        @file_put_contents($sentinel, "Adapt IA private storage\n", LOCK_EX);
    }
}

function adapt_storage_status(): array
{
    try {
        adapt_ensure_private_storage();

        return [
            'ok' => true,
            'path' => adapt_private_root(),
            'error' => '',
        ];
    } catch (Throwable $exception) {
        return [
            'ok' => false,
            'path' => adapt_private_root(),
            'error' => $exception->getMessage(),
        ];
    }
}

function adapt_write_json_file_locked(string $path, array $data): void
{
    $handle = fopen($path, 'c+');

    if (!$handle) {
        throw new RuntimeException('json_file_open_failed');
    }

    try {
        if (!flock($handle, LOCK_EX)) {
            throw new RuntimeException('json_file_lock_failed');
        }

        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);

        if (!is_string($json)) {
            throw new RuntimeException('json_encode_failed');
        }

        ftruncate($handle, 0);
        rewind($handle);
        fwrite($handle, $json . "\n");
        fflush($handle);
        flock($handle, LOCK_UN);
    } finally {
        fclose($handle);
    }
}

function adapt_read_json_file(string $path, array $fallback): array
{
    if (!is_file($path)) {
        return $fallback;
    }

    $content = file_get_contents($path);

    if (!is_string($content) || trim($content) === '') {
        return $fallback;
    }

    $decoded = json_decode($content, true);

    return is_array($decoded) ? $decoded : $fallback;
}

function adapt_append_submission(array $submission): void
{
    adapt_ensure_private_storage();

    $path = adapt_private_path('submissions.json');
    $handle = fopen($path, 'c+');

    if (!$handle) {
        throw new RuntimeException('submissions_file_open_failed');
    }

    try {
        if (!flock($handle, LOCK_EX)) {
            throw new RuntimeException('submissions_file_lock_failed');
        }

        rewind($handle);
        $content = stream_get_contents($handle);
        $decoded = is_string($content) && trim($content) !== '' ? json_decode($content, true) : null;
        $data = is_array($decoded) ? $decoded : ['version' => 1, 'items' => []];
        $data['items'] = isset($data['items']) && is_array($data['items']) ? $data['items'] : [];
        $data['items'][] = $submission;
        $data['updated_at'] = gmdate('c');

        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);

        if (!is_string($json)) {
            throw new RuntimeException('submissions_json_encode_failed');
        }

        ftruncate($handle, 0);
        rewind($handle);
        fwrite($handle, $json . "\n");
        fflush($handle);
        flock($handle, LOCK_UN);
    } finally {
        fclose($handle);
    }
}

function adapt_load_submissions(): array
{
    adapt_ensure_private_storage();

    return adapt_read_json_file(adapt_private_path('submissions.json'), ['version' => 1, 'items' => []]);
}

function adapt_append_tracking_records(array $records): void
{
    if (!$records) {
        return;
    }

    adapt_ensure_private_storage();
    adapt_maybe_prune_tracking_events();

    $handle = fopen(adapt_private_path('tracking-events.jsonl'), 'ab');

    if (!$handle) {
        throw new RuntimeException('tracking_file_open_failed');
    }

    try {
        if (!flock($handle, LOCK_EX)) {
            throw new RuntimeException('tracking_file_lock_failed');
        }

        foreach ($records as $record) {
            $line = json_encode($record, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

            if (is_string($line)) {
                fwrite($handle, $line . "\n");
            }
        }

        fflush($handle);
        flock($handle, LOCK_UN);
    } finally {
        fclose($handle);
    }
}

function adapt_maybe_prune_tracking_events(): void
{
    $today = gmdate('Y-m-d');
    $metaPath = adapt_private_path('tracking-prune.json');
    $meta = adapt_read_json_file($metaPath, []);

    if (($meta['last_pruned'] ?? '') === $today) {
        return;
    }

    $trackingPath = adapt_private_path('tracking-events.jsonl');

    if (is_file($trackingPath)) {
        $cutoff = time() - (ADAPT_HEATMAP_RETENTION_DAYS * 86400);
        $handle = fopen($trackingPath, 'c+');

        if (!$handle) {
            throw new RuntimeException('tracking_prune_open_failed');
        }

        try {
            if (!flock($handle, LOCK_EX)) {
                throw new RuntimeException('tracking_prune_lock_failed');
            }

            rewind($handle);
            $keptLines = [];

            while (($line = fgets($handle)) !== false) {
                $decoded = json_decode($line, true);
                $serverTime = is_array($decoded) ? strtotime((string) ($decoded['server_time'] ?? '')) : false;

                if ($serverTime !== false && $serverTime >= $cutoff) {
                    $keptLines[] = rtrim($line, "\r\n");
                }
            }

            ftruncate($handle, 0);
            rewind($handle);

            if ($keptLines) {
                fwrite($handle, implode("\n", $keptLines) . "\n");
            }

            fflush($handle);
            flock($handle, LOCK_UN);
        } finally {
            fclose($handle);
        }
    }

    adapt_write_json_file_locked($metaPath, ['last_pruned' => $today, 'retention_days' => ADAPT_HEATMAP_RETENTION_DAYS]);
}

function adapt_read_tracking_events(int $maxLines = 30000): array
{
    adapt_ensure_private_storage();

    $path = adapt_private_path('tracking-events.jsonl');

    if (!is_file($path)) {
        return [];
    }

    $handle = fopen($path, 'rb');

    if (!$handle) {
        return [];
    }

    $events = [];

    try {
        while (($line = fgets($handle)) !== false) {
            $decoded = json_decode($line, true);

            if (is_array($decoded)) {
                $events[] = $decoded;

                if (count($events) > $maxLines) {
                    array_shift($events);
                }
            }
        }
    } finally {
        fclose($handle);
    }

    return $events;
}

function adapt_clean_text(mixed $value, int $maxLength = 160): string
{
    $text = trim(strip_tags((string) $value));
    $text = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $text) ?? '';
    $text = preg_replace('/\s+/u', ' ', $text) ?? $text;
    $text = trim($text);

    if (function_exists('mb_substr')) {
        return mb_substr($text, 0, $maxLength, 'UTF-8');
    }

    return substr($text, 0, $maxLength);
}

function adapt_clean_multiline_text(mixed $value, int $maxLength = 2000): string
{
    $text = trim(strip_tags((string) $value));
    $text = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $text) ?? '';
    $text = preg_replace("/\r\n|\r/u", "\n", $text) ?? $text;
    $text = preg_replace("/\n{3,}/u", "\n\n", $text) ?? $text;
    $text = trim($text);

    if (function_exists('mb_substr')) {
        return mb_substr($text, 0, $maxLength, 'UTF-8');
    }

    return substr($text, 0, $maxLength);
}

function adapt_clean_url(mixed $value): string
{
    $url = adapt_clean_text($value, 500);

    if ($url === '') {
        return '';
    }

    if (str_starts_with($url, '/')) {
        return $url;
    }

    $parts = parse_url($url);

    if (!is_array($parts) || !isset($parts['scheme'], $parts['host'])) {
        return '';
    }

    $scheme = strtolower((string) $parts['scheme']);

    if (!in_array($scheme, ['http', 'https'], true)) {
        return '';
    }

    $path = isset($parts['path']) ? (string) $parts['path'] : '';

    return $scheme . '://' . strtolower((string) $parts['host']) . $path;
}

function adapt_clean_token(mixed $value, int $maxLength = 80): string
{
    $token = preg_replace('/[^a-zA-Z0-9._:-]/', '', (string) $value) ?? '';

    if (function_exists('mb_substr')) {
        return mb_substr($token, 0, $maxLength, 'UTF-8');
    }

    return substr($token, 0, $maxLength);
}

function adapt_int_between(mixed $value, int $min, int $max): int
{
    $number = is_numeric($value) ? (int) $value : 0;

    return max($min, min($max, $number));
}

function adapt_sanitize_viewport(mixed $viewport): array
{
    if (!is_array($viewport)) {
        return ['width' => 0, 'height' => 0, 'dpr' => 1];
    }

    return [
        'width' => adapt_int_between($viewport['width'] ?? 0, 0, 10000),
        'height' => adapt_int_between($viewport['height'] ?? 0, 0, 10000),
        'dpr' => max(1, min(5, (float) ($viewport['dpr'] ?? 1))),
    ];
}

function adapt_sanitize_section_list(mixed $sections): array
{
    if (!is_array($sections)) {
        return [];
    }

    $cleanSections = [];

    foreach (array_slice($sections, 0, 24) as $section) {
        if (!is_array($section)) {
            continue;
        }

        $id = adapt_clean_token($section['id'] ?? '', 80);

        if ($id === '') {
            continue;
        }

        $cleanSections[] = [
            'id' => $id,
            'title' => adapt_clean_text($section['title'] ?? $id, 120),
            'duration_ms' => adapt_int_between($section['duration_ms'] ?? 0, 0, 86400000),
        ];
    }

    return $cleanSections;
}

function adapt_sanitize_tracking_target(mixed $target): array
{
    if (!is_array($target)) {
        return [];
    }

    $tag = strtoupper(adapt_clean_text($target['tag'] ?? '', 20));
    $cleanTarget = [
        'tag' => $tag,
        'id' => adapt_clean_token($target['id'] ?? '', 80),
        'classes' => adapt_clean_text($target['classes'] ?? '', 120),
        'role' => adapt_clean_text($target['role'] ?? '', 60),
        'name' => adapt_clean_text($target['name'] ?? '', 80),
        'type' => adapt_clean_text($target['type'] ?? '', 40),
        'label' => adapt_clean_text($target['label'] ?? '', 120),
        'href' => adapt_clean_text($target['href'] ?? '', 160),
        'link_host' => adapt_clean_text($target['link_host'] ?? '', 120),
    ];

    if (in_array($tag, ['INPUT', 'TEXTAREA', 'SELECT'], true)) {
        $cleanTarget['label'] = '';
        $cleanTarget['href'] = '';
    }

    return array_filter($cleanTarget, static fn ($value) => $value !== '');
}

function adapt_sanitize_tracking_event(mixed $event): ?array
{
    if (!is_array($event)) {
        return null;
    }

    $type = adapt_clean_token($event['type'] ?? '', 40);
    $allowedTypes = ['session_start', 'section_enter', 'section_leave', 'click', 'scroll_depth', 'session_ping', 'session_end'];

    if (!in_array($type, $allowedTypes, true)) {
        return null;
    }

    $clean = [
        'type' => $type,
        't' => adapt_int_between($event['t'] ?? 0, 0, 86400000),
    ];

    if (isset($event['section_id'])) {
        $clean['section_id'] = adapt_clean_token($event['section_id'], 80);
    }

    if (isset($event['section_title'])) {
        $clean['section_title'] = adapt_clean_text($event['section_title'], 120);
    }

    if (isset($event['duration_ms'])) {
        $clean['duration_ms'] = adapt_int_between($event['duration_ms'], 0, 86400000);
    }

    if (isset($event['max_scroll_depth'])) {
        $clean['max_scroll_depth'] = adapt_int_between($event['max_scroll_depth'], 0, 100);
    }

    if (isset($event['click_count'])) {
        $clean['click_count'] = adapt_int_between($event['click_count'], 0, 10000);
    }

    if (isset($event['x'])) {
        $clean['x'] = adapt_int_between($event['x'], 0, 10000);
    }

    if (isset($event['y'])) {
        $clean['y'] = adapt_int_between($event['y'], 0, 10000);
    }

    if (isset($event['target'])) {
        $clean['target'] = adapt_sanitize_tracking_target($event['target']);
    }

    if (isset($event['sections'])) {
        $clean['sections'] = adapt_sanitize_section_list($event['sections']);
    }

    if (isset($event['viewport'])) {
        $clean['viewport'] = adapt_sanitize_viewport($event['viewport']);
    }

    return $clean;
}

function adapt_start_admin_session(): void
{
    if (session_status() === PHP_SESSION_ACTIVE) {
        return;
    }

    $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');

    session_name('adapt_admin');
    session_set_cookie_params([
        'lifetime' => 0,
        'path' => '/admin',
        'secure' => $isHttps,
        'httponly' => true,
        'samesite' => 'Strict',
    ]);
    session_start();
}

function adapt_client_rate_key(): string
{
    $ip = (string) ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    $agent = (string) ($_SERVER['HTTP_USER_AGENT'] ?? 'unknown');

    return hash('sha256', $ip . '|' . $agent . '|' . ADAPT_AUTH_RATE_SALT);
}

function adapt_auth_rate_data(): array
{
    adapt_ensure_private_storage();

    $data = adapt_read_json_file(adapt_private_path('auth-rate.json'), ['items' => []]);
    $data['items'] = isset($data['items']) && is_array($data['items']) ? $data['items'] : [];

    return $data;
}

function adapt_auth_locked_until(string $key): int
{
    $data = adapt_auth_rate_data();
    $entry = $data['items'][$key] ?? [];

    return is_array($entry) ? (int) ($entry['locked_until'] ?? 0) : 0;
}

function adapt_auth_record_failure(string $key): void
{
    $data = adapt_auth_rate_data();
    $entry = $data['items'][$key] ?? ['attempts' => 0, 'locked_until' => 0];
    $attempts = ((int) ($entry['attempts'] ?? 0)) + 1;
    $lockedUntil = $attempts >= ADAPT_AUTH_MAX_ATTEMPTS ? time() + ADAPT_AUTH_LOCK_SECONDS : 0;
    $data['items'][$key] = [
        'attempts' => $attempts,
        'locked_until' => $lockedUntil,
        'updated_at' => gmdate('c'),
    ];

    adapt_write_json_file_locked(adapt_private_path('auth-rate.json'), $data);
}

function adapt_auth_clear_rate(string $key): void
{
    $data = adapt_auth_rate_data();

    if (isset($data['items'][$key])) {
        unset($data['items'][$key]);
        adapt_write_json_file_locked(adapt_private_path('auth-rate.json'), $data);
    }
}