| 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/admin/ |
Upload File : |
<?php
declare(strict_types=1);
/**
* POLANA V18.16 — Google Calendar private-store repair
* Admin-only, explicit changes, existing backup private location, no token reuse.
* Delete this one-off installer from /admin after successful repair.
*/
require_once dirname(__DIR__) . '/includes/functions.php';
require_admin_role();
header('Content-Type: text/html; charset=utf-8');
header('Cache-Control: no-store, private, max-age=0');
header('X-Robots-Tag: noindex, nofollow');
header('Referrer-Policy: no-referrer');
header('X-Content-Type-Options: nosniff');
function v1816_h($v): string { return htmlspecialchars((string)$v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function v1816_root(): string { return dirname(__DIR__); }
function v1816_calendar_lib(): string { return v1816_root() . '/includes/polana_gcal.php'; }
function v1816_backup_lib(): string { return v1816_root() . '/includes/polana_backup_v182.php'; }
function v1816_private(): string {
if (!is_file(v1816_backup_lib())) throw new RuntimeException('Brak istniejącej biblioteki prywatnych kopii SQL. Nie zapisuję konfiguracji w WWW.');
require_once v1816_backup_lib();
if (!function_exists('pbk_private')) throw new RuntimeException('Nie znaleziono pbk_private().');
$dir = pbk_private();
$site = realpath(v1816_root());
$real = realpath($dir);
$doc = realpath((string)($_SERVER['DOCUMENT_ROOT'] ?? ''));
if (!$real || !$site || $real === $site || str_starts_with($real . '/', $site . '/') ||
($doc && ($real === $doc || str_starts_with($real . '/', $doc . '/')))) {
throw new RuntimeException('Katalog backupu nie jest zweryfikowany jako prywatny. Nie zapisuję kluczy.');
}
if (!is_writable($real)) throw new RuntimeException('Sprawdzony katalog backupów nie jest zapisywalny.');
return $real;
}
function v1816_old_folder(): string { return dirname(__DIR__, 2) . '/.polana_gcal_private'; }
function v1816_status(): array {
$legacy = v1816_old_folder();
$private = v1816_private();
$target = $private . '/gcal-oauth';
$oldFiles = is_file($legacy . '/oauth.enc') || is_file($legacy . '/encryption.key');
$newFiles = is_file($target . '/oauth.enc') || is_file($target . '/encryption.key');
return [
'legacy_exists' => is_dir($legacy),
'legacy_any_secrets' => $oldFiles,
'new_exists' => is_dir($target),
'new_any_secrets' => $newFiles,
'new_full_pair' => is_file($target.'/oauth.enc') && is_file($target.'/encryption.key'),
'private_ok' => true,
];
}
function v1816_new_function(): string {
return <<<'EOP'
function pg173_private_dir(): string {
// V18.16: existing Google Calendar secrets always take precedence.
$legacy = dirname(__DIR__, 2) . '/.polana_gcal_private';
if (is_file($legacy . '/oauth.enc') || is_file($legacy . '/encryption.key')) return $legacy;
require_once __DIR__ . '/polana_backup_v182.php';
if (!function_exists('pbk_private')) throw new RuntimeException('Brak bezpiecznej lokalizacji danych Google Calendar.');
$base = pbk_private(); // configured private storage, already verified outside WWW
$target = $base . '/gcal-oauth';
if (is_link($target)) throw new RuntimeException('Niedozwolony link symboliczny katalogu Google Calendar.');
return $target;
}
EOP;
}
function v1816_transform(string $old): string {
if (str_contains($old, 'V18.16: existing Google Calendar secrets')) return $old;
$normalized = str_replace("\r\n", "\n", $old);
$pattern = '~function pg173_private_dir\(\): string \{\s*return dirname\(__DIR__, 2\) \. \'/\.polana_gcal_private\';\s*\}~';
$count = 0;
$new = preg_replace($pattern, v1816_new_function(), $normalized, 1, $count);
if (!is_string($new) || $count !== 1) throw new RuntimeException('Nieznana wersja polana_gcal.php. Nie nadpisuję jej automatycznie.');
if (substr_count($new, 'function pg173_private_dir()') !== 1) throw new RuntimeException('Nieprawidłowa liczba funkcji katalogu OAuth.');
return str_contains($old,"\r\n") ? str_replace("\n", "\r\n", $new) : $new;
}
function v1816_atomic_write(string $file, string $data): void {
$tmp = $file . '.v1816-' . bin2hex(random_bytes(8));
$fh = @fopen($tmp, 'xb');
if (!$fh) throw new RuntimeException('Brak prawa zapisu pliku docelowego.');
$ok = fwrite($fh, $data) === strlen($data);
fflush($fh);
fclose($fh);
if (!$ok) { @unlink($tmp); throw new RuntimeException('Niepełny zapis pliku PHP.'); }
$perms = @fileperms($file);
if ($perms !== false) @chmod($tmp, $perms & 0777);
if (!@rename($tmp, $file)) { @unlink($tmp); throw new RuntimeException('Nie udało się atomowo podmienić biblioteki kalendarza.'); }
}
function v1816_install(): string {
$status = v1816_status();
if ($status['legacy_any_secrets']) throw new RuntimeException('Istnieje stara konfiguracja OAuth. Niczego nie przenoszę ani nie zastępuję; najpierw trzeba ją odzyskać lub zweryfikować.');
$library = v1816_calendar_lib();
if (!is_file($library) || !is_readable($library)) throw new RuntimeException('Brak dostępu do biblioteki kalendarza.');
$old = file_get_contents($library);
if (!is_string($old)) throw new RuntimeException('Nie udało się przeczytać biblioteki kalendarza.');
$new = v1816_transform($old);
if ($new === $old) return 'Poprawka ścieżki jest już zainstalowana. Niczego nie nadpisano.';
$base = v1816_private() . '/gcal-v1816-code-backup';
if (is_link($base)) throw new RuntimeException('Niedozwolony symlink kopii kodu.');
if (!is_dir($base) && !@mkdir($base, 0700)) throw new RuntimeException('Nie mogę utworzyć prywatnego backupu kodu.');
$stamp = date('Ymd_His') . '_' . bin2hex(random_bytes(4));
$bak = $base . '/' . $stamp . '.php.backup';
if (@file_put_contents($bak, $old, LOCK_EX) !== strlen($old)) throw new RuntimeException('Nie udało się zachować oryginalnego kodu.');
@chmod($bak, 0600);
$manifest = ['original_sha256'=>hash('sha256',$old), 'modified_sha256'=>hash('sha256',$new), 'backup_file'=>basename($bak), 'created'=>date(DATE_ATOM)];
$meta = $base . '/' . $stamp . '.manifest.json';
if (@file_put_contents($meta, json_encode($manifest, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR), LOCK_EX) === false) throw new RuntimeException('Nie zapisano manifestu. Plik główny bez zmian.');
@chmod($meta, 0600);
v1816_atomic_write($library, $new);
if (hash_file('sha256', $library) !== hash('sha256', $new)) throw new RuntimeException('Kopia kodu zachowana, ale nowy plik nie przeszedł weryfikacji SHA-256.');
return 'Zainstalowano bezpieczną lokalizację kalendarza. Oryginalna biblioteka zabezpieczona poza WWW. Odśwież tę stronę i przejdź do importu Client ID/Secret.';
}
function v1816_import_drive_client(): string {
$status = v1816_status();
if ($status['legacy_any_secrets']) throw new RuntimeException('W starej lokalizacji istnieją pliki OAuth — nie tworzę nowej konfiguracji.');
require_once v1816_calendar_lib();
if (!function_exists('pg173_private_dir') || !str_contains((string)file_get_contents(v1816_calendar_lib()),'V18.16: existing Google Calendar secrets')) {
throw new RuntimeException('Najpierw zainstaluj poprawkę ścieżki i ponownie otwórz panel.');
}
if (realpath(dirname(pg173_private_dir())) !== realpath(v1816_private())) throw new RuntimeException('Lokalizacja kalendarza nie jest w bezpiecznym katalogu backupu.');
$existing = pg173_read_secrets();
if (!empty($existing['refresh_token'])) throw new RuntimeException('Kalendarz ma już zapisany refresh token — nie nadpisuję danych.');
if (!empty($existing['client_id']) || !empty($existing['client_secret'])) {
throw new RuntimeException('Kalendarz ma już dane Client ID/Secret — nie nadpisuję ich. Otwórz Kalendarz i kliknij „Połącz konto Google”.');
}
$cfg = pbk_cfg();
$id = trim((string)($cfg['drive_client_id'] ?? ''));
$encrypted = (string)($cfg['drive_client_secret'] ?? '');
$secret = pbk_secret_decrypt($encrypted);
if (strlen($id) < 20 || strlen($id) > 260 || !str_ends_with($id, '.apps.googleusercontent.com') || strlen($secret) < 8) {
throw new RuntimeException('Nie odnaleziono poprawnych danych klienta OAuth w konfiguracji Google Drive. Nic nie zapisano.');
}
pg173_write_secrets(['client_id'=>$id,'client_secret'=>$secret]);
return 'Przeniesiono wyłącznie Client ID i Client Secret z istniejącej konfiguracji Drive. Nie skopiowano tokenu Drive. Teraz kliknij „Połącz konto Google” w Kalendarzu.';
}
function v1816_rollback(): string {
$base = v1816_private() . '/gcal-v1816-code-backup';
$caldir = v1816_private() . '/gcal-oauth';
if (is_file($caldir.'/oauth.enc') || is_file($caldir.'/encryption.key')) {
throw new RuntimeException('Nie cofam ścieżki, ponieważ nowy magazyn ma już dane OAuth. Najpierw zabezpiecz je i przeprowadź migrację ręcznie.');
}
$files = glob($base . '/*.manifest.json') ?: [];
rsort($files, SORT_STRING);
if (!$files) throw new RuntimeException('Brak zachowanego manifestu V18.16.');
foreach ($files as $meta) {
$manifest = json_decode((string)file_get_contents($meta), true);
if (!is_array($manifest) || !isset($manifest['backup_file'], $manifest['original_sha256'], $manifest['modified_sha256'])) continue;
$backup = $base . '/' . basename((string)$manifest['backup_file']);
if (!is_file($backup) || hash_file('sha256',$backup) !== $manifest['original_sha256']) throw new RuntimeException('Kopia źródłowa jest uszkodzona.');
$lib = v1816_calendar_lib();
if (!is_file($lib) || hash_file('sha256',$lib) !== $manifest['modified_sha256']) throw new RuntimeException('Biblioteka zmieniła się po instalacji — odmowa nadpisania.');
v1816_atomic_write($lib,(string)file_get_contents($backup));
return 'Przywrócono starszą bibliotekę kalendarza. Nie zmieniono danych Google ani tabel rezerwacji.';
}
throw new RuntimeException('Nie odnaleziono poprawnego manifestu rollbacku.');
}
$message = '';
$failed = false;
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
verify_csrf();
try {
$action = (string)($_POST['action'] ?? '');
if ($action === 'install') $message = v1816_install();
elseif ($action === 'import') $message = v1816_import_drive_client();
elseif ($action === 'rollback') $message = v1816_rollback();
else throw new RuntimeException('Nieznana operacja.');
} catch (Throwable $e) { $failed = true; $message = $e->getMessage(); }
}
$status = [];
try { $status = v1816_status(); }
catch (Throwable $e) { $failed = true; $message .= ' ' . $e->getMessage(); }
$source = is_file(v1816_calendar_lib()) ? (string)@file_get_contents(v1816_calendar_lib()) : '';
$installed = str_contains($source, 'V18.16: existing Google Calendar secrets');
$hasDriveClient = false;
if (!empty($status)) {
try {
require_once v1816_backup_lib();
$cfg = pbk_cfg();
$hasDriveClient = !empty($cfg['drive_client_id']) && !empty($cfg['drive_client_secret']);
} catch (Throwable $ignored) { }
}
?>
<!doctype html><html lang="pl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>POLANA V18.16 — Google Calendar OAuth</title><style>
body{font:16px/1.6 system-ui,sans-serif;background:#f3f5f1;color:#213b2d;margin:0;padding:22px}main{max-width:920px;margin:auto;background:#fff;border:1px solid #d9e2d9;border-radius:18px;padding:25px}h1{font-size:26px}section{border-top:1px solid #e4ebe4;padding:16px 0}button,.link{padding:12px 18px;border:0;border-radius:10px;background:#176143;color:#fff;font-weight:700;cursor:pointer;text-decoration:none;display:inline-block}button.alt{background:#42544a}button:disabled{opacity:.5;cursor:not-allowed}.note{padding:13px;border-radius:9px;background:#f0f5ef}.bad{background:#fff0ed}.sub{color:#5b7063}li{margin:6px 0}small{color:#6b736b}@media(max-width:580px){body{padding:7px}main{padding:15px}}</style></head><body><main>
<h1>POLANA V18.16 — naprawa Google Calendar</h1>
<p>Bez zmian w rezerwacjach, historii Google, SMTP i backupach. Oryginał biblioteki PHP jest zachowywany w prywatnej lokalizacji przed instalacją.</p>
<?php if ($message !== ''): ?><p class="note <?= $failed ? 'bad' : '' ?>"><b><?=v1816_h($failed?'Błąd':'Wynik')?>:</b> <?=v1816_h(trim($message))?></p><?php endif; ?>
<section><h2>1. Stan bezpiecznych lokalizacji</h2>
<p>Katalog backupu poza WWW: <strong><?=!empty($status['private_ok'])?'TAK':'NIE / BŁĄD'?></strong></p>
<p>Dawny magazyn Calendar z plikami OAuth: <strong><?=!empty($status['legacy_any_secrets'])?'TAK':'NIE'?></strong></p>
<p>Nowy wydzielony magazyn Calendar: <strong><?=!empty($status['new_exists'])?'ISTNIEJE':'JESZCZE NIE'?></strong></p>
<p>Poprawiona biblioteka kalendarza: <strong><?=$installed?'TAK':'NIE'?></strong></p>
</section>
<section><h2>2. Zabezpiecz i popraw ścieżkę</h2><p>Ten krok nie generuje nowego tokenu ani nowego klucza Google. Zachowuje kopię oryginalnego kodu poza WWW.</p>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="install"><button type="submit" <?=empty($status['private_ok'])||!empty($status['legacy_any_secrets'])?'disabled':''?>>Backup kodu i napraw katalog OAuth</button></form>
</section>
<section><h2>3. Użyj istniejących danych klienta Google Drive</h2>
<p>Importuje tylko Client ID i Client Secret z backupów. Nie przenosi refresh tokenu Drive, nie usuwa wcześniejszych wydarzeń i nie dotyka SQL. Jeśli odzyskujesz starą parę <code>oauth.enc</code> i <code>encryption.key</code>, najpierw zajmij się jej przywróceniem — bez tworzenia nowego klucza.</p>
<p>Dane klienta Google w backupach: <strong><?=$hasDriveClient?'WYKRYTO':'BRAK / NIEDOSTĘPNE'?></strong></p>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="import"><button type="submit" <?=(!$installed||!$hasDriveClient||!empty($status['legacy_any_secrets']))?'disabled':''?>>Przenieś Client ID i Secret z Drive</button></form>
<p><a class="link" href="/admin/polana-kalendarz.php">Otwórz Calendar i kliknij „Połącz konto Google”</a></p>
</section>
<section><h2>4. Cofnięcie wyłącznie kodu</h2><p>Rollback jest blokowany, gdy nowa lokalizacja ma już dane OAuth albo biblioteka zmieniła się po instalacji.</p>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="rollback"><button type="submit" class="alt">Cofnij zmianę biblioteki</button></form>
</section>
<p class="sub">Po zakończonym połączeniu i teście jednej rezerwacji usuń ten jednorazowy plik z serwera. Nie wysyłaj do nikogo Client Secret, refresh tokenów ani kluczy szyfrowania.</p>
</main></body></html>