| 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);
require_once dirname(__DIR__).'/includes/functions.php';
require_admin_role();
require_once dirname(__DIR__).'/includes/polana_indexnow.php';
header('Cache-Control: private, no-store');
$root=dirname(__DIR__);
$backupDir=dirname(dirname($root)).'/.polana_v177_backups';
$messages=[]; $errors=[];
function pin77_patch_file(string $relative, string $source): array {
if (str_contains($source, 'PIN77_AUTO_BLOG_SAVE') && $relative==='admin/blog-edit.php') return [$source,false];
if (str_contains($source, 'PIN77_AUTO_BLOG_DELETE') && $relative==='admin/blog.php') return [$source,false];
if (str_contains($source, 'PIN77_AUTO_MENU') && $relative==='admin/menu.php') return [$source,false];
if (str_contains($source, 'PIN77_SEO_LINK') && in_array($relative,['admin/seo.php','admin/polana-seo-centrum.php','admin/polana-zarzadzanie.php'],true)) return [$source,false];
$include = "require_once dirname(__DIR__) . '/includes/polana_indexnow.php'; ";
if ($relative==='admin/blog-edit.php') {
$needle=" redirect('/admin/blog-edit.php?id='.\$id);";
if (substr_count($source,$needle)!==1) throw new RuntimeException('Brak bezpiecznego wzorca zapisu bloga; pominięto automatyzację tego pliku.');
$hook=" /* PIN77_AUTO_BLOG_SAVE */\n ".$include."pin77_after_blog_save(\$post, \$id);\n";
return [str_replace($needle,$hook.$needle,$source),true];
}
if ($relative==='admin/blog.php') {
$needle=" db()->prepare('DELETE FROM blog_posts WHERE id=?')->execute([\$id]);";
if (substr_count($source,$needle)!==1) throw new RuntimeException('Brak bezpiecznego wzorca usuwania bloga; pominięto automatyzację tego pliku.');
$hook="\n /* PIN77_AUTO_BLOG_DELETE */\n ".$include."pin77_after_blog_delete(\$post);";
return [str_replace($needle,$needle.$hook,$source),true];
}
if ($relative==='admin/menu.php') {
$count=0;
$result=preg_replace_callback("~(admin_log\('menu_(?:update|publish|draft|archive)'\s*,[^;]+;)~",function($m)use(&$count,$include){
$count++; return $m[1]."\n /* PIN77_AUTO_MENU */\n ".$include."pin77_after_menu_change();";
},$source);
if ($result===null || $count!==4) throw new RuntimeException('Zmodyfikowany edytor menu — pominięto hooki (oczekiwano 4, znaleziono '.$count.').');
return [$result,true];
}
if (in_array($relative,['admin/seo.php','admin/polana-seo-centrum.php','admin/polana-zarzadzanie.php'],true)) {
$needle='<div class="admin-title-row">';
if (substr_count($source,$needle)<1) throw new RuntimeException('Nie znaleziono bezpiecznego miejsca na link SEO.');
$html='<!-- PIN77_SEO_LINK --><div class="form-card" style="margin:12px 0;padding:12px 18px"><strong>IndexNow / Bing:</strong> <a href="/admin/polana-indexnow.php">Kolejka zgłoszeń, mapa witryny i automatyzacja →</a></div>';
return [preg_replace('~'.preg_quote($needle,'~').'~',$html."\n".$needle,$source,1),true];
}
return [$source,false];
}
function pin77_make_schema(): void {
db()->exec("CREATE TABLE IF NOT EXISTS polana_indexnow_queue (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
url_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL UNIQUE,
url TEXT NOT NULL,
status VARCHAR(12) NOT NULL DEFAULT 'pending',
attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0,
last_http SMALLINT UNSIGNED NULL,
last_error VARCHAR(255) NULL,
next_attempt DATETIME NULL,
last_attempt DATETIME NULL,
sent_at DATETIME NULL,
updated_at DATETIME NOT NULL,
KEY pin77_due (status,next_attempt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
db()->exec("CREATE TABLE IF NOT EXISTS polana_indexnow_log (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
url_count INT UNSIGNED NOT NULL,
http_code SMALLINT UNSIGNED NULL,
result VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL,
KEY pin77_date (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
}
function pin77_install(string $root,string $backupDir): array {
$messages=[];
if (!is_dir($backupDir) && !@mkdir($backupDir,0700,true)) throw new RuntimeException('Nie można utworzyć bezpiecznego katalogu backupu poza WWW.');
if (!is_writable($backupDir)) throw new RuntimeException('Brak zapisu do katalogu backupu.');
$targets=['admin/blog-edit.php','admin/blog.php','admin/menu.php','admin/seo.php','admin/polana-seo-centrum.php','admin/polana-zarzadzanie.php'];
$patches=[]; $skips=[];
foreach ($targets as $relative) {
$path=$root.'/'.$relative;
if (!is_file($path)) { $skips[]=$relative.' — brak pliku'; continue; }
$source=@file_get_contents($path);
if ($source===false) { $skips[]=$relative.' — brak odczytu'; continue; }
try { [$result,$changed]=pin77_patch_file($relative,$source); }
catch (Throwable $e) { $skips[]=$relative.' — '.$e->getMessage(); continue; }
if (!$changed) { $skips[]=$relative.' — już zainstalowany'; continue; }
if (!is_writable($path)) { $skips[]=$relative.' — brak zapisu'; continue; }
$patches[$relative]=['original'=>$source,'patched'=>$result];
}
// Check PHP syntax before writing ANY modified original file.
foreach ($patches as $relative=>$item) {
if (!str_starts_with($item['patched'],'<?php')) throw new RuntimeException('Niepoprawny format pliku '.$relative);
}
pin77_make_schema();
$key=pin77_key();
if ($key==='') {
$key=bin2hex(random_bytes(16));
}
if (!preg_match('/^[A-Za-z0-9-]{8,128}$/D',$key)) throw new RuntimeException('Nieprawidłowy zapisany klucz IndexNow.');
$keyFile=$root.'/'.$key.'.txt';
if (is_file($keyFile)) {
if (trim((string)@file_get_contents($keyFile))!==$key) throw new RuntimeException('Plik klucza już istnieje, ale ma inną zawartość.');
} else {
$fp=@fopen($keyFile,'x');
if (!$fp) throw new RuntimeException('Nie mogę utworzyć pliku klucza w katalogu strony. Sprawdź uprawnienia home.pl.');
fwrite($fp,$key); fclose($fp); @chmod($keyFile,0644);
}
db()->prepare("INSERT INTO settings (setting_key,setting_value) VALUES ('polana_indexnow_key',?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)")->execute([$key]);
// Enabled explicitly by administrator after key file is publicly verified.
if (setting('polana_indexnow_enabled','')==='') {
db()->prepare("INSERT IGNORE INTO settings (setting_key,setting_value) VALUES ('polana_indexnow_enabled','0')")->execute();
}
if ($patches) {
$dir=$backupDir.'/'.date('Ymd_His').'_'.bin2hex(random_bytes(3));
if (!@mkdir($dir,0700)) throw new RuntimeException('Nie można utworzyć folderu backupu.');
$manifest=[];
foreach ($patches as $relative=>$item) {
$backupName=str_replace('/','__',$relative);
if (file_put_contents($dir.'/'.$backupName,$item['original'],LOCK_EX)===false) throw new RuntimeException('Błąd zapisu backupu '.$relative);
$manifest[]=['file'=>$relative,'backup'=>$backupName,'original_hash'=>hash('sha256',$item['original']),
'patched_hash'=>hash('sha256',$item['patched'])];
}
if (file_put_contents($dir.'/manifest.json',json_encode($manifest,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES),LOCK_EX)===false)
throw new RuntimeException('Nie udało się zapisać manifestu backupu.');
$written=[];
try {
foreach ($patches as $relative=>$item) {
$path=$root.'/'.$relative;
$tmp=tempnam(dirname($path),'.pin77_');
if ($tmp===false) throw new RuntimeException('Brak możliwości utworzenia pliku tymczasowego: '.$relative);
if (file_put_contents($tmp,$item['patched'],LOCK_EX)===false) { @unlink($tmp); throw new RuntimeException('Błąd zapisu: '.$relative); }
@chmod($tmp,fileperms($path)&0777);
if (!@rename($tmp,$path)) { @unlink($tmp); throw new RuntimeException('Błąd podmiany: '.$relative); }
$written[]=$relative;
}
} catch (Throwable $e) {
foreach ($written as $relative) @file_put_contents($root.'/'.$relative,$patches[$relative]['original'],LOCK_EX);
throw $e;
}
$messages[]='Zmodyfikowano pliki: '.implode(', ',array_keys($patches));
$messages[]='Backup: zapisano poza katalogiem WWW.';
} else $messages[]='Nie trzeba podmieniać plików — instalacja była już wykonana albo nie znaleziono zgodnych wzorców.';
$messages[]='Klucz IndexNow utworzony/sprawdzony: '.$key.'.txt';
foreach ($skips as $line) $messages[]='Pominięto: '.$line;
return $messages;
}
function pin77_rollback(string $root,string $backupDir): array {
$dirs=glob($backupDir.'/*/manifest.json') ?: [];
rsort($dirs);
foreach ($dirs as $manifestFile) {
if (is_file(dirname($manifestFile).'/.rolledback')) continue;
$rows=json_decode((string)file_get_contents($manifestFile),true);
if (!is_array($rows)) continue;
foreach ($rows as $row) {
$file=$root.'/'.$row['file'];
if (!is_file($file) || hash_file('sha256',$file)!==$row['patched_hash'])
throw new RuntimeException('Rollback przerwany: plik '.$row['file'].' zmienił się po instalacji. Nie nadpisuję cudzych zmian.');
$backup=dirname($manifestFile).'/'.$row['backup'];
if (!is_file($backup) || hash_file('sha256',$backup)!==$row['original_hash'])
throw new RuntimeException('Backup jest niekompletny: '.$row['file']);
}
foreach ($rows as $row) {
$file=$root.'/'.$row['file']; $backup=dirname($manifestFile).'/'.$row['backup'];
if (file_put_contents($file,file_get_contents($backup),LOCK_EX)===false)
throw new RuntimeException('Nie można przywrócić '.$row['file']);
}
file_put_contents(dirname($manifestFile).'/.rolledback',date('c'));
return ['Przywrócono pliki z ostatniego backupu.','Baza, klucz i historia zgłoszeń pozostają bez zmian.'];
}
throw new RuntimeException('Brak backupu do przywrócenia.');
}
if ($_SERVER['REQUEST_METHOD']==='POST') {
verify_csrf();
try {
$action=(string)($_POST['action']??'');
if ($action==='install') $messages=pin77_install($root,$backupDir);
elseif ($action==='rollback' && ($_POST['confirm']??'')==='yes') $messages=pin77_rollback($root,$backupDir);
else throw new RuntimeException('Niepoprawna operacja.');
} catch (Throwable $e) { $errors[]=$e->getMessage(); }
}
$ready=pin77_tables_exist();
$hasKey=pin77_key()!=='' && is_file($root.'/'.pin77_key().'.txt');
render_admin_header('Instalator IndexNow V17.7');
?>
<div class="admin-title-row"><div><h1>Instalator IndexNow V17.7</h1><p>Bezpieczne podłączenie zgłaszania URL-i do Bing i innych wyszukiwarek IndexNow. Nie zmienia rezerwacji, SMTP, Google Kalendarza ani GTM.</p></div><a class="btn btn-light" href="/admin/polana-indexnow.php">Przejdź do IndexNow</a></div>
<?php foreach($errors as $msg): ?><div class="flash flash-error"><?= e($msg) ?></div><?php endforeach; ?>
<?php foreach($messages as $msg): ?><div class="flash flash-success"><?= e($msg) ?></div><?php endforeach; ?>
<div class="form-card"><h2>Stan</h2><p>Baza i kolejka: <strong><?= $ready?'gotowe':'do instalacji' ?></strong>. Klucz domeny: <strong><?= $hasKey?'obecny':'do utworzenia' ?></strong>.</p>
<p>Instalator tworzy dwie tabele kolejki/logów, plik klucza w katalogu strony i dodaje bezpieczne hooki do edycji bloga oraz menu — wyłącznie gdy rozpoznaje kod. Nieznane wersje plików pozostają nietknięte.</p>
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="install"><button class="btn btn-primary">Zainstaluj / sprawdź V17.7 z backupem</button></form></div>
<div class="form-card" style="margin-top:18px"><h2>Rollback</h2><p>Cofa tylko pliki zmienione przez instalator. Nie usuwa tabel, klucza ani historii. Odmawia przywrócenia, jeśli pliki zostały później zmodyfikowane.</p>
<form method="post" onsubmit="return confirm('Cofnąć hooki IndexNow w plikach PHP?')"><?= csrf_field() ?><input type="hidden" name="action" value="rollback"><label><input type="checkbox" name="confirm" value="yes" required> Potwierdzam cofnięcie hooków</label><p><button class="btn btn-light">Przywróć pliki</button></p></form></div>
<?php render_admin_footer(); ?>