| 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.12 – emergency mail diagnostics. Install one admin file; reversible config patch.
* This file is intentionally a standalone authenticated admin page.
* It does not change reservation status or any customer data.
*/
require_once dirname(__DIR__) . '/includes/functions.php';
require_admin_role();
header('Cache-Control: private, no-store, max-age=0');
header('Pragma: no-cache');
header('X-Robots-Tag: noindex,nofollow');
const PM12_URL = '/admin/polana-v1812-naprawa-maili.php';
function pm12_h($s): string { return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function pm12_settings_write(array $items): void {
$pdo = db();
$pdo->beginTransaction();
try {
$q=$pdo->prepare('INSERT INTO settings (setting_key,setting_value) VALUES (?,?) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value)');
foreach ($items as $k=>$v) $q->execute([(string)$k,(string)$v]);
$pdo->commit();
} catch (Throwable $e) {
if($pdo->inTransaction()) $pdo->rollBack();
throw $e;
}
}
function pm12_is_email(string $val): bool { return $val!=='' && filter_var($val, FILTER_VALIDATE_EMAIL)!==false && !preg_match('/[\r\n]/',$val); }
function pm12_rate_limit(string $action, int $seconds=20): void {
start_session();
$k='pm12_last_'.$action;
if((int)($_SESSION[$k]??0)>time()-$seconds) throw new RuntimeException('Odczekaj chwilę przed kolejną wysyłką.');
$_SESSION[$k]=time();
}
function pm12_error_safe(Throwable $e): string {
$s=$e->getMessage();
$s=preg_replace('/(?i)(password|secret|token)\s*[:=]\s*\S+/','[dane ukryte]',$s)??'Nieznany błąd';
return function_exists('mb_substr') ? mb_substr($s,0,500,'UTF-8') : substr($s,0,500);
}
if(($_SERVER['REQUEST_METHOD']??'GET')==='POST') {
verify_csrf();
$action=(string)($_POST['action']??'');
try {
if($action==='fix_from') {
$username=trim(setting('smtp_username',''));
$from=trim(setting('smtp_from_email',''));
if(!pm12_is_email($username)) throw new RuntimeException('Login SMTP nie jest adresem e-mail; nie można bezpiecznie ustawić nadawcy automatycznie.');
if(strcasecmp($from,$username)===0) throw new RuntimeException('Nadawca SMTP już odpowiada loginowi – nie ma tu nic do naprawienia.');
// Own undo keys are created only when actually changing the setting.
pm12_settings_write([
'polana_v1812_previous_from'=>$from,
'polana_v1812_fixed_from'=>$username,
'smtp_from_email'=>$username,
]);
try { admin_log('polana_v1812_smtp_from','Skorygowano nadawcę SMTP do loginu; stare ustawienie dostępne do rollbacku.'); } catch(Throwable $ignored) {}
flash('success','Zmieniono nadawcę SMTP na konto używane do logowania. Adres powiadomień restauracji oraz hasło SMTP nie zostały zmienione. Wykonaj test wysyłki.');
} elseif($action==='undo_from') {
$previous=setting('polana_v1812_previous_from','');
$fixed=setting('polana_v1812_fixed_from','');
$current=setting('smtp_from_email','');
if($fixed==='' || strcasecmp($current,$fixed)!==0) throw new RuntimeException('Ustawienie nadawcy zmieniło się od czasu poprawki. Cofanie automatyczne zablokowane.');
if($previous!=='' && !pm12_is_email($previous)) throw new RuntimeException('Poprzedni adres jest niepoprawny.');
pm12_settings_write(['smtp_from_email'=>$previous,'polana_v1812_fixed_from'=>'']);
flash('success','Przywrócono poprzedni adres nadawcy SMTP.');
} elseif($action==='auto_reply_on') {
$prev=setting('reservation_auto_reply_enabled','0');
pm12_settings_write(['polana_v1812_previous_auto_reply'=>$prev,'reservation_auto_reply_enabled'=>'1']);
flash('success','Włączono wiadomość automatyczną: otrzymaliśmy prośbę. Nie jest ona potwierdzeniem stolika.');
} elseif($action==='test_smtp') {
pm12_rate_limit($action);
$to=trim((string)($_POST['test_email']??''));
if(!pm12_is_email($to)) throw new RuntimeException('Podaj poprawny adres odbiorcy testu.');
if(setting('smtp_enabled','0')!=='1') throw new RuntimeException('SMTP jest wyłączone.');
$text='Test POLANA V18.12: serwer SMTP przyjął wiadomość testową. Sprawdź odbiór i folder SPAM.';
smtp_send_mail($to,'POLANA V18.12 — test potwierdzeń',reservation_email_html('Test potwierdzeń',$text),$text);
flash('success','SMTP przyjął wiadomość testową do wskazanego odbiorcy. Sprawdź skrzynkę i SPAM (sam sukces SMTP nie gwarantuje doręczenia).');
} elseif($action==='resend_received' || $action==='resend_confirmed') {
pm12_rate_limit('resend',15);
if((string)($_POST['confirm_send']??'')!=='TAK') throw new RuntimeException('Potwierdź świadomie ponowną wysyłkę.');
$id=filter_var($_POST['reservation_id']??'',FILTER_VALIDATE_INT,['options'=>['min_range'=>1]]);
if(!$id) throw new RuntimeException('Nieprawidłowy numer ID rezerwacji.');
$reservation=reservation_by_id((int)$id);
if(!$reservation) throw new RuntimeException('Nie znaleziono rezerwacji.');
$kind=$action==='resend_confirmed'?'confirmed':'received';
if($kind==='confirmed' && (string)$reservation['status']!=='confirmed') {
throw new RuntimeException('Potwierdzenie można ponowić tylko dla rezerwacji ze statusem Potwierdzona.');
}
if($kind==='received' && (string)$reservation['status']!=='new') {
throw new RuntimeException('Autoresponder prośby można ponowić tylko dla rezerwacji Nowa.');
}
if(!pm12_is_email(trim((string)($reservation['email']??'')))) throw new RuntimeException('Rezerwacja nie ma prawidłowego e-maila.');
try {
reservation_send_guest_message($reservation,$kind);
} catch(Throwable $mailError) {
$safe=pm12_error_safe($mailError);
try { reservation_add_event((int)$id,'email_error','POLANA V18.12: nieudana próba ręcznej wysyłki: '.$safe); } catch(Throwable $ignored) {}
throw new RuntimeException('E-mail nie został przyjęty przez SMTP: '.$safe);
}
flash('success','SMTP przyjął wiadomość dla wybranej rezerwacji; jej status NIE został zmieniony. Sprawdź doręczenie u gościa.');
} else {
throw new RuntimeException('Nieznana operacja.');
}
} catch(Throwable $e) {
flash('error','Nie wykonano: '.pm12_error_safe($e));
}
redirect(PM12_URL);
}
$username=trim(setting('smtp_username',''));
$from=trim(setting('smtp_from_email',''));
$reply=trim(setting('reservation_admin_email',''));
$isEnabled=setting('smtp_enabled','0')==='1';
$autoReply=setting('reservation_auto_reply_enabled','0')==='1';
$hasPassword=function_exists('smtp_password_is_set') && smtp_password_is_set();
$match=$username!=='' && strcasecmp($username,$from)===0;
$canFix=pm12_is_email($username) && !$match;
$errors=[];$recent=[];$historyReadError='';
try {
ensure_reservations_schema();
$errors=db()->query("SELECT e.reservation_id, e.message, e.created_at, r.reservation_code, r.status
FROM reservation_events e JOIN reservations r ON r.id=e.reservation_id
WHERE e.event_type='email_error' ORDER BY e.id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC);
$recent=db()->query("SELECT id, reservation_code, status, reservation_date, reservation_time, email
FROM reservations WHERE status IN ('confirmed','new') ORDER BY id DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC);
} catch(Throwable $e) { $historyReadError='Historia niedostępna: '.pm12_error_safe($e); }
render_admin_header('POLANA V18.12 — Naprawa potwierdzeń e-mail');
?>
<style>
.pm12{max-width:1100px;margin:0 auto;padding:14px 12px 60px}.pm12 .pane{background:var(--card,#fff);border:1px solid #d3d9d3;border-radius:14px;margin:15px 0;padding:18px;color:inherit}.pm12 h1{font-size:1.7rem}.pm12 .dim{opacity:.78}.pm12 .status{font-weight:bold}.pm12 .good{color:#15803d}.pm12 .bad{color:#b91c1c}.pm12 .warn{color:#9a6517}.pm12 table{width:100%;border-collapse:collapse;font-size:14px}.pm12 th,.pm12 td{border-bottom:1px solid #9995;padding:10px 7px;text-align:left;vertical-align:top}.pm12 input{padding:11px;border:1px solid #aebbb2;border-radius:8px;min-width:220px;max-width:100%}.pm12 button{border:0;background:#b95418;color:#fff;padding:12px 16px;border-radius:9px;cursor:pointer;font-weight:bold;margin:4px}.pm12 button.secondary{background:#3e4f50}.pm12 .wide{overflow-x:auto}.pm12 .small{font-size:13px}.pm12 form{margin:10px 0}.pm12 a{color:inherit;text-decoration:underline}.pm12 .stop{border-left:5px solid #b45309;padding-left:12px}.pm12 .line{display:flex;align-items:center;flex-wrap:wrap;gap:10px}
</style>
<div class="pm12">
<h1>Naprawa wysyłania potwierdzeń rezerwacji</h1>
<p>Diagnostyka działa na <strong>aktualnych ustawieniach hostingu</strong>, bez odczytywania haseł na ekran. Nie zmienia statusów rezerwacji, a przyciski wysyłki wymagają świadomego potwierdzenia.</p>
<section class="pane"><h2>1. Kontrola SMTP</h2>
<table><tr><td>SMTP</td><td><span class="status <?= $isEnabled?'good':'bad' ?>"><?= $isEnabled?'WŁĄCZONE':'WYŁĄCZONE' ?></span></td></tr>
<tr><td>Zaszyfrowane hasło</td><td><?= $hasPassword?'Zapisano':'BRAK' ?></td></tr>
<tr><td>Serwer</td><td><?=pm12_h(setting('smtp_host',''))?> : <?=pm12_h(setting('smtp_port',''))?> (<?=pm12_h(setting('smtp_security',''))?>)</td></tr>
<tr><td>Login SMTP</td><td><?=pm12_h($username)?></td></tr>
<tr><td>Nadawca SMTP (From)</td><td><?=pm12_h($from)?></td></tr>
<tr><td>Zgodność nadawcy z kontem</td><td class="status <?= $match?'good':'bad' ?>"><?= $match?'TAK':'NIE — może powodować 553 na home.pl' ?></td></tr>
<tr><td>Adres powiadomień restauracji (odbiorca / Reply-To)</td><td><?=pm12_h($reply)?></td></tr>
<tr><td>Autoresponder po nowej prośbie</td><td class="status <?= $autoReply?'good':'warn' ?>"><?= $autoReply?'WŁĄCZONY':'WYŁĄCZONY' ?></td></tr></table>
<?php if($canFix): ?>
<p class="stop"><strong>Naprawa możliwa:</strong> nadawca różni się od loginu. Kliknięcie zmienia tylko ustawienie <code>smtp_from_email</code> na login. Nie zmienia hasła, adresu powiadomień ani stanu rezerwacji.</p>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="fix_from"><button type="submit">Napraw nadawcę SMTP</button></form>
<?php endif; ?>
<?php if(setting('polana_v1812_fixed_from','')!=='' && $from===setting('polana_v1812_fixed_from','')): ?>
<form method="post"><?=csrf_field()?><input type="hidden" name="action" value="undo_from"><button class="secondary" type="submit">Rollback — przywróć poprzedniego nadawcę</button></form>
<?php endif; ?>
<?php if(!$autoReply): ?><form method="post"><?=csrf_field()?><input type="hidden" name="action" value="auto_reply_on"><button class="secondary" type="submit">Włącz automatyczną odpowiedź po nowym zgłoszeniu</button></form><?php endif; ?>
<p><a href="/admin/polana-poczta.php">Pełna konfiguracja SMTP</a> · <a href="/admin/reservation-settings.php">Ustawienia rezerwacji</a></p>
</section>
<section class="pane"><h2>2. Test SMTP — bez zmiany rezerwacji</h2>
<p>Najpierw testuj na własnym adresie. SMTP może przyjąć e-mail, ale jego doręczenie do skrzynki należy jeszcze sprawdzić.</p>
<form method="post" class="line"><?=csrf_field()?><input type="hidden" name="action" value="test_smtp">
<label for="test_email">E-mail testowy <input id="test_email" name="test_email" type="email" required maxlength="254" placeholder="[email protected]"></label>
<button type="submit">Wyślij jeden test</button></form></section>
<section class="pane"><h2>3. Błędy wiadomości zapisane przy rezerwacjach</h2>
<?php if($historyReadError!==''):?><p class="bad"><?=pm12_h($historyReadError)?></p>
<?php elseif(!$errors):?><p>Nie znaleziono wpisów <code>email_error</code> w ostatnich zdarzeniach. To nie potwierdza doręczenia poczty.</p>
<?php else:?><div class="wide"><table><thead><tr><th>Termin</th><th>Rezerwacja</th><th>Wynik</th></tr></thead><tbody>
<?php foreach($errors as $row): ?><tr><td><?=pm12_h($row['created_at'])?></td><td><a href="/admin/reservation.php?id=<?=(int)$row['reservation_id']?>"><?=pm12_h($row['reservation_code'])?></a><br><?=pm12_h($row['status'])?></td><td><?=pm12_h($row['message'])?></td></tr><?php endforeach; ?>
</tbody></table></div><?php endif; ?></section>
<section class="pane"><h2>4. Ponów potwierdzenie jednej istniejącej rezerwacji</h2>
<p class="stop"><strong>Uwaga:</strong> to wysyła prawdziwy e-mail do gościa. Najpierw wykonaj test SMTP i sprawdź historię, żeby nie zdublować wiadomości. Nie zmienia statusu, terminu, kalendarza ani danych rezerwacji.</p>
<?php if($recent): ?><div class="wide"><table><thead><tr><th>Rezerwacja</th><th>Status</th><th>Operacja</th></tr></thead><tbody>
<?php foreach($recent as $row): $isConfirmed=$row['status']==='confirmed'; ?>
<tr><td><a href="/admin/reservation.php?id=<?=(int)$row['id']?>"><?=pm12_h($row['reservation_code'])?></a> <span class="dim">· <?=pm12_h($row['reservation_date'])?> <?=pm12_h(substr((string)$row['reservation_time'],0,5))?></span></td><td><?= $isConfirmed?'Potwierdzona':'Nowa' ?></td>
<td><form method="post" class="line"><?=csrf_field()?><input type="hidden" name="action" value="<?= $isConfirmed?'resend_confirmed':'resend_received' ?>"><input type="hidden" name="reservation_id" value="<?=(int)$row['id']?>">
<label class="small"><input type="checkbox" name="confirm_send" value="TAK" required style="min-width:0"> Potwierdzam wysyłkę</label>
<button type="submit" class="secondary"><?= $isConfirmed?'Wyślij potwierdzenie ponownie':'Wyślij potwierdzenie otrzymania' ?></button></form></td></tr>
<?php endforeach; ?></tbody></table></div>
<?php else: ?><p>Brak rezerwacji na liście.</p><?php endif; ?>
<p class="dim small">Lista to maksymalnie 20 ostatnich rezerwacji o statusach Nowa i Potwierdzona. Brak pozycji oznacza tylko, że nie mieści się na tej liście. Błędy są zapisywane w historii rezerwacji.</p>
</section>
<section class="pane"><h2>Ważne</h2><p>Wysłanie autorespondera „otrzymaliśmy prośbę” nie oznacza potwierdzenia stolika. Właściwy e-mail potwierdzający wysyłany jest po decyzji obsługi „Potwierdź”, o ile SMTP jest włączone. W starej wersji aplikacji zmiana statusu nie sygnalizuje jednoznacznie błędu poczty — sprawdź wpisy <code>email_error</code> powyżej.</p>
<p>Po skutecznym teście sprawdź jeszcze folder SPAM i dostarczalność u odbiorcy. Samo <code>SMTP 250</code> to akceptacja przez serwer, nie dowód dostarczenia.</p></section>
</div>
<?php render_admin_footer(); ?>