This commit is contained in:
2026-08-07 12:27:52 +03:00
parent 5c6064ba9e
commit 0e12d7489f
+42 -7
View File
@@ -12,7 +12,31 @@ function mail_env(string $key): string
return ($value === 'null') ? '' : $value; return ($value === 'null') ? '' : $value;
} }
/** Never surfaces the SMTP error to the caller: it can leak host and credentials. */ /**
* PHPMailer compares SMTPSecure with ===, so "SSL" from .env would silently fall
* back to an unencrypted socket and hang on an implicit-TLS port. Normalise, and
* infer the usual encryption from the port when MAIL_ENCRYPTION is not set.
*/
function mail_encryption(int $port): string
{
$enc = strtolower(mail_env('MAIL_ENCRYPTION'));
if ($enc === 'ssl' || $enc === 'tls') {
return $enc;
}
if ($enc !== '' && $enc !== 'none') {
error_log("mail: unknown MAIL_ENCRYPTION '{$enc}', falling back to the port default");
} elseif ($enc === 'none') {
return '';
}
return in_array($port, [465, 2465], true) ? 'ssl' : 'tls';
}
/**
* Never surfaces the SMTP error to the caller: it can leak host and credentials.
* The error log gets the connection details (minus the password) instead — without
* them a failure is indistinguishable from a wrong recipient address.
*/
function send_mail(string $to, string $subject, string $html): bool function send_mail(string $to, string $subject, string $html): bool
{ {
if (!class_exists(PHPMailer::class)) { if (!class_exists(PHPMailer::class)) {
@@ -20,21 +44,26 @@ function send_mail(string $to, string $subject, string $html): bool
return false; return false;
} }
$host = mail_env('MAIL_HOST') ?: 'localhost';
$port = (int) (mail_env('MAIL_PORT') ?: 587);
$user = mail_env('MAIL_USERNAME');
$enc = mail_encryption($port);
$mail = new PHPMailer(true); $mail = new PHPMailer(true);
try { try {
$mail->isSMTP(); $mail->isSMTP();
$mail->Host = mail_env('MAIL_HOST') ?: 'localhost'; $mail->Host = $host;
$mail->Port = (int) (mail_env('MAIL_PORT') ?: 587); $mail->Port = $port;
$mail->CharSet = 'UTF-8'; $mail->CharSet = 'UTF-8';
// Default is 300s: an unreachable SMTP would otherwise stall registration. // Default is 300s: an unreachable SMTP would otherwise stall registration.
$mail->Timeout = 10; $mail->Timeout = 10;
if ($user = mail_env('MAIL_USERNAME')) { if ($user !== '') {
$mail->SMTPAuth = true; $mail->SMTPAuth = true;
$mail->Username = $user; $mail->Username = $user;
$mail->Password = mail_env('MAIL_PASSWORD'); $mail->Password = mail_env('MAIL_PASSWORD');
} }
if ($enc = mail_env('MAIL_ENCRYPTION')) { if ($enc !== '') {
$mail->SMTPSecure = $enc; $mail->SMTPSecure = $enc;
} }
@@ -47,9 +76,15 @@ function send_mail(string $to, string $subject, string $html): bool
$mail->Subject = $subject; $mail->Subject = $subject;
$mail->Body = $html; $mail->Body = $html;
return $mail->send(); if ($mail->send()) {
return true;
}
error_log("mail failed to {$to} via {$enc}://{$user}@{$host}:{$port}: {$mail->ErrorInfo}");
return false;
} catch (Throwable $e) { } catch (Throwable $e) {
error_log('mail failed: ' . $e->getMessage()); error_log("mail failed to {$to} via {$enc}://{$user}@{$host}:{$port}: " . $e->getMessage());
return false; return false;
} }
} }