| 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.0.1 — bezpieczny instalator; różnice CRLF/LF nie są zmianą kodu. */
require_once dirname(__DIR__).'/includes/functions.php';
require_admin_role();
header('Cache-Control: private, no-store');
header('X-Robots-Tag: noindex, nofollow');
if (!function_exists('gzdecode')) throw new RuntimeException('Brak PHP zlib/gzdecode. Instalacja zablokowana.');
define('PV1801_BUNDLE_AUTHORIZED', true);
$bundle=require __DIR__.'/polana-v1801-bundle.php';
$manifest=$bundle['manifest']??[];
$payloads=$bundle['payloads']??[];
$root=dirname(__DIR__);
$backupRoot=dirname($root,2).'/.polana_v1801_rollback';
$backupManifest=$backupRoot.'/manifest.json';
$ok='';$error='';$notes=[];
function pv1801_normalize(string $s):string { return str_replace("\r\n","\n",$s); }
function pv1801_canonical_sha(string $s):string { return hash('sha256',pv1801_normalize($s)); }
function pv1801_payload(string $rel,array $payloads,array $meta):string {
if (!isset($payloads[$rel]) || !is_string($payloads[$rel])) throw new RuntimeException('Brak payloadu: '.$rel);
$packed=base64_decode($payloads[$rel],true);
if ($packed===false) throw new RuntimeException('Błędne kodowanie payloadu: '.$rel);
$bytes=gzdecode($packed);
if ($bytes===false || pv1801_canonical_sha($bytes)!==($meta['new_lf']??''))
throw new RuntimeException('Uszkodzony payload: '.$rel);
return $bytes;
}
function pv1801_preflight(string $root,array $manifest,array $payloads,bool $rollback=false):array {
$errors=[];$checks=[];
if(!is_file($root.'/includes/polana_v18_menu.php'))$errors[]='Brak includes/polana_v18_menu.php.';
if(!is_file($root.'/admin/polana-v18-diagnostyka.php'))$errors[]='Brak diagnostyki V18.';
if(!is_file($root.'/includes/polana_v166_seo.php'))$errors[]='Brak generatora sitemap V16.6.';
$allow=['admin/menu.php','admin/menus.php','admin/polana-menu-centrum.php','admin/polana-indexnow.php',
'includes/polana_indexnow.php','admin/polana-v164-seo.php','admin/blog.php','admin/users.php',
'admin/opinie.php','.htaccess','includes/polana_v167_audit.php','admin/tips-photo-file.php',
'includes/functions.php','admin/polana-poczta.php'];
if(count($manifest)!==count($allow))$errors[]='Nieprawidłowa liczba plików w paczce.';
foreach($manifest as $rel=>$meta) {
if(!is_string($rel)||!in_array($rel,$allow,true)||!is_array($meta)){
$errors[]='Niedozwolony plik w paczce.';continue;
}
try{pv1801_payload($rel,$payloads,$meta);}catch(Throwable $e){$errors[]=$e->getMessage();continue;}
$target=$root.'/'.$rel;
$expected=$rollback?($meta['new_lf']??null):($meta['orig_lf']??null);
$exists=is_file($target);
if ($expected===null && !$exists) {
$checks[]=$rel.' — nowy plik';
} elseif ($expected!==null && $exists) {
$content=@file_get_contents($target);
if($content===false){$errors[]='Nie można odczytać: '.$rel;continue;}
if(pv1801_canonical_sha($content)!==$expected){
$errors[]='RZECZYWISTA RÓŻNICA KODU: '.$rel.' — nie nadpisuję. Potrzebna aktualna kopia pliku z hostingu.';
continue;
}
$original=$rollback?($meta['new']??null):($meta['orig']??null);
$sameRaw=$original!==null && hash('sha256',$content)===$original;
$checks[]=$rel.' — '.($sameRaw?'zgodny 1:1':'zgodny po normalizacji CRLF/LF');
} else {
$errors[]='Brak pliku lub nieoczekiwany plik: '.$rel.' — nie nadpisuję.';continue;
}
if(!is_writable(dirname($target)))$errors[]='Brak zapisu w katalogu: '.$rel;
}
return [$errors,$checks];
}
function pv1801_write_atomic(string $target,string $content):void {
$dir=dirname($target);$tmp=tempnam($dir,'.pv1801-');
if($tmp===false)throw new RuntimeException('Nie można utworzyć pliku tymczasowego.');
try{
if(file_put_contents($tmp,$content,LOCK_EX)!==strlen($content))throw new RuntimeException('Niepełny zapis: '.basename($target));
$mode=is_file($target)?(fileperms($target)&0777):0644;
@chmod($tmp,$mode);
if(!rename($tmp,$target))throw new RuntimeException('Nie można podmienić: '.basename($target));
if(function_exists('opcache_invalidate'))@opcache_invalidate($target,true);
}finally{if(is_file($tmp))@unlink($tmp);}
}
function pv1801_backup(string $root,string $backupRoot,array $manifest):void {
if(is_file($backupRoot.'/manifest.json'))throw new RuntimeException('Backup V18.0.1 już istnieje — nie instaluję ponownie.');
if(!is_dir($backupRoot)&&!@mkdir($backupRoot,0700,true))throw new RuntimeException('Brak zapisu backupu poza WWW.');
@chmod($backupRoot,0700);
if(!is_writable($backupRoot))throw new RuntimeException('Brak uprawnień do backupu.');
@file_put_contents($backupRoot.'/.htaccess',"Require all denied\nDeny from all\n",LOCK_EX);
$hashes=[];
foreach($manifest as $rel=>$meta){
if(($meta['orig_lf']??null)===null)continue;
$source=$root.'/'.$rel;
$bytes=@file_get_contents($source);
if($bytes===false||pv1801_canonical_sha($bytes)!==$meta['orig_lf'])throw new RuntimeException('Błąd backupu: '.$rel);
$backup=$backupRoot.'/'.str_replace('/','__',$rel).'.backup';
if(file_put_contents($backup,$bytes,LOCK_EX)!==strlen($bytes))throw new RuntimeException('Błąd zapisu kopii: '.$rel);
@chmod($backup,0600);
$hashes[$rel]=hash('sha256',$bytes); // dokładne bajty pliku z hostingu, bez zmiany CRLF/LF
}
$record=['version'=>'18.0.1','created_at'=>date(DATE_ATOM),'files'=>$manifest,'backup_sha256'=>$hashes];
$encoded=json_encode($record,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE);
if(file_put_contents($backupRoot.'/manifest.json',$encoded,LOCK_EX)!==strlen($encoded))throw new RuntimeException('Nie zapisano manifestu backupu.');
@chmod($backupRoot.'/manifest.json',0600);
}
function pv1801_install(string $root,string $backupRoot,array $manifest,array $payloads):void {
[$errors]=pv1801_preflight($root,$manifest,$payloads,false);
if($errors)throw new RuntimeException(implode("\n",$errors));
require_once $root.'/includes/polana_v18_menu.php';
pv18_menu_schema(); // tylko CREATE TABLE IF NOT EXISTS, bez kasowania danych
pv1801_backup($root,$backupRoot,$manifest);
$done=[];
try{
foreach($manifest as $rel=>$meta){
$content=pv1801_payload($rel,$payloads,$meta);
pv1801_write_atomic($root.'/'.$rel,$content);
$done[]=$rel;
}
}catch(Throwable $e){
foreach(array_reverse($done) as $rel){
$target=$root.'/'.$rel;
if(($manifest[$rel]['orig_lf']??null)===null){@unlink($target);continue;}
$backup=$backupRoot.'/'.str_replace('/','__',$rel).'.backup';
if(is_file($backup))pv1801_write_atomic($target,(string)file_get_contents($backup));
}
@unlink($backupRoot.'/manifest.json');
throw new RuntimeException('Instalacja przerwana; przywrócono poprzednie pliki. '.$e->getMessage());
}
admin_log('polana_v1801_install','V18.0.1: '.count($manifest).' plików, backup poza WWW');
}
function pv1801_rollback(string $root,string $backupRoot,array $manifest,array $payloads):void {
$file=$backupRoot.'/manifest.json';
if(!is_file($file))throw new RuntimeException('Brak kopii do rollbacku.');
$record=json_decode((string)file_get_contents($file),true,512,JSON_THROW_ON_ERROR);
if(($record['version']??'')!=='18.0.1'||!is_array($record['files']??null)||$record['files']!==$manifest)
throw new RuntimeException('Nieprawidłowy manifest kopii.');
[$errors]=pv1801_preflight($root,$manifest,$payloads,true);
if($errors)throw new RuntimeException(implode("\n",$errors));
foreach($manifest as $rel=>$meta){
if(($meta['orig_lf']??null)===null)continue;
$backup=$backupRoot.'/'.str_replace('/','__',$rel).'.backup';
if(!is_file($backup)||hash_file('sha256',$backup)!==($record['backup_sha256'][$rel]??null))
throw new RuntimeException('Kopia uszkodzona: '.$rel);
}
foreach($manifest as $rel=>$meta){
$target=$root.'/'.$rel;
if(($meta['orig_lf']??null)===null){if(!@unlink($target))throw new RuntimeException('Nie usunięto nowego pliku: '.$rel);}
else pv1801_write_atomic($target,(string)file_get_contents($backupRoot.'/'.str_replace('/','__',$rel).'.backup'));
}
@unlink($file);
admin_log('polana_v1801_rollback','Rollback V18.0.1 — przywrócono oryginalne pliki');
}
$installed=is_file($backupManifest);
try {
if(!is_array($manifest)||count($manifest)!==14||!is_array($payloads)||count($payloads)!==14)
throw new RuntimeException('Niekompletna paczka V18.0.1.');
if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'){
verify_csrf();
$action=(string)($_POST['action']??'');
if($action==='install'){
if(empty($_POST['confirm']))throw new RuntimeException('Potwierdź wykonanie backupu.');
pv1801_install($root,$backupRoot,$manifest,$payloads);
$ok='Zainstalowano V18.0.1. Sprawdź diagnostykę i menu.';
}elseif($action==='rollback'){
if(empty($_POST['confirm_rollback']))throw new RuntimeException('Potwierdź rollback.');
pv1801_rollback($root,$backupRoot,$manifest,$payloads);
$ok='Przywrócono poprzednie pliki. Nowa tabela kosza pozostaje bez zmian.';
}else throw new RuntimeException('Nieznana operacja.');
}
$installed=is_file($backupManifest);
[$problems,$notes]=pv1801_preflight($root,$manifest,$payloads,$installed);
if($problems && $error==='')$error=implode("\n",$problems);
}catch(Throwable $e){$error=$e->getMessage();}
$legacyPayload=is_dir(__DIR__.'/polana-v18-payload');
render_admin_header('POLANA V18.0.1 — instalator naprawczy');
?>
<style>.pv1801{max-width:1000px;margin:0 auto 60px;padding:18px}.pv1801 .panel{background:#fff;border:1px solid #d8e3d8;border-radius:14px;padding:20px;margin:16px 0}.pv1801 .error{background:#fff1ed;border:1px solid #e6c4ba;border-radius:8px;padding:12px;white-space:pre-wrap;overflow-wrap:anywhere}.pv1801 .ok{background:#e7f7e9;padding:12px;border-radius:8px}.pv1801 .btn{background:#23563b;color:#fff;border:0;border-radius:9px;padding:12px 16px;font:inherit;cursor:pointer}.pv1801 .btn:disabled{opacity:.4;cursor:not-allowed}.pv1801 label{display:flex;gap:10px;align-items:center;margin:12px 0}.pv1801 li{margin:6px 0}</style>
<div class="pv1801"><h1>POLANA V18.0.1 — poprawka instalatora</h1>
<p>Naprawiono fałszywe konflikty SHA256 wynikające wyłącznie z CRLF/LF oraz problem z ukrytym plikiem <code>.htaccess.payload</code>. Kod nadal musi być identyczny po normalizacji końców wierszy. Nie ma opcji wymuszenia nadpisania.</p>
<?php if($ok):?><div class="ok"><?=e($ok)?></div><?php endif;?>
<?php if($error):?><div class="error"><?=e($error)?></div><?php endif;?>
<div class="panel"><h2><?=$installed?'V18.0.1 zainstalowana':'Stan przed instalacją'?></h2>
<p>Sprawdzono <?=count($notes)?> z <?=count($manifest)?> plików.</p><ul><?php foreach($notes as $note):?><li><?=e($note)?></li><?php endforeach;?></ul>
</div>
<div class="panel"><h2>Instalacja z kopią bezpieczeństwa</h2>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="install">
<label><input type="checkbox" name="confirm" value="1" required> Mam aktualny backup bazy i plików.</label>
<button class="btn" <?=($installed||$error)?'disabled':''?>>Zainstaluj V18.0.1</button></form></div>
<?php if($installed):?><div class="panel"><h2>Rollback plików</h2><form method="post" onsubmit="return confirm('Przywrócić poprzednie pliki?')"><?=csrf_field()?><input type="hidden" name="action" value="rollback"><label><input type="checkbox" name="confirm_rollback" value="1" required> Przywracam pliki z kopii.</label><button class="btn">Rollback</button></form></div><?php endif;?>
<?php if($legacyPayload):?><div class="panel"><strong>Bezpieczeństwo:</strong> po poprawnej instalacji usuń przez FileZilla cały katalog <code>/admin/polana-v18-payload/</code> z poprzedniej paczki. Nowy instalator nie korzysta z tego katalogu, a zawiera on pliki źródłowe.</div><?php endif;?>
<div class="panel"><a href="/admin/polana-v18-diagnostyka.php">Diagnostyka V18</a> · <a href="/admin/menus.php">Menu</a> · <a href="/admin/polana-indexnow.php">IndexNow</a></div>
</div><?php render_admin_footer(); ?>