| 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 : |
<?php
declare(strict_types=1);
/** POLANA V17.2: newsletter with double opt-in. Loaded by patched newsletter.php and newsletter_subscribe(). */
function pv172_domain(): string { return 'https://mojapolanasmakow.pl'; }
function pv172_support_mail(): string { return '[email protected]'; }
function pv172_upgrade_schema(): void {
newsletter_install_table();
$db = newsletter_db();
$columns = [];
foreach ($db->query('SHOW COLUMNS FROM newsletter_subscribers')->fetchAll(PDO::FETCH_ASSOC) as $column) {
$columns[(string)$column['Field']] = true;
}
$missing = [];
if (!isset($columns['verify_token'])) $missing[] = 'ADD COLUMN verify_token CHAR(64) NULL';
if (!isset($columns['verified_at'])) $missing[] = 'ADD COLUMN verified_at DATETIME NULL';
if (!isset($columns['verification_sent_at'])) $missing[] = 'ADD COLUMN verification_sent_at DATETIME NULL';
if ($missing) $db->exec('ALTER TABLE newsletter_subscribers ' . implode(', ', $missing));
}
function pv172_valid_email(string $email): string {
$email = mb_strtolower(trim($email), 'UTF-8');
return (strlen($email) <= 190 && filter_var($email, FILTER_VALIDATE_EMAIL)) ? $email : '';
}
function pv172_mail_html(string $heading, string $body): string {
return '<!doctype html><html lang="pl"><head><meta charset="utf-8"></head>' .
'<body style="margin:0;padding:25px;background:#f4f1ea;font-family:Arial,sans-serif;color:#20382b">' .
'<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td align="center">' .
'<table role="presentation" style="max-width:580px;background:#fff;border-radius:15px" width="100%">' .
'<tr><td style="padding:32px"><h2 style="color:#1a5c42">' . htmlspecialchars($heading, ENT_QUOTES, 'UTF-8') .
'</h2><div style="line-height:1.7">' . $body . '</div><p style="font-size:12px;color:#737373;margin-top:30px">' .
'Moja Polana Smaków · Kraków</p></td></tr></table></td></tr></table></body></html>';
}
function pv172_subscribe(string $email, string $name = '', string $source = 'footer'): array {
$email = pv172_valid_email($email);
if ($email === '') return ['ok'=>false, 'message'=>'Podaj poprawny adres e-mail.'];
$generic = 'Jeśli ten adres wymaga potwierdzenia, wyślemy wiadomość z linkiem. Sprawdź też folder SPAM.';
// Never add an unverified subscriber to the active marketing audience.
if (setting('smtp_enabled', '0') !== '1' || !smtp_password_is_set() || smtp_password() === '') {
return ['ok'=>false, 'message'=>'Zapis chwilowo niedostępny. Spróbuj ponownie później.'];
}
pv172_upgrade_schema();
$db = newsletter_db();
$stmt = $db->prepare('SELECT id, status, verify_token, verification_sent_at FROM newsletter_subscribers WHERE email=? LIMIT 1');
$stmt->execute([$email]);
$existing = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
if ($existing && (string)$existing['status'] === 'active') {
return ['ok'=>true, 'message'=>$generic];
}
// Avoid mail bombing; a pending confirmation can be resent after five minutes.
if ($existing && !empty($existing['verification_sent_at']) && (time() - strtotime((string)$existing['verification_sent_at']) < 300)) {
return ['ok'=>true, 'message'=>$generic];
}
$token = bin2hex(random_bytes(32));
$name = mb_substr(trim($name), 0, 150, 'UTF-8');
$source = mb_substr(preg_replace('/[^a-z0-9_-]/i', '', $source) ?: 'footer', 0, 50);
$ipHash = newsletter_ip_hash();
$agent = mb_substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255, 'UTF-8');
if ($existing) {
$up = $db->prepare('UPDATE newsletter_subscribers SET status="pending", name=?, source=?, consent_at=NOW(), consent_ip_hash=?, user_agent=?, verify_token=?, verification_sent_at=NULL WHERE id=?');
$up->execute([$name ?: null, $source, $ipHash, $agent ?: null, $token, $existing['id']]);
} else {
$up = $db->prepare('INSERT INTO newsletter_subscribers (email,name,source,status,consent_at,consent_ip_hash,user_agent,unsubscribe_token,verify_token) VALUES (?,?,?,"pending",NOW(),?,?,?,?)');
$up->execute([$email, $name ?: null, $source, $ipHash, $agent ?: null, bin2hex(random_bytes(32)), $token]);
}
$url = pv172_domain() . '/newsletter.php?confirm=' . rawurlencode($token);
$safeUrl = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
$html = pv172_mail_html('Potwierdź zapis do newslettera',
'<p>Otrzymaliśmy prośbę o zapis na newsletter Mojej Polany Smaków.</p>' .
'<p><a href="'.$safeUrl.'" style="display:inline-block;background:#1a6347;color:#fff;text-decoration:none;padding:14px 20px;border-radius:10px">Potwierdź adres e-mail</a></p>' .
'<p>Nie zapisywałeś(-aś) się? Zignoruj tę wiadomość — bez potwierdzenia nie będziemy wysyłać newslettera.</p>');
$text = "Potwierdź zapis do newslettera Mojej Polany Smaków:\n" . $url . "\n\nJeśli to nie Ty, zignoruj wiadomość.";
try {
smtp_send_mail($email, 'Potwierdź zapis — Moja Polana Smaków', $html, $text, pv172_support_mail());
$db->prepare('UPDATE newsletter_subscribers SET verification_sent_at=NOW() WHERE email=? AND verify_token=?')->execute([$email,$token]);
} catch (Throwable $e) {
// Keep as pending; do not claim success if SMTP rejected the send.
error_log('[POLANA NEWSLETTER v172 mail] ' . $e->getMessage());
return ['ok'=>false,'message'=>'Nie udało się wysłać potwierdzenia. Spróbuj ponownie później.'];
}
return ['ok'=>true,'message'=>$generic];
}
function pv172_confirm(string $token): bool {
if (!preg_match('/^[a-f0-9]{64}$/D', $token)) return false;
pv172_upgrade_schema();
$db = newsletter_db();
$db->beginTransaction();
try {
$q = $db->prepare('SELECT id, email, unsubscribe_token FROM newsletter_subscribers WHERE verify_token=? AND status="pending" LIMIT 1 FOR UPDATE');
$q->execute([$token]);
$row = $q->fetch(PDO::FETCH_ASSOC);
if (!$row) { $db->rollBack(); return false; }
$up = $db->prepare('UPDATE newsletter_subscribers SET status="active", verified_at=NOW(), withdrawn_at=NULL, verify_token=NULL WHERE id=? AND verify_token=?');
$up->execute([$row['id'], $token]);
$db->commit();
} catch (Throwable $e) {
if ($db->inTransaction()) $db->rollBack();
throw $e;
}
// Confirmation succeeds even if the optional welcome email fails.
if (setting('smtp_enabled', '0') === '1') {
try {
$url = pv172_domain() . '/newsletter.php?unsubscribe=' . rawurlencode((string)$row['unsubscribe_token']);
$body = "Dziękujemy za potwierdzenie zapisu.\n\nWypisz się w dowolnym momencie: " . $url;
$html = pv172_mail_html('Witamy na Polanie!', '<p>Twój adres został potwierdzony. Będziemy pisać o menu, nowościach i wydarzeniach.</p><p><a href="'.htmlspecialchars($url, ENT_QUOTES, 'UTF-8').'">Wypisz się z newslettera</a></p>');
smtp_send_mail((string)$row['email'], 'Witamy w newsletterze — Moja Polana Smaków', $html, $body, pv172_support_mail());
} catch (Throwable $e) { error_log('[POLANA NEWSLETTER v172 welcome] ' . $e->getMessage()); }
}
return true;
}