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_v173_v172_install_lib.php
<?php
declare(strict_types=1);
/** POLANA V17.2: safe, limited-scope patcher of the existing PHP app. */
require_once __DIR__ . '/functions.php';
require_once __DIR__ . '/polana_v172_newsletter.php';
require_admin_role();
header('Cache-Control: private, no-store, max-age=0');
header('X-Robots-Tag: noindex,nofollow');

function pi172_root():string { return dirname(__DIR__); }
function pi172_backupdir():string { return dirname(pi172_root()).'/.polana_v172_private_backups'; }
function pi172_marker():string { return 'POLANA_V172_NEWSLETTER_ADAPTER'; }
function pi172_targets():array {
    $root=pi172_root();
    return [
        'includes/newsletter.php'=>$root.'/includes/newsletter.php',
        'newsletter.php'=>$root.'/newsletter.php',
        'rezerwacja.php'=>$root.'/rezerwacja.php',
        'includes/reservations.php'=>$root.'/includes/reservations.php',
        'includes/functions.php'=>$root.'/includes/functions.php',
    ];
}
function pi172_one(string $contents,string $needle,string $replacement,string $label):string {
    if (strpos($contents,$needle)===false) throw new RuntimeException('Nie znaleziono wzorca: '.$label.'. Instalacja przerwana, bez nadpisywania nieznanego kodu.');
    if (substr_count($contents,$needle)!==1) throw new RuntimeException('Wzorzec niejednoznaczny: '.$label);
    return str_replace($needle,$replacement,$contents);
}
function pi172_patch(string $relative,string $content):string {
    if ($relative==='includes/newsletter.php') {
        if (str_contains($content,pi172_marker())) return $content;
        $regex='~function newsletter_subscribe\(.*?\): array\s*\{.*?\n\}\s*(?=function newsletter_unsubscribe\()~s';
        if(preg_match_all($regex,$content,$m)!==1) throw new RuntimeException('Niezgodna struktura funkcji newsletter_subscribe.');
        $replacement = <<<'REPLACE'
/** POLANA_V172_NEWSLETTER_ADAPTER: double opt-in instead of automatic activation */
function newsletter_subscribe(string $email, string $name='', string $source='footer'): array {
    require_once __DIR__ . '/polana_v172_newsletter.php';
    return pv172_subscribe($email, $name, $source);
}

REPLACE;
        return preg_replace_callback($regex,static fn($match)=>$replacement,$content,1) ?? throw new RuntimeException('Nie udało się dostosować newslettera.');
    }
    if ($relative==='newsletter.php') {
        if(str_contains($content,'POLANA_V172_CONFIRM_FLOW')) return $content;
        $content=pi172_one($content,"require_once __DIR__ . '/includes/newsletter.php';", "require_once __DIR__ . '/includes/newsletter.php';\nrequire_once __DIR__ . '/includes/polana_v172_newsletter.php';\n/* POLANA_V172_CONFIRM_FLOW */\nheader('Cache-Control: private, no-store');\nheader('Referrer-Policy: no-referrer');\nheader('X-Robots-Tag: noindex,nofollow');
start_session();",'newsletter public include');
        $branch=<<<'BRANCH'
if (isset($_GET['confirm'])) {
    $mode = 'confirm';
    $confirmToken = trim((string)$_GET['confirm']);
    $message = preg_match('/^[a-f0-9]{64}$/D', $confirmToken)
        ? 'Kliknij przycisk poniżej, aby potwierdzić zapis. Samo otwarcie linku nie aktywuje adresu.'
        : 'Nieprawidłowy link potwierdzenia.';
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['newsletter_confirm_token'])) {
    $mode = 'confirm';
    verify_csrf();
    try {
        $ok = pv172_confirm(trim((string)$_POST['newsletter_confirm_token']));
        $message = $ok ? 'Adres potwierdzony. Dziękujemy za zapis!' : 'Link już wykorzystany, wygasły lub nieprawidłowy.';
    } catch (Throwable $ex) {
        $message = 'Nie udało się potwierdzić adresu. Spróbuj ponownie.';
        error_log('[POLANA NEWSLETTER confirm] '.$ex->getMessage());
    }
} elseif (isset($_GET['unsubscribe']))
BRANCH;
        $content=pi172_one($content,"if (isset(\$_GET['unsubscribe']))",$branch,'newsletter confirmation routing');
        $content=pi172_one($content,"<h1><?= \$mode === 'unsubscribe' ? 'Newsletter' : 'Dołącz do newslettera' ?></h1>","<h1><?= \$mode === 'unsubscribe' ? 'Newsletter' : (\$mode === 'confirm' ? 'Potwierdź zapis' : 'Dołącz do newslettera') ?></h1>",'newsletter confirmation title');
        $form=<<<'FORM'
<?php if (!$ok && $mode === 'confirm' && !empty($confirmToken) && preg_match('/^[a-f0-9]{64}$/D', $confirmToken)): ?>
      <form method="post" action="/newsletter.php" class="newsletter-page__form">
        <?= csrf_field() ?>
        <input type="hidden" name="newsletter_confirm_token" value="<?= e($confirmToken) ?>">
        <button type="submit">Potwierdzam zapis na newsletter</button>
      </form>
    <?php elseif (!$ok && $mode !== 'unsubscribe' && $mode !== 'confirm'): ?>
FORM;
        $content=pi172_one($content,"<?php if (!\$ok && \$mode !== 'unsubscribe'): ?>",$form,'newsletter form confirmation');
        return $content;
    }
    if ($relative==='rezerwacja.php') {
        // FIX V17.3.1: nie wymagamy identycznego komentarza/formatowania starszej wstawki.
        // Usuwamy tylko znane, przedwczesne wywołanie przed zapisem rezerwacji.
        if (str_contains($content, 'POLANA_V172_RESERVATION_OPTIN_AFTER_SAVE')) return $content;

        $createNeedle = '$reservation = reservation_create(';
        if (substr_count($content, $createNeedle) !== 1) {
            throw new RuntimeException('Rezerwacja: niejednoznaczny zapis reservation_create — nic nie zmieniono.');
        }
        $createAt = strpos($content, $createNeedle);
        $sendAt = strpos($content, 'reservation_send_after_create(', $createAt);
        if ($sendAt === false || $sendAt <= $createAt) {
            throw new RuntimeException('Rezerwacja: brak bezpiecznego punktu po zapisaniu rezerwacji — nic nie zmieniono.');
        }

        $prefix = substr($content, 0, $createAt);
        // Obsługiwane są pojedyncze wywołania w wariancie 1-liniowym lub blokowym,
        // z komentarzem V1.1 lub bez. Nigdy nie usuwamy dowolnego nieznanego kodu.
        $earlyCall = '~^[ \\t]*(?:/\\*\\s*POLANA_NEWSLETTER_RESERVATION_POST_V1_1\\s*\\*/[ \\t]*\\n)?[ \\t]*if\\s*\\(\\s*function_exists\\s*\\(\\s*[\\\'\"]newsletter_handle_reservation_optin[\\\'\"]\\s*\\)\\s*\\)\\s*(?:\\{[ \\t]*\\n[ \\t]*)?newsletter_handle_reservation_optin\\s*\\(\\s*\\)\\s*;[ \\t]*(?:\\n[ \\t]*\\})?[ \\t]*(?:\\n|$)~m';
        $callsInPrefix = preg_match_all('~\\bnewsletter_handle_reservation_optin\\s*\\(\\s*\\)\\s*;~', $prefix);
        if ($callsInPrefix === false) throw new RuntimeException('Błąd analizy newslettera.');
        if ($callsInPrefix > 1) throw new RuntimeException('Wiele przedwczesnych wywołań newslettera — wymagana kontrola kodu.');
        if ($callsInPrefix === 1) {
            $matches = [];
            if (preg_match_all($earlyCall, $prefix, $matches) !== 1) {
                throw new RuntimeException('Nieznany wariant wczesnego zapisu newslettera — bez zmian.');
            }
            $prefix = preg_replace($earlyCall, '', $prefix, 1, $removed);
            if (!is_string($prefix) || $removed !== 1) throw new RuntimeException('Nie można bezpiecznie usunąć starego wywołania newslettera.');
            $content = $prefix . substr($content, $createAt);
            $createAt = strpos($content, $createNeedle);
            $sendAt = strpos($content, 'reservation_send_after_create(', $createAt);
        }

        $successBlock = substr($content, $createAt, $sendAt - $createAt);
        $successCalls = preg_match_all('~\\bnewsletter_handle_reservation_optin\\s*\\(\\s*\\)\\s*;~', $successBlock);
        if ($successCalls === false || $successCalls > 1) {
            throw new RuntimeException('Niejednoznaczny zapis newslettera po rezerwacji.');
        }
        // Jeśli wcześniejsza poprawka już przeniosła wywołanie, nie duplikujemy go.
        if ($successCalls === 1) return $content;

        $sessionLine = '~(?m)^([ \\t]*)(\\$_SESSION\\[\\s*[\\\'\"]reservation_last_submit[\\\'\"]\\s*\\]\\s*=\\s*time\\(\\)\\s*;)~';
        if (preg_match_all($sessionLine, $successBlock, $m, PREG_SET_ORDER) !== 1) {
            throw new RuntimeException('Nie znaleziono jednoznacznego miejsca po poprawnym zapisie rezerwacji.');
        }
        $indent = $m[0][1];
        $old = $m[0][0];
        $new = $old . "\\n" . $indent . '/* POLANA_V172_RESERVATION_OPTIN_AFTER_SAVE: zgoda dopiero po zapisie */' . "\\n" . $indent . "if (function_exists('newsletter_handle_reservation_optin')) newsletter_handle_reservation_optin();";
        $updated = substr_replace($successBlock, str_replace('\\n', "\n", $new), strpos($successBlock, $old), strlen($old));
        return substr($content, 0, $createAt) . $updated . substr($content, $sendAt);
    }
    if ($relative==='includes/reservations.php') {
        if(str_contains($content,'POLANA_V172_REPLY_TO')) return $content;
        $before=<<<'BEFORE'
    smtp_send_mail(
        (string)$reservation['email'],
        $subject,
        reservation_email_html($subject, $body),
        $body
    );
BEFORE;
        $after=<<<'AFTER'
    /* POLANA_V172_REPLY_TO: gość odpisuje do rezerwacji, nie do no-reply */
    smtp_send_mail(
        (string)$reservation['email'],
        $subject,
        reservation_email_html($subject, $body),
        $body,
        trim(reservation_setting('reservation_admin_email')) ?: null
    );
AFTER;
        return pi172_one($content,$before,$after,'guest autoresponder Reply-To');
    }
    if ($relative==='includes/functions.php') {
        if(str_contains($content,'POLANA_V172_ADMIN_TOP_LINKS')) return $content;
        $needle='<a href="/admin/">Pulpit</a>';
        $insert=$needle."\n        <!-- POLANA_V172_ADMIN_TOP_LINKS -->\n        <?php if (\$role === 'admin'): ?>\n          <a href=\"/admin/polana-komunikacja.php\">Poczta i komunikacja</a>\n          <a href=\"/admin/polana-newsletter.php\">Newsletter</a>\n        <?php endif; ?>";
        return pi172_one($content,$needle,$insert,'admin top navigation');
    }
    throw new RuntimeException('Nieobsługiwany plik '.$relative);
}
function pi172_write_atomic(string $file,string $contents):void {
    $temp=$file.'.p172_'.bin2hex(random_bytes(6)).'.tmp';
    if(@file_put_contents($temp,$contents,LOCK_EX)!==strlen($contents)){
        @unlink($temp);throw new RuntimeException('Nie można zapisać pliku '.basename($file));
    }
    $mode=@fileperms($file);if($mode!==false)@chmod($temp,$mode&0777);
    if(!@rename($temp,$file)){
        @unlink($temp);throw new RuntimeException('Nie można podmienić pliku '.basename($file));
    }
}
function pi172_settings_targets():array {
    return [
        'reservation_admin_email'=>'[email protected]',
        'reservation_auto_reply_enabled'=>'1',
    ];
}
function pi172_setting_snapshot(array $settings):array {
    $select=db()->prepare('SELECT setting_value FROM settings WHERE setting_key=? LIMIT 1');
    $rows=[];
    foreach($settings as $k=>$value){$select->execute([$k]);$old=$select->fetchColumn();$rows[$k]=['had'=>$old!==false,'before'=>$old!==false?(string)$old:null,'after'=>$value];}
    return $rows;
}
function pi172_save_settings(array $settings):void {
    $stmt=db()->prepare('INSERT INTO settings(setting_key,setting_value) VALUES(?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
    foreach($settings as $key=>$value)$stmt->execute([$key,$value]);
}
function pi172_install():string {
    if(!is_file(pi172_root().'/includes/polana_v172_newsletter.php') || !is_file(pi172_root().'/admin/polana-newsletter.php') || !is_file(pi172_root().'/admin/polana-komunikacja.php')){
        throw new RuntimeException('Najpierw wgraj WSZYSTKIE pliki ZIP do odpowiednich katalogów.');
    }
    $changes=[];
    foreach(pi172_targets() as $relative=>$file){
        if(!is_file($file) || !is_readable($file) || !is_writable($file))throw new RuntimeException('Brak odczytu/zapisu: '.$relative);
        $before=file_get_contents($file);
        if(!is_string($before))throw new RuntimeException('Brak odczytu: '.$relative);
        $normalized=str_replace("\r\n","\n",$before);
        $patched=pi172_patch($relative,$normalized);
        $after=str_contains($before,"\r\n") ? str_replace("\n","\r\n",$patched) : $patched;
        if($after!==$before)$changes[$relative]=['file'=>$file,'before'=>$before,'after'=>$after];
    }
    if (!$changes) {
        return 'V17.2 jest już zainstalowana — nie utworzono kolejnego backupu ani nie zmieniono ustawień.';
    }
    $settingSnapshot=pi172_setting_snapshot(pi172_settings_targets());
    $dir=pi172_backupdir();
    if(!is_dir($dir)&&!@mkdir($dir,0700,true))throw new RuntimeException('Nie można utworzyć prywatnych backupów poza WWW.');
    if(!is_writable($dir))throw new RuntimeException('Katalog backupu nie jest zapisywalny.');
    $path=$dir.'/'.date('Ymd_His').'_'.bin2hex(random_bytes(4));
    if(!@mkdir($path,0700))throw new RuntimeException('Nie można utworzyć folderu kopii.');
    $manifest=['version'=>'17.2','files'=>[],'settings'=>$settingSnapshot];
    foreach($changes as $relative=>$c){
        $backup='original-'.count($manifest['files']).'.bak';
        if(@file_put_contents($path.'/'.$backup,$c['before'],LOCK_EX)!==strlen($c['before']))throw new RuntimeException('Nie udało się zapisać backupu. Nic nie zmieniono.');
        @chmod($path.'/'.$backup,0600);
        $manifest['files'][$relative]=['backup'=>$backup,'before_sha'=>hash('sha256',$c['before']),'after_sha'=>hash('sha256',$c['after'])];
    }
    $manifestJson=json_encode($manifest,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
    if(!$manifestJson || @file_put_contents($path.'/manifest.json',$manifestJson,LOCK_EX)!==strlen($manifestJson))throw new RuntimeException('Brak zapisu manifestu. Nic nie zmieniono.');
    @chmod($path.'/manifest.json',0600);
    // MariaDB DDL is additive. It does not alter existing subscriber status, consent or data.
    pv172_upgrade_schema();
    $written=[];
    try {
        foreach($changes as $relative=>$c){
            $current=@file_get_contents($c['file']);
            if($current!==$c['before'])throw new RuntimeException('Plik zmienił się w trakcie instalacji: '.$relative);
            pi172_write_atomic($c['file'],$c['after']);$written[$relative]=$c;
        }
        $pdo=db();$pdo->beginTransaction();
        try{pi172_save_settings(pi172_settings_targets());$pdo->commit();}catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
    }catch(Throwable $e){
        foreach(array_reverse($written) as $c){
            if(@file_get_contents($c['file'])===$c['after']){
                try{pi172_write_atomic($c['file'],$c['before']);}catch(Throwable $ignored){}
            }
        }
        throw $e;
    }
    try { admin_log('polana_v172_install','Aktywowano newsletter double opt-in, komunikację i Reply-To (bez haseł)'); } catch(Throwable $ignored) {}
    return 'Instalacja ukończona. Zmieniono '.count($changes).' plików i skonfigurowano odbiorcę rezerwacji. Kliknij „Poczta i komunikacja”.';
}
function pi172_rollback():string {
    $paths=glob(pi172_backupdir().'/*/manifest.json')?:[];rsort($paths);
    $last=null;
    foreach($paths as $candidate){if(str_ends_with($candidate,'/manifest.json')){$last=$candidate;break;}}
    if(!$last)throw new RuntimeException('Nie znaleziono wykonanej kopii V17.2.');
    $manifest=json_decode((string)file_get_contents($last),true);
    if(!is_array($manifest)||($manifest['version']??'')!=='17.2')throw new RuntimeException('Nieprawidłowa kopia V17.2.');
    $restores=[];$dir=dirname($last);
    foreach(($manifest['files']??[]) as $relative=>$item){
        $targets=pi172_targets();
        if(!isset($targets[$relative]))throw new RuntimeException('Nieznany plik w kopii: '.$relative);
        $target=$targets[$relative];
        $saved=@file_get_contents($dir.'/'.basename((string)$item['backup']));
        $current=@file_get_contents($target);
        if(!is_string($saved)||!is_string($current)||hash('sha256',$saved)!==$item['before_sha']||hash('sha256',$current)!==$item['after_sha']){
            throw new RuntimeException('Plik '.$relative.' został później zmieniony lub kopia jest uszkodzona. Nie cofnięto niczego.');
        }
        $restores[]=[$target,$saved];
    }
    $currentSettings=pi172_setting_snapshot(pi172_settings_targets());
    foreach(($manifest['settings']??[]) as $key=>$v){
        if(!isset($currentSettings[$key]) || $currentSettings[$key]['before']!==(string)$v['after'])throw new RuntimeException('Ustawienie '.$key.' było zmieniane później — rollback zablokowany.');
    }
    foreach($restores as [$path,$saved])pi172_write_atomic($path,$saved);
    $pdo=db();$pdo->beginTransaction();
    try{
        $up=$pdo->prepare('INSERT INTO settings(setting_key,setting_value) VALUES(?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
        $del=$pdo->prepare('DELETE FROM settings WHERE setting_key=?');
        foreach(($manifest['settings']??[]) as $key=>$v){if($v['had'])$up->execute([$key,$v['before']]);else $del->execute([$key]);}
        $pdo->commit();
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
    @rename($last,$last.'.restored');
    return 'Przywrócono pliki i ustawienia z poprzedniej wersji. Dane zgód newslettera zostały zachowane. Dodatkowe kolumny bazy pozostają bezpiecznie w tabeli.';
}


Youez - 2016 - github.com/yon3zu
LinuXploit