From 609bd5dd34e958eea3cd31854f036c68c88bc639 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sun, 1 Feb 2026 20:09:58 +0100 Subject: [PATCH 1/3] Update compose to set correct permissions for data directory --- compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compose.yaml b/compose.yaml index 2a31ee8..468fffc 100644 --- a/compose.yaml +++ b/compose.yaml @@ -9,3 +9,5 @@ services: - ./custom:/var/www/custom:z ports: - "4040:80" + command: > + bash -c "chown -R www-data:www-data /var/www/custom/data && apache2-foreground" From 798bf268aae4a10cf5cdc5f552de5a3e91d4f0c4 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sun, 1 Feb 2026 20:10:10 +0100 Subject: [PATCH 2/3] Add petition-specific SMTP configuration support Allow separate SMTP account for petition emails to improve deliverability through proper SPF/DKIM configuration matching the from address --- custom/petition-cli.php | 16 ++++++++---- custom/plugins/page/petition-form.php | 36 ++++++++++++++++++--------- custom/smtp-config.php.example | 11 +++++++- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/custom/petition-cli.php b/custom/petition-cli.php index 7a746f8..f409ca5 100755 --- a/custom/petition-cli.php +++ b/custom/petition-cli.php @@ -266,8 +266,14 @@ function sendEmail(string $to, string $toName, string $subject, string $body): a $fromEmail = $config['petition']['from_email'] ?? $config['from_email']; $fromName = $config['petition']['from_name'] ?? $config['from_name']; + // Get petition-specific SMTP settings (allows separate SMTP account for better deliverability) + $smtpHost = $config['petition']['host'] ?? $config['host']; + $smtpPort = $config['petition']['port'] ?? $config['port']; + $smtpUser = $config['petition']['username'] ?? $config['username']; + $smtpPass = $config['petition']['password'] ?? $config['password']; + // Pre-flight check - $fp = @fsockopen($config['host'], $config['port'], $errno, $errstr, 10); + $fp = @fsockopen($smtpHost, $smtpPort, $errno, $errstr, 10); if (!$fp) { return ['success' => false, 'error' => "Tilkobling feilet: {$errno} - {$errstr}"]; } @@ -278,10 +284,10 @@ function sendEmail(string $to, string $toName, string $subject, string $body): a $mail = new \codeworxtech\PHPMailerLite\PHPMailerLite(); - $mail->SetSMTPhost($config['host']); - $mail->SetSMTPport($config['port']); - $mail->SetSMTPuser($config['username']); - $mail->SetSMTPpass($config['password']); + $mail->SetSMTPhost($smtpHost); + $mail->SetSMTPport($smtpPort); + $mail->SetSMTPuser($smtpUser); + $mail->SetSMTPpass($smtpPass); $mail->SetSender([$fromEmail => $fromName]); $mail->AddRecipient([$to => $toName]); diff --git a/custom/plugins/page/petition-form.php b/custom/plugins/page/petition-form.php index 6ece4dd..f650744 100644 --- a/custom/plugins/page/petition-form.php +++ b/custom/plugins/page/petition-form.php @@ -713,11 +713,17 @@ function petitionSendConfirmationEmailInternal(array $data, string $confirmUrl, $subject = petitionT($ctx, 'petition', 'email_subject'); + // Get petition-specific SMTP settings (allows separate SMTP account for better deliverability) + $smtpHost = $config['petition']['host'] ?? $config['host']; + $smtpPort = $config['petition']['port'] ?? $config['port']; + $smtpUser = $config['petition']['username'] ?? $config['username']; + $smtpPass = $config['petition']['password'] ?? $config['password']; + // Pre-flight check - $fp = @fsockopen($config['host'], $config['port'], $errno, $errstr, 10); + $fp = @fsockopen($smtpHost, $smtpPort, $errno, $errstr, 10); if (!$fp) { $errorMessage = "Connection failed: {$errno} - {$errstr}"; - error_log("Petition SMTP pre-flight failed: {$config['host']}:{$config['port']} - {$errno} - {$errstr}"); + error_log("Petition SMTP pre-flight failed: {$smtpHost}:{$smtpPort} - {$errno} - {$errstr}"); return false; } fclose($fp); @@ -727,10 +733,10 @@ function petitionSendConfirmationEmailInternal(array $data, string $confirmUrl, $mail = new \codeworxtech\PHPMailerLite\PHPMailerLite(); - $mail->SetSMTPhost($config['host']); - $mail->SetSMTPport($config['port']); - $mail->SetSMTPuser($config['username']); - $mail->SetSMTPpass($config['password']); + $mail->SetSMTPhost($smtpHost); + $mail->SetSMTPport($smtpPort); + $mail->SetSMTPuser($smtpUser); + $mail->SetSMTPpass($smtpPass); $recipientName = $data['firstname'] . ' ' . $data['surname']; $mail->SetSender([$fromEmail => $fromName]); @@ -859,11 +865,17 @@ function petitionSendThankYouEmailInternal(string $token, string $deleteUrl, str $subject = petitionT($ctx, 'petition', 'email_thankyou_subject'); + // Get petition-specific SMTP settings (allows separate SMTP account for better deliverability) + $smtpHost = $config['petition']['host'] ?? $config['host']; + $smtpPort = $config['petition']['port'] ?? $config['port']; + $smtpUser = $config['petition']['username'] ?? $config['username']; + $smtpPass = $config['petition']['password'] ?? $config['password']; + // Pre-flight check - $fp = @fsockopen($config['host'], $config['port'], $errno, $errstr, 10); + $fp = @fsockopen($smtpHost, $smtpPort, $errno, $errstr, 10); if (!$fp) { $errorMessage = "Connection failed: {$errno} - {$errstr}"; - error_log("Petition SMTP pre-flight failed: {$config['host']}:{$config['port']} - {$errno} - {$errstr}"); + error_log("Petition SMTP pre-flight failed: {$smtpHost}:{$smtpPort} - {$errno} - {$errstr}"); return false; } fclose($fp); @@ -873,10 +885,10 @@ function petitionSendThankYouEmailInternal(string $token, string $deleteUrl, str $mail = new \codeworxtech\PHPMailerLite\PHPMailerLite(); - $mail->SetSMTPhost($config['host']); - $mail->SetSMTPport($config['port']); - $mail->SetSMTPuser($config['username']); - $mail->SetSMTPpass($config['password']); + $mail->SetSMTPhost($smtpHost); + $mail->SetSMTPport($smtpPort); + $mail->SetSMTPuser($smtpUser); + $mail->SetSMTPpass($smtpPass); $recipientName = $signature['firstname'] . ' ' . $signature['surname']; $mail->SetSender([$fromEmail => $fromName]); diff --git a/custom/smtp-config.php.example b/custom/smtp-config.php.example index 4506108..6c7fcab 100644 --- a/custom/smtp-config.php.example +++ b/custom/smtp-config.php.example @@ -17,9 +17,18 @@ return [ 'to_email' => 'email@example.com', 'to_name' => 'Stopp Lidelsen', - // Petition-specific overrides (optional) + // Petition-specific settings (optional) // If not set, the default values above will be used + // + // IMPORTANT: For better email deliverability, use a separate SMTP account + // for petitions where the from_email matches the SMTP username. + // This ensures SPF/DKIM/DMARC checks pass. 'petition' => [ + // Separate SMTP account (recommended for deliverability) + // 'host' => 'smtp.example.com', + // 'port' => 587, + // 'username' => 'underskrifter@example.com', + // 'password' => 'petition-smtp-password', // 'from_email' => 'underskrifter@example.com', // 'from_name' => 'Underskriftskampanje', ], From bb82e9a4b952a42207d842324fe0b68eb8522965 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sun, 1 Feb 2026 20:10:27 +0100 Subject: [PATCH 3/3] Update SMTP host validation for third-party services Simplify validation to check DNS records instead of MX records Remove redundant port 80 checks that fail for mail-only subdomains Maintain compatibility with services like Mailgun and SendGrid --- custom/vendor/PHPMailer.Lite.php | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/custom/vendor/PHPMailer.Lite.php b/custom/vendor/PHPMailer.Lite.php index 8a15dde..925f275 100644 --- a/custom/vendor/PHPMailer.Lite.php +++ b/custom/vendor/PHPMailer.Lite.php @@ -709,18 +709,15 @@ class PHPMailerLite } $tld = substr($url, strpos($url, ".") + 1); if ($validate === "is_valid") { - $url = $tld; + // Just check if the SMTP host is reachable via DNS (A record) + // The original check compared SMTP host to MX records which breaks + // third-party SMTP services like Mailgun, SendGrid, etc. + return checkdnsrr($url, 'A') || checkdnsrr($url, 'AAAA'); } if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $tld, $match)) { getmxrr($match['domain'], $mx_details); if (is_array($mx_details) && count($mx_details) > 0) { - if ($validate === "is_valid") { - if ($url == reset($mx_details)) { - return true; - } - } else { - return reset($mx_details); - } + return reset($mx_details); } } return false; @@ -961,12 +958,8 @@ class PHPMailerLite $rz = false; } if ($rz) { - $rz = false; - $check = @fsockopen($domn, 80, $errno, $errstr, 1); - if ($check) { - $rz = true; - @fclose($check); - } + // Check DNS records instead of port 80 (mail-only subdomains don't have web servers) + $rz = (checkdnsrr($domn, 'MX') || checkdnsrr($domn, 'A')); } if ($rz) { $rz = (bool) preg_match('/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD', $email);