403Webshell
Server IP : 213.255.246.8  /  Your IP : 216.73.217.178
Web Server : Apache
System : Linux dublin.stapolin.com 5.14.0-362.18.1.el9_3.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Jan 29 07:05:48 EST 2024 x86_64
User : stapolin ( 1019)
PHP Version : 8.4.24
Disable Function : exec,passthru,shell_exec,system
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/stapolin/public_html/tracking.stapolin.com/api/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/stapolin/public_html/tracking.stapolin.com/api/collect.php
<?php

declare(strict_types=1);

require_once __DIR__ . '/../includes/bootstrap.php';
require_once APP_ROOT . '/includes/conversions.php';

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    handle_cors_preflight();
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    json_response(false, null, 'Unsupported request method.', 405);
}

$raw = file_get_contents('php://input') ?: '';
if (strlen($raw) > 16384) {
    json_response(false, null, 'Payload too large.', 413);
}

$payload = json_decode($raw, true);
if (!is_array($payload)) {
    json_response(false, null, 'Invalid payload.', 400);
}

$siteId = string_value($payload, 'site_id', 80);
$website = db_fetch_one('SELECT * FROM websites WHERE public_id = ? AND status = "active"', 's', [$siteId]);
if ($website === null) {
    log_tracking_error(null, 'invalid_site', 'Unknown or inactive site ID.');
    json_response(false, null, 'Invalid site.', 404);
}

if (!origin_is_allowed((int) $website['id'])) {
    log_tracking_error((int) $website['id'], 'invalid_origin', 'Origin is not allowed.');
    json_response(false, null, 'Origin is not allowed.', 403);
}

send_cors_headers();

if ((int) $website['bot_filtering_enabled'] === 1 && looks_like_bot()) {
    log_tracking_error((int) $website['id'], 'bot_filtered', 'Known bot or automated browser filtered.');
    json_response(true, ['accepted' => false, 'reason' => 'bot_filtered'], null, 202);
}

if (rate_limited((int) $website['id'])) {
    log_tracking_error((int) $website['id'], 'rate_limited', 'Tracking request rate limit exceeded.');
    json_response(false, null, 'Rate limit exceeded.', 429);
}

if ((int) $website['honour_do_not_track'] === 1 && boolean_value($payload['do_not_track'] ?? false)) {
    json_response(true, ['accepted' => false, 'reason' => 'do_not_track'], null, 202);
}

$type = string_value($payload, 'type', 40);
if (!in_array($type, ['page_view', 'page_exit', 'custom_event'], true)) {
    json_response(false, null, 'Unsupported event type.', 400);
}

try {
    db()->begin_transaction();
    $now = utc_now();
    $visitorId = upsert_visitor((int) $website['id'], string_value($payload, 'visitor_id', 80), string_value($payload, 'consent_mode', 20), $now);
    $sessionId = upsert_session((int) $website['id'], $visitorId, string_value($payload, 'session_id', 80), $payload, $now);
    $pageId = upsert_page((int) $website['id'], normalised_path($payload, $website), string_value($payload, 'title', 300), $now);

    if ($type === 'page_view') {
        $pageInserted = save_page_view((int) $website['id'], $sessionId, $visitorId, $pageId, $payload, $now);
        if ($pageInserted) {
            detect_page_view_conversions((int) $website['id'], $sessionId, $visitorId, $pageId, normalised_path($payload, $website), $now);
        }
    } elseif ($type === 'page_exit') {
        save_page_exit((int) $website['id'], $sessionId, $visitorId, $pageId, $payload, $now);
    } else {
        $eventId = save_event((int) $website['id'], $sessionId, $visitorId, $pageId, $payload, $type, $now);
        if ($eventId !== null) {
            detect_event_conversions((int) $website['id'], $sessionId, $visitorId, $eventId, event_name_from_payload($payload, $type), numeric_value($payload['value'] ?? null), string_value($payload, 'currency', 3) ?: null, $now);
        }
    }

    update_live_activity((int) $website['id'], $sessionId, $visitorId, $pageId, $type, $now);

    db()->commit();
    json_response(true, ['accepted' => true], null, 202);
} catch (Throwable $exception) {
    db()->rollback();
    error_log('Collect failed: ' . $exception->getMessage());
    log_tracking_error(
        (int) $website['id'],
        'collect_failed',
        'Collection failed: ' . collect_error_message($exception),
        collect_request_meta($payload, $exception)
    );
    json_response(false, null, 'Collection failed.', 500);
}

function handle_cors_preflight(): never
{
    send_cors_headers();
    http_response_code(204);
    exit;
}

function send_cors_headers(): void
{
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
    if ($origin !== '') {
        header('Access-Control-Allow-Origin: ' . $origin);
        header('Access-Control-Allow-Methods: POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');
        header('Vary: Origin');
    }
}

function origin_is_allowed(int $websiteId): bool
{
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
    $referer = $_SERVER['HTTP_REFERER'] ?? '';
    $host = parse_url($origin !== '' ? $origin : $referer, PHP_URL_HOST);
    if (!is_string($host) || $host === '') {
        return false;
    }
    $host = strtolower($host);
    $row = db_fetch_one('SELECT id FROM website_domains WHERE website_id = ? AND domain = ?', 'is', [$websiteId, $host]);
    return $row !== null;
}

function looks_like_bot(): bool
{
    $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
    if ($ua === '') {
        return true;
    }

    return preg_match('/bot|crawler|spider|slurp|headless|phantom|selenium|uptime|monitor|curl|wget|python-requests|httpclient/', $ua) === 1;
}

function rate_limited(int $websiteId): bool
{
    $windowStart = gmdate('Y-m-d H:i:00');
    $ipRange = ip_rate_bucket(client_ip());
    $bucket = 'collect';
    $limit = 180;

    $row = db_fetch_one(
        'SELECT id, hits FROM rate_limits WHERE website_id = ? AND ip_range = ? AND bucket_key = ? AND window_start = ?',
        'isss',
        [$websiteId, $ipRange, $bucket, $windowStart]
    );

    if ($row === null) {
        db_execute(
            'INSERT INTO rate_limits (website_id, ip_range, bucket_key, hits, window_start, updated_at) VALUES (?, ?, ?, 1, ?, ?)',
            'issss',
            [$websiteId, $ipRange, $bucket, $windowStart, utc_now()]
        );
        return false;
    }

    $hits = (int) $row['hits'] + 1;
    db_execute('UPDATE rate_limits SET hits = ?, updated_at = ? WHERE id = ?', 'isi', [$hits, utc_now(), (int) $row['id']]);
    return $hits > $limit;
}

function ip_rate_bucket(string $ip): string
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $parts = explode('.', $ip);
        return $parts[0] . '.' . $parts[1] . '.' . $parts[2] . '.0/24';
    }
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
        return substr($ip, 0, 20) . '::/64';
    }
    return 'unknown';
}

function string_value(array $payload, string $key, int $maxLength): string
{
    $value = $payload[$key] ?? '';
    if (!is_scalar($value)) {
        return '';
    }
    return substr(trim((string) $value), 0, $maxLength);
}

function boolean_value(mixed $value): bool
{
    return $value === true || $value === 1 || $value === '1' || $value === 'true';
}

function normalised_path(array $payload, array $website): string
{
    $url = string_value($payload, 'url', 900);
    $path = parse_url($url, PHP_URL_PATH) ?: (string) ($payload['path'] ?? '/');
    $path = '/' . ltrim($path, '/');
    $path = rtrim($path, '/') ?: '/';
    $query = (string) (parse_url($url, PHP_URL_QUERY) ?? '');
    $queryHandling = (string) ($website['query_handling'] ?? 'drop_all');

    if ($query === '' || $queryHandling === 'drop_all') {
        return $path;
    }

    parse_str($query, $params);
    unset($params['utm_source'], $params['utm_medium'], $params['utm_campaign'], $params['utm_term'], $params['utm_content'], $params['gclid'], $params['fbclid']);

    if ($queryHandling === 'keep_allowed') {
        $allowed = array_filter(array_map('trim', explode(',', (string) ($website['allowed_query_params'] ?? ''))));
        $params = array_intersect_key($params, array_flip($allowed));
    }

    if ($params === []) {
        return $path;
    }

    ksort($params);
    return $path . '?' . http_build_query($params);
}

function upsert_visitor(int $websiteId, string $publicId, string $consentMode, string $now): ?int
{
    if ($publicId === '') {
        return null;
    }
    $consentMode = in_array($consentMode, ['persistent', 'session', 'cookieless'], true) ? $consentMode : 'session';
    $row = db_fetch_one('SELECT id FROM visitors WHERE website_id = ? AND public_id = ?', 'is', [$websiteId, $publicId]);
    if ($row !== null) {
        db_execute('UPDATE visitors SET last_seen_at = ?, consent_mode = ? WHERE id = ?', 'ssi', [$now, $consentMode, (int) $row['id']]);
        return (int) $row['id'];
    }
    db_execute(
        'INSERT INTO visitors (website_id, public_id, first_seen_at, last_seen_at, consent_mode) VALUES (?, ?, ?, ?, ?)',
        'issss',
        [$websiteId, $publicId, $now, $now, $consentMode]
    );
    return db_insert_id();
}

function upsert_session(int $websiteId, ?int $visitorId, string $publicId, array $payload, string $now): int
{
    if ($publicId === '') {
        $publicId = random_token(16);
    }
    $row = db_fetch_one('SELECT id FROM sessions WHERE website_id = ? AND public_id = ?', 'is', [$websiteId, $publicId]);
    if ($row !== null) {
        $existing = db_fetch_one('SELECT id, last_activity_at FROM sessions WHERE id = ?', 'i', [(int) $row['id']]);
        $timeoutAt = gmdate('Y-m-d H:i:s', time() - (int) config_value('tracking.session_timeout_minutes', 30) * 60);
        if ($existing !== null && (string) $existing['last_activity_at'] < $timeoutAt) {
            db_execute('UPDATE sessions SET ended_at = COALESCE(ended_at, last_activity_at) WHERE id = ?', 'i', [(int) $existing['id']]);
            $publicId = substr($publicId . '_' . random_token(6), 0, 80);
        } else {
            $geo = geo_from_request();
            db_execute(
                'UPDATE sessions SET visitor_id = ?, last_activity_at = ?, total_events = total_events + 1, country = COALESCE(country, ?), region = COALESCE(region, ?), city = COALESCE(city, ?) WHERE id = ?',
                'issssi',
                [$visitorId, $now, $geo['country'], $geo['region'], $geo['city'], (int) $row['id']]
            );
            return (int) $row['id'];
        }
    }
    $agent = parse_user_agent($_SERVER['HTTP_USER_AGENT'] ?? '');
    $referrer = string_value($payload, 'referrer', 700);
    $geo = geo_from_request();
    db_execute(
        'INSERT INTO sessions (website_id, visitor_id, public_id, started_at, last_activity_at, referrer_url, referrer_domain, device_category, browser, browser_version, operating_system, consent_mode, country, region, city) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
        'iisssssssssssss',
        [$websiteId, $visitorId, $publicId, $now, $now, $referrer, referrer_domain($referrer), $agent['device'], $agent['browser'], $agent['version'], $agent['os'], consent_mode(string_value($payload, 'consent_mode', 20)), $geo['country'], $geo['region'], $geo['city']]
    );
    return db_insert_id();
}

function geo_from_request(): array
{
    $country = country_header_value([
        'HTTP_CF_IPCOUNTRY',
        'HTTP_CLOUDFRONT_VIEWER_COUNTRY',
        'HTTP_X_APPENGINE_COUNTRY',
        'HTTP_X_VERCEL_IP_COUNTRY',
        'HTTP_X_COUNTRY_CODE',
    ]);

    $region = region_header_value([
        'HTTP_CF_REGION',
        'HTTP_X_APPENGINE_REGION',
        'HTTP_X_VERCEL_IP_COUNTRY_REGION',
        'HTTP_X_REGION',
    ]);
    $city = city_header_value([
        'HTTP_CF_IPCITY',
        'HTTP_X_VERCEL_IP_CITY',
        'HTTP_X_CITY',
    ]);

    if ($country === null) {
        $lookup = geoip_lookup_country(client_ip());
        if ($lookup !== null) {
            $country = $lookup['country'];
            $region = $lookup['region'];
            $city = $lookup['city'];
        }
    }

    return ['country' => $country, 'region' => $region, 'city' => $city];
}

function country_header_value(array $headerNames): ?string
{
    foreach ($headerNames as $headerName) {
        $value = strtoupper(trim((string) ($_SERVER[$headerName] ?? '')));
        if (preg_match('/^[A-Z]{2}$/', $value) === 1 && !in_array($value, ['XX', 'T1'], true)) {
            return $value;
        }
    }
    return null;
}

function region_header_value(array $headerNames): ?string
{
    foreach ($headerNames as $headerName) {
        $value = trim((string) ($_SERVER[$headerName] ?? ''));
        if ($value !== '') {
            return substr(preg_replace('/[^\p{L}\p{N}\s._-]/u', '', $value) ?: '', 0, 120) ?: null;
        }
    }
    return null;
}

function city_header_value(array $headerNames): ?string
{
    return region_header_value($headerNames);
}

function consent_mode(string $mode): string
{
    return in_array($mode, ['persistent', 'session', 'cookieless'], true) ? $mode : 'session';
}

function referrer_domain(string $referrer): ?string
{
    $host = parse_url($referrer, PHP_URL_HOST);
    return is_string($host) && $host !== '' ? strtolower($host) : null;
}

function parse_user_agent(string $ua): array
{
    $lower = strtolower($ua);
    $device = str_contains($lower, 'tablet') || str_contains($lower, 'ipad') ? 'tablet' : (preg_match('/mobile|iphone|android/', $lower) === 1 ? 'mobile' : 'desktop');
    $os = 'Unknown';
    foreach (['Windows' => 'windows', 'macOS' => 'mac os', 'iOS' => 'iphone|ipad', 'Android' => 'android', 'Linux' => 'linux'] as $name => $pattern) {
        if (preg_match('/' . $pattern . '/', $lower) === 1) {
            $os = $name;
            break;
        }
    }

    $browser = 'Unknown';
    $version = null;
    $patterns = [
        'Edge' => '/edg\/([\d.]+)/i',
        'Chrome' => '/chrome\/([\d.]+)/i',
        'Firefox' => '/firefox\/([\d.]+)/i',
        'Safari' => '/version\/([\d.]+).*safari/i',
    ];
    foreach ($patterns as $name => $pattern) {
        if (preg_match($pattern, $ua, $match) === 1) {
            $browser = $name;
            $version = substr($match[1], 0, 60);
            break;
        }
    }

    return ['device' => $device, 'browser' => $browser, 'version' => $version, 'os' => $os];
}

function upsert_page(int $websiteId, string $path, string $title, string $now): int
{
    $hash = hash('sha256', $path);
    $row = db_fetch_one('SELECT id FROM pages WHERE website_id = ? AND path_hash = ?', 'is', [$websiteId, $hash]);
    if ($row !== null) {
        db_execute('UPDATE pages SET latest_title = ?, updated_at = ? WHERE id = ?', 'ssi', [$title, $now, (int) $row['id']]);
        return (int) $row['id'];
    }
    db_execute(
        'INSERT INTO pages (website_id, path_hash, path, latest_title, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)',
        'isssss',
        [$websiteId, $hash, $path, $title, $now, $now]
    );
    return db_insert_id();
}

function save_page_view(int $websiteId, int $sessionId, ?int $visitorId, int $pageId, array $payload, string $now): bool
{
    $eventUuid = string_value($payload, 'page_view_uuid', 36) ?: string_value($payload, 'event_uuid', 36) ?: random_token(16);
    $stmt = db_execute(
        'INSERT IGNORE INTO page_views (website_id, session_id, visitor_id, page_id, event_uuid, title, url, referrer_url, entered_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
        'iiiisssss',
        [$websiteId, $sessionId, $visitorId, $pageId, $eventUuid, string_value($payload, 'title', 300), string_value($payload, 'url', 900), string_value($payload, 'referrer', 700), $now]
    );
    if ($stmt->affected_rows === 0) {
        log_tracking_error($websiteId, 'duplicate_event', 'Duplicate page-view event ignored.');
        return false;
    }
    db_execute('UPDATE sessions SET page_views = page_views + 1, entry_page_id = COALESCE(entry_page_id, ?), exit_page_id = ?, last_activity_at = ? WHERE id = ?', 'iisi', [$pageId, $pageId, $now, $sessionId]);
    return true;
}

function save_page_exit(int $websiteId, int $sessionId, ?int $visitorId, int $pageId, array $payload, string $now): void
{
    $pageViewUuid = string_value($payload, 'page_view_uuid', 36);
    $timeOnPage = max(0, min(86400, (int) ($payload['time_on_page'] ?? 0)));
    $scrollDepth = max(0, min(100, (int) ($payload['scroll_depth'] ?? 0)));

    if ($pageViewUuid !== '') {
        db_execute(
            'UPDATE page_views SET exited_at = ?, time_on_page_seconds = ?, max_scroll_depth = ? WHERE website_id = ? AND session_id = ? AND event_uuid = ?',
            'siiiis',
            [$now, $timeOnPage, $scrollDepth, $websiteId, $sessionId, $pageViewUuid]
        );
    }

    save_event($websiteId, $sessionId, $visitorId, $pageId, $payload, 'page_exit', $now);
}

function save_event(int $websiteId, int $sessionId, ?int $visitorId, int $pageId, array $payload, string $type, string $now): ?int
{
    $name = event_name_from_payload($payload, $type);
    $eventUuid = string_value($payload, 'event_uuid', 36) ?: random_token(16);
    $properties = $payload['properties'] ?? null;
    $metadata = is_array($properties) ? json_encode($properties, JSON_THROW_ON_ERROR) : null;
    $stmt = db_execute(
        'INSERT IGNORE INTO events (website_id, session_id, visitor_id, page_id, event_uuid, event_type, category, label, numeric_value, metadata_json, occurred_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
        'iiiissssdss',
        [$websiteId, $sessionId, $visitorId, $pageId, $eventUuid, $name, string_value($payload, 'category', 120), string_value($payload, 'label', 190), numeric_value($payload['value'] ?? null), $metadata, $now]
    );
    if ($stmt->affected_rows === 0) {
        log_tracking_error($websiteId, 'duplicate_event', 'Duplicate event ignored.');
        return null;
    }
    $eventId = db_insert_id();
    db_execute('UPDATE sessions SET total_events = total_events + 1, last_activity_at = ? WHERE id = ?', 'si', [$now, $sessionId]);
    return $eventId;
}

function event_name_from_payload(array $payload, string $type): string
{
    return $type === 'custom_event' ? string_value($payload, 'event_name', 80) : $type;
}

function update_live_activity(int $websiteId, int $sessionId, ?int $visitorId, int $pageId, string $type, string $now): void
{
    $previous = db_fetch_one('SELECT current_page_id FROM live_activity WHERE website_id = ? AND session_id = ?', 'ii', [$websiteId, $sessionId]);
    $previousPageId = $previous !== null ? (int) $previous['current_page_id'] : null;
    db_execute(
        'REPLACE INTO live_activity (website_id, session_id, visitor_id, current_page_id, previous_page_id, last_event_type, last_seen_at, page_view_count) VALUES (?, ?, ?, ?, ?, ?, ?, (SELECT page_views FROM sessions WHERE id = ?))',
        'iiiiissi',
        [$websiteId, $sessionId, $visitorId, $pageId, $previousPageId, $type, $now, $sessionId]
    );
}

function numeric_value(mixed $value): ?float
{
    return is_numeric($value) ? (float) $value : null;
}

function collect_error_message(Throwable $exception): string
{
    return substr($exception::class . ': ' . $exception->getMessage(), 0, 450);
}

function collect_request_meta(array $payload, Throwable $exception): array
{
    $url = string_value($payload, 'url', 900);
    return [
        'event_type' => string_value($payload, 'type', 40),
        'url' => substr($url, 0, 300),
        'path' => substr((string) (parse_url($url, PHP_URL_PATH) ?: ($payload['path'] ?? '')), 0, 200),
        'origin' => substr((string) ($_SERVER['HTTP_ORIGIN'] ?? ''), 0, 200),
        'user_agent' => substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 300),
        'exception' => substr($exception::class, 0, 120),
        'exception_line' => basename($exception->getFile()) . ':' . $exception->getLine(),
    ];
}

function log_tracking_error(?int $websiteId, string $type, string $message, array $meta = []): void
{
    try {
        $message = substr($message, 0, 500);
        $metaJson = $meta === [] ? null : json_encode($meta, JSON_THROW_ON_ERROR);
        db_execute(
            'INSERT INTO tracking_errors (website_id, error_type, message, ip_address, request_meta_json, created_at) VALUES (?, ?, ?, ?, ?, ?)',
            'isssss',
            [$websiteId, $type, $message, client_ip(), $metaJson, utc_now()]
        );
    } catch (Throwable $exception) {
        error_log('Tracking error log failed: ' . $exception->getMessage());
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit