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_backup_v1811.php
<?php
declare(strict_types=1);
/** POLANA V18.11 — additive quarterly + pre-change full backups and Drive verification. */
require_once __DIR__ . '/polana_backup_v182.php';

function p1811_state_path(string $name): string {
    if (!in_array($name, ['manual','quarter','cron'], true)) throw new RuntimeException('Nieprawidłowy stan.');
    return pbk_private() . '/v1811-' . $name . '.json';
}
function p1811_read(string $name): array {
    $file = p1811_state_path($name);
    if (!is_file($file)) return [];
    $data = json_decode((string)file_get_contents($file), true);
    if (!is_array($data)) throw new RuntimeException('Niepoprawny plik stanu V18.11: ' . $name);
    return $data;
}
function p1811_write(string $name, array $data): void {
    $file = p1811_state_path($name);
    $tmp = $file . '.tmp-' . bin2hex(random_bytes(5));
    try {
        if (file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR), LOCK_EX) === false)
            throw new RuntimeException('Nie można zapisać stanu backupu.');
        @chmod($tmp, 0600);
        if (!rename($tmp, $file)) throw new RuntimeException('Nie można zatwierdzić stanu backupu.');
    } finally { if (is_file($tmp)) @unlink($tmp); }
}
function p1811_fullsql_dir(): string {
    $dir = pbk_private() . '/fullsql';
    if (!is_dir($dir) && !mkdir($dir, 0700)) throw new RuntimeException('Nie można utworzyć katalogu kopii pełnych SQL.');
    @chmod($dir,0700);
    return $dir;
}
function p1811_remote_meta(string $id, array $cfg): array {
    if (!preg_match('/^[A-Za-z0-9_-]{8,200}$/', $id)) throw new RuntimeException('Brak poprawnego ID kopii w Google Drive.');
    $token = pbk_drive_access_token($cfg);
    [$code,$body] = pbk_http('https://www.googleapis.com/drive/v3/files/' . rawurlencode($id) . '?fields=id,name,size,md5Checksum,trashed,parents',
        'GET', ['Authorization: Bearer ' . $token]);
    if ($code !== 200) throw new RuntimeException('Google Drive nie potwierdził obecności pliku (HTTP ' . $code . ').');
    $data = json_decode($body, true);
    if (!is_array($data) || empty($data['id']) || !empty($data['trashed'])) throw new RuntimeException('Nieprawidłowa odpowiedź Google Drive.');
    return $data;
}
function p1811_verify_remote(string $path, string $id, array $cfg): array {
    if (!is_file($path) || !pbk_verify_encrypted($path)) throw new RuntimeException('Kopia lokalna jest nieobecna lub ma uszkodzone szyfrowanie.');
    $remote = p1811_remote_meta($id,$cfg);
    $expectedSize = filesize($path);
    if (($remote['name'] ?? '') !== basename($path) || (string)($remote['size'] ?? '') !== (string)$expectedSize) {
        throw new RuntimeException('Google Drive: nazwa lub wielkość pliku nie zgadza się z kopią lokalną.');
    }
    $md5 = (string)($remote['md5Checksum'] ?? '');
    if ($md5 === '' || !hash_equals(strtolower($md5), strtolower(hash_file('md5',$path)))) {
        throw new RuntimeException('Google Drive: suma kontrolna przesłanego archiwum jest niezgodna lub niedostępna.');
    }
    return ['size'=>$expectedSize,'md5'=>$md5,'id'=>$id,'verified_at'=>date(DATE_ATOM)];
}
function p1811_verify_existing(string $path, array &$cfg): array {
    $state = pbk_status($path);
    $id = (string)($state['drive_file_id'] ?? '');
    if ($id === '') throw new RuntimeException('Ta kopia nie ma potwierdzonego identyfikatora Google Drive.');
    $result = p1811_verify_remote($path,$id,$cfg);
    $state['drive'] = true;
    $state['drive_verified_at'] = $result['verified_at'];
    $state['drive_verified_size'] = $result['size'];
    pbk_save_status($path,$state);
    return $result;
}
function p1811_send_verified(string $path, array &$cfg): array {
    if (empty($cfg['drive_enabled'])) throw new RuntimeException('Włącz wysyłkę na Google Drive w ustawieniach backupów.');
    if (empty($cfg['drive_refresh_token'])) throw new RuntimeException('Połącz konto Google Drive.');
    $state = pbk_status($path);
    if (!empty($state['drive_file_id'])) {
        try { return p1811_verify_existing($path,$cfg); }
        catch (Throwable $e) {
            // Do not silently replace existing remote file on transient API failures.
            throw new RuntimeException('Istniejący plik Drive wymaga wyjaśnienia: ' . $e->getMessage());
        }
    }
    if (!pbk_verify_encrypted($path)) throw new RuntimeException('Archiwum nie przeszło sprawdzenia szyfrowania.');
    $id = pbk_drive_upload($path,$cfg);
    // Save ID immediately: after a temporary verification failure the next run checks the same remote file, not a duplicate.
    $state['drive_file_id'] = $id;
    $state['drive'] = false;
    pbk_save_status($path,$state);
    $verified = p1811_verify_remote($path,$id,$cfg);
    $state['drive'] = true;
    $state['drive_verified_at'] = $verified['verified_at'];
    $state['drive_verified_size'] = $verified['size'];
    pbk_save_status($path,$state);
    pbk_log('drive-verify','OK',basename($path));
    return $verified;
}
/** A tiny encrypted probe is uploaded, verified using Drive md5/size and deleted afterwards. */
function p1811_drive_probe(array &$cfg): string {
    if (empty($cfg['drive_refresh_token'])) throw new RuntimeException('Najpierw połącz konto Google Drive.');
    $path = pbk_private() . '/tmp/polana-test-drive-' . date('YmdHis') . '-' . bin2hex(random_bytes(4)) . '.pbackup';
    $source = fopen('php://temp','w+b');
    if ($source === false) throw new RuntimeException('Brak pamięci na test.');
    fwrite($source, 'POLANA DRIVE TEST ' . date(DATE_ATOM) . ' ' . bin2hex(random_bytes(16)));
    rewind($source);
    $id = '';
    try {
        pbk_encrypt_stream($source,$path);
        $id = pbk_drive_upload($path,$cfg);
        $verified = p1811_verify_remote($path,$id,$cfg);
        pbk_log('drive-probe','OK','Wysłano i sprawdzono plik testowy na Drive: ' . $verified['size'] . ' B.');
        $message = 'Wysłano i zweryfikowano plik testowy na Google Drive (rozmiar i MD5).';
    } finally {
        fclose($source);
        @unlink($path);
        if ($id !== '') {
            try {
                $token = pbk_drive_access_token($cfg);
                [$http] = pbk_http('https://www.googleapis.com/drive/v3/files/'.rawurlencode($id),'DELETE',['Authorization: Bear '.$token]);
                if (!in_array($http,[200,204],true)) pbk_log('drive-probe','WARN','Nie udało się usunąć testowego pliku z Drive.');
            } catch (Throwable $ignored) { pbk_log('drive-probe','WARN','Testowy plik może pozostać na Drive.'); }
        }
    }
    return $message;
}
function p1811_estimate_site_bytes(): int {
    $root = pbk_root(); $bytes = 0; $files = 0;
    $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::LEAVES_ONLY);
    foreach ($it as $entry) {
        if (!$entry->isFile() || $entry->isLink()) continue;
        $rel = str_replace('\\','/',substr($entry->getPathname(),strlen($root)+1));
        if (preg_match('~(^|/)(\.git|node_modules|vendor-cache|cache|tmp|sessions|\.polana_v174_backups|polana-v15-backups|polana-i18n-v96-backup)(/|$)~i', $rel)) continue;
        if (preg_match('~(\.log$|\.bak$|\.tmp$|\.zip$|\.tar$|\.sql$|\.pbackup$|^admin/database_z_zip\.sql$)~i', $rel)) continue;
        $bytes += max(0,(int)$entry->getSize()); $files++;
        if ($files > 150000) throw new RuntimeException('Za dużo plików do archiwizacji.');
    }
    if ($files < 15) throw new RuntimeException('Znaleziono za mało plików strony.');
    $free = disk_free_space(pbk_private());
    if ($free !== false && $free < ($bytes * 2.2 + 64 * 1048576)) {
        throw new RuntimeException('Za mało wolnego miejsca na tymczasowe archiwum strony i kopię szyfrowaną.');
    }
    return $bytes;
}
function p1811_current_quarter(): string {
    return date('Y') . '-Q' . (int)ceil((int)date('n') / 3);
}
function p1811_enqueue_manual(): array {
    $cfg = pbk_cfg();
    if (empty($cfg['enabled']) || empty($cfg['key_exported'])) throw new RuntimeException('Najpierw włącz backupy i zachowaj klucz szyfrowania.');
    $job = p1811_read('manual');
    if (in_array(($job['status'] ?? ''), ['queued','working','waiting_drive'],true)) throw new RuntimeException('Pełna kopia jest już zlecona. Zaczekaj na zakończenie.');
    $job = ['id'=>bin2hex(random_bytes(8)), 'status'=>'queued','requested_at'=>date(DATE_ATOM),'source'=>'manual'];
    p1811_write('manual',$job);
    return $job;
}
function p1811_site_job(string $name, array $job, array &$cfg): array {
    $lock = fopen(pbk_private() . '/backup.lock','c+');
    if (!$lock || !flock($lock, LOCK_EX|LOCK_NB)) throw new RuntimeException('Trwa już inne zadanie backupu.');
    try {
        $job['status']='working';
        p1811_write($name,$job);
        // Stamp is kept stable across retries, preventing multiple new full archives.
        $stamp = (string)($job['stamp'] ?? '');
        if ($stamp === '') { $stamp = date('Y-m-d-His'); $job['stamp']=$stamp; p1811_write($name,$job); }
        if (empty($job['site'])) {
            p1811_estimate_site_bytes();
            $site = pbk_site_weekly($stamp);
            $job['site'] = basename($site);
            p1811_write($name,$job);
            pbk_log('full-site','OK','Utworzono kopię plików: '.$job['site']);
        }
        if (empty($job['sql'])) {
            $dest = p1811_fullsql_dir().'/polana-sql-'.$stamp.'.sql.gz.pbackup';
            if (!is_file($dest)) {
                $src = pbk_sql_daily($stamp);
                if (!rename($src,$dest)) throw new RuntimeException('Nie można przenieść SQL do katalogu pełnych kopii.');
                @chmod($dest,0600);
            }
            $job['sql']=basename($dest);
            p1811_write($name,$job);
            pbk_log('full-sql','OK','Utworzono punktowy SQL: '.$job['sql']);
        }
        $sitePath = pbk_private().'/weekly/'.basename((string)$job['site']);
        $sqlPath = p1811_fullsql_dir().'/'.basename((string)$job['sql']);
        if (!is_file($sitePath) || !is_file($sqlPath)) throw new RuntimeException('Brakuje jednej z dwóch części kompletnej kopii.');
        if (empty($cfg['drive_enabled']) || empty($cfg['drive_refresh_token'])) {
            $job['status']='waiting_drive';
            $job['last_error']='Obie kopie powstały lokalnie, ale wysyłka Drive nie jest włączona.';
            p1811_write($name,$job);
            pbk_log('full-site','WARN',$job['last_error']);
            return $job;
        }
        $siteVerified = p1811_send_verified($sitePath,$cfg);
        $sqlVerified = p1811_send_verified($sqlPath,$cfg);
        $job['status']='done'; $job['completed_at']=date(DATE_ATOM);
        $job['site_drive_id']=$siteVerified['id']; $job['sql_drive_id']=$sqlVerified['id'];
        unset($job['last_error']);
        p1811_write($name,$job);
        pbk_log('full-backup','OK','Pełna kopia plików + SQL potwierdzona na Google Drive: '.$job['site'].' / '.$job['sql']);
        return $job;
    } catch (Throwable $e) {
        $job['status']='waiting_drive';
        $job['last_error']=substr($e->getMessage(),0,300);
        p1811_write($name,$job);
        pbk_log('full-backup','ERROR',$job['last_error']);
        throw $e;
    } finally { flock($lock,LOCK_UN); fclose($lock); }
}
function p1811_prune_fullsql(array $cfg): void {
    $list=glob(p1811_fullsql_dir().'/*.pbackup')?:[];
    rsort($list,SORT_STRING);
    foreach (array_slice($list,8) as $file) {
        $state=pbk_status($file);
        if (empty($state['drive_verified_at'])) continue;
        @unlink($file); @unlink(pbk_status_path($file));
    }
}
function p1811_sql_needs_retry(array $cfg): bool {
    $path=pbk_private().'/daily/polana-sql-'.date('Y-m-d').'.sql.gz.pbackup';
    if (!is_file($path)) return true;
    $s=pbk_status($path);
    return (!empty($cfg['email_enabled']) && empty($s['mail'])) || (!empty($cfg['drive_enabled']) && empty($s['drive']));
}
/** 1 cron hourly: daily SQL after 03:00, quarterly full once per quarter, queued full from panel. */
function p1811_tick(bool $forceFull=false): array {
    date_default_timezone_set('Europe/Warsaw');
    $cfg=pbk_cfg();
    if (empty($cfg['enabled']) || empty($cfg['key_exported'])) throw new RuntimeException('Backup wyłączony lub nie pobrano klucza.');
    $result=[]; $failed=[];
    if ((int)date('G')>=3 && p1811_sql_needs_retry($cfg)) {
        try { $result=array_merge($result,pbk_run('sql')); }
        catch (Throwable $e) { $failed[]='SQL: '.$e->getMessage(); }
    }
    $quarter=p1811_current_quarter();
    $qjob=p1811_read('quarter');
    if (($qjob['quarter']??'')!==$quarter) {
        $qjob=['source'=>'quarter','quarter'=>$quarter,'status'=>'queued','requested_at'=>date(DATE_ATOM)];
        p1811_write('quarter',$qjob);
    }
    if ($forceFull) p1811_enqueue_manual();
    // Manual backup takes priority; the same archive can satisfy the quarterly point.
    foreach (['manual','quarter'] as $name) {
        $job=p1811_read($name);
        if (!in_array(($job['status']??''),['queued','working','waiting_drive'],true)) continue;
        try {
            $updated=p1811_site_job($name,$job,$cfg);
            $result[]=$name.': '.$updated['status'];
            if ($name==='manual' && !empty($updated['site']) && !empty($updated['sql'])) {
                $quarterJob=p1811_read('quarter');
                if (($quarterJob['quarter']??'')===$quarter && ($quarterJob['status']??'')==='queued') {
                    foreach (['stamp','site','sql','status','completed_at','site_drive_id','sql_drive_id','last_error'] as $field) {
                        if (array_key_exists($field,$updated)) $quarterJob[$field]=$updated[$field];
                    }
                    p1811_write('quarter',$quarterJob);
                }
            }
            if (($updated['status'] ?? '') !== 'done') $failed[]=$name.': wymaga potwierdzenia wysyłki na Drive';
        } catch(Throwable $e) { $failed[]=$name.': '.$e->getMessage(); }
    }
    p1811_prune_fullsql($cfg);
    p1811_write('cron',['last_run'=>date(DATE_ATOM),'last_result'=>$failed?'WARN':'OK','details'=>array_slice($result,-6),'errors'=>array_slice($failed,-4)]);
    if ($failed) throw new RuntimeException(implode('; ',$failed));
    return $result?:['Harmonogram sprawdzony; nowe kopie nie są obecnie wymagane.'];
}

Youez - 2016 - github.com/yon3zu
LinuXploit