403Webshell
Server IP : 188.114.96.3  /  Your IP : 216.73.216.143
Web Server : Apache
System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64
User :  ( 3194769)
PHP Version : 8.5.12
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : OFF
Directory :  /polana/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /polana/includes/polana_ntfy_v186.php
<?php
declare(strict_types=1);
/**
 * POLANA V18.6 - optional push notifications, stored only outside public WWW.
 * This library never changes reservations, calendar OAuth, or backup configuration.
 */
require_once __DIR__ . '/polana_backup_v182.php';

function pnt186_config_path(): string {
    return pbk_private() . '/polana_ntfy_v186.json';
}

function pnt186_config(): array {
    $defaults = ['enabled' => false, 'url' => '', 'topic' => '', 'token_enc' => ''];
    $path = pnt186_config_path();
    if (!is_file($path)) return $defaults;
    $data = json_decode((string)file_get_contents($path), true);
    if (!is_array($data)) throw new RuntimeException('Nieprawidłowy format ustawień ntfy.');
    return array_merge($defaults, array_intersect_key($data, $defaults));
}

function pnt186_validate_url(string $url): string {
    $url = trim($url);
    $parsed = parse_url($url);
    if (!is_array($parsed) || strtolower((string)($parsed['scheme'] ?? '')) !== 'https'
        || empty($parsed['host']) || isset($parsed['user']) || isset($parsed['pass'])
        || isset($parsed['query']) || isset($parsed['fragment'])
        || isset($parsed['port']) || !in_array(($parsed['path'] ?? ''), ['', '/'], true)) {
        throw new RuntimeException('Podaj adres HTTPS serwera ntfy, np. https://ntfy.example.com, bez portu i ścieżki.');
    }
    $host = strtolower((string)$parsed['host']);
    if (!preg_match('/^(?=.{1,253}$)[a-z0-9](?:[a-z0-9.-]*[a-z0-9])$/D', $host)
        || !str_contains($host, '.') || str_ends_with($host, '.local') || str_ends_with($host, '.internal')
        || str_ends_with($host, '.localhost') || str_ends_with($host, '.test')
        || filter_var($host, FILTER_VALIDATE_IP) !== false) {
        throw new RuntimeException('Wymagana publiczna domena DNS serwera ntfy.');
    }
    return 'https://' . $host;
}

function pnt186_validate_topic(string $topic): string {
    $topic = trim($topic);
    if (!preg_match('/^[A-Za-z0-9_-]{12,80}$/D', $topic)) {
        throw new RuntimeException('Temat ntfy musi mieć 12-80 znaków: litery, cyfry, - lub _. Użyj trudnego do odgadnięcia tematu.');
    }
    return $topic;
}

function pnt186_save(array $settings): void {
    $path = pnt186_config_path();
    $tmp = dirname($path) . '/.ntfy-v186-' . bin2hex(random_bytes(8)) . '.tmp';
    $bytes = json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
    $handle = @fopen($tmp, 'xb');
    if (!$handle) throw new RuntimeException('Nie można zapisać konfiguracji ntfy poza WWW.');
    @chmod($tmp, 0600);
    try {
        if (fwrite($handle, $bytes) !== strlen($bytes)) throw new RuntimeException('Nie zapisano całej konfiguracji.');
    } catch (Throwable $e) { fclose($handle); @unlink($tmp); throw $e; }
    fclose($handle);
    if (!@rename($tmp, $path)) { @unlink($tmp); throw new RuntimeException('Nie zapisano ustawień ntfy.'); }
}

function pnt186_send(string $title, string $message, string $tag = 'warning'): void {
    $cfg = pnt186_config();
    if (empty($cfg['enabled'])) throw new RuntimeException('Powiadomienia ntfy są wyłączone.');
    $base = pnt186_validate_url((string)$cfg['url']);
    $topic = pnt186_validate_topic((string)$cfg['topic']);
    if (!function_exists('curl_init')) throw new RuntimeException('Hosting nie udostępnia rozszerzenia cURL.');
    $title = preg_replace('/[^\x20-\x7E]/', '', $title);
    $title = substr(trim((string)$title), 0, 100);
    $tag = preg_match('/^[a-z0-9_-]{1,24}$/D', $tag) ? $tag : 'warning';
    $message = trim($message);
    if ($message === '' || strlen($message) > 1500) throw new RuntimeException('Nieprawidłowy rozmiar komunikatu.');
    $headers = ['Content-Type: text/plain; charset=utf-8', 'Title: ' . $title, 'Tags: ' . $tag, 'Priority: 3'];
    if (!empty($cfg['token_enc'])) {
        $token = pbk_secret_decrypt((string)$cfg['token_enc']);
        if (!preg_match('/^[\x21-\x7E]{1,500}$/D', $token)) throw new RuntimeException('Niepoprawny token ntfy.');
        $headers[] = 'Authorization: Bearer ' . $token;
    }
    $ch = curl_init($base . '/' . rawurlencode($topic));
    if ($ch === false) throw new RuntimeException('Nie można uruchomić HTTP ntfy.');
    $options = [
        CURLOPT_POST => true, CURLOPT_POSTFIELDS => $message, CURLOPT_HTTPHEADER => $headers,
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_FOLLOWLOCATION => false, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT => 'POLANA-notify/18.6',
    ];
    if (defined('CURLOPT_PROTOCOLS_STR')) $options[CURLOPT_PROTOCOLS_STR] = 'https';
    elseif (defined('CURLOPT_PROTOCOLS')) $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTPS;
    curl_setopt_array($ch, $options);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $err = curl_error($ch);
    curl_close($ch);
    if ($body === false || $status < 200 || $status >= 300) {
        // Do not expose request URL, authorization header or raw provider body.
        throw new RuntimeException('Wysyłka ntfy nieudana (HTTP ' . (int)$status . ($err ? ', połączenie/TLS' : '') . ').');
    }
}

/** Returns a redacted snapshot. This does not create archives or modify reservations. */
function pnt186_backup_state(): array {
    $folder = pbk_private() . '/daily';
    $files = glob($folder . '/*.pbackup') ?: [];
    $latest = 0;
    foreach ($files as $file) {
        if (is_file($file)) $latest = max($latest, (int)(filemtime($file) ?: 0));
    }
    $age = $latest ? max(0, time() - $latest) : null;
    $valid = $latest !== 0 && $age <= 30 * 3600;
    return ['healthy' => $valid, 'count' => count($files), 'latest_ts' => $latest,
        'age_h' => $age === null ? null : round($age / 3600, 1)];
}

function pnt186_state_path(): string { return pbk_private() . '/polana_ntfy_v186_state.json'; }

/** At-most-one notification per unhealthy episode, plus one recovery message. */
function pnt186_check_backup(): array {
    $lockFile = pbk_private() . '/polana_ntfy_v186.lock';
    $lock = @fopen($lockFile, 'c+');
    if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) throw new RuntimeException('Kontrola ntfy już działa.');
    try {
        $cfg = pnt186_config();
        if (empty($cfg['enabled'])) return ['status' => 'disabled'];
        $snap = pnt186_backup_state();
        $current = $snap['healthy'] ? 'ok' : 'alert';
        $path = pnt186_state_path();
        $old = is_file($path) ? json_decode((string)file_get_contents($path), true) : [];
        if (!is_array($old)) $old = [];
        $previous = (string)($old['status'] ?? '');
        $lastAlertAt = (int)($old['last_alert_at'] ?? 0);
        // Re-alert after 24h, not every CRON run. On change to healthy send a single recovery message.
        $shouldSend = $current === 'alert' ? ($previous !== 'alert' || (time() - $lastAlertAt) >= 86400)
            : $previous === 'alert';
        if ($shouldSend) {
            if ($current === 'alert') {
                pnt186_send('POLANA backup', 'Brak aktualnej kopii SQL (maksymalny wiek 30 godzin). Sprawdź Centrum backupów w panelu Polany.', 'warning');
                $lastAlertAt = time();
            } else {
                pnt186_send('POLANA backup OK', 'Ponownie wykryto aktualną kopię SQL. Sprawdź integralność i odzyskanie w panelu.', 'white_check_mark');
            }
        }
        $state = ['status' => $current, 'last_alert_at' => $lastAlertAt, 'checked_at' => date(DATE_ATOM)];
        $tmp = $path . '.' . bin2hex(random_bytes(4)) . '.tmp';
        if (file_put_contents($tmp, json_encode($state, JSON_THROW_ON_ERROR), LOCK_EX) === false) {
            throw new RuntimeException('Nie zapisano stanu monitoringu.');
        }
        @chmod($tmp, 0600);
        if (!@rename($tmp, $path)) { @unlink($tmp); throw new RuntimeException('Błąd zapisu stanu monitoringu.'); }
        return ['status' => $current, 'sent' => $shouldSend, 'snap' => $snap];
    } finally {
        flock($lock, LOCK_UN);
        fclose($lock);
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit