Lisää tarkka virheviesti SMTP-lähetykseen debuggausta varten

Virheviesti palautetaan nyt frontendille tarkan SMTP-vaiheen
kera (connect, STARTTLS, AUTH, MAIL FROM, RCPT TO, DATA, send),
jotta nähdään missä kohtaa lähetys epäonnistuu.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 23:30:28 +02:00
parent ff2613ff01
commit 6c1ee4e0d8

41
api.php
View File

@@ -599,6 +599,7 @@ function sendTicketMail(string $to, string $subject, string $body, string $inRep
// Jos mailboxilla on SMTP-asetukset, käytä SMTP:tä
$smtpHost = $mailbox['smtp_host'] ?? '';
error_log("MAIL DEBUG: to={$to} smtpHost={$smtpHost} from={$fromEmail} mailbox_keys=" . implode(',', array_keys($mailbox ?? [])));
if ($smtpHost !== '') {
return sendViaSMTP($to, $subject, $body, $fromEmail, $fromName, $inReplyTo, $references, $mailbox, $cc);
}
@@ -618,6 +619,9 @@ function sendTicketMail(string $to, string $subject, string $body, string $inRep
return mail($to, $subject, $body, $headers, '-f ' . $fromEmail);
}
/** @var string|null Viimeisin SMTP-virhe (palautetaan frontendille) */
$GLOBALS['smtp_last_error'] = null;
function sendViaSMTP(string $to, string $subject, string $body, string $fromEmail, string $fromName, string $inReplyTo, string $references, array $mailbox, string $cc): bool {
$host = $mailbox['smtp_host'];
$port = (int)($mailbox['smtp_port'] ?? 587);
@@ -628,6 +632,14 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
$timeout = 15;
$errno = 0; $errstr = '';
$fail = function(string $step, string $detail) use (&$fp) {
$msg = "SMTP $step: $detail";
error_log($msg);
$GLOBALS['smtp_last_error'] = $msg;
if (isset($fp) && $fp) fclose($fp);
return false;
};
// Yhteys
if ($encryption === 'ssl') {
$fp = @stream_socket_client("ssl://{$host}:{$port}", $errno, $errstr, $timeout);
@@ -635,13 +647,12 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
$fp = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, $timeout);
}
if (!$fp) {
error_log("SMTP connect failed: {$errstr} ({$errno})");
return false;
return $fail('connect', "{$errstr} ({$errno}) — host={$host}:{$port} enc={$encryption}");
}
stream_set_timeout($fp, $timeout);
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '220') { fclose($fp); error_log("SMTP banner: $resp"); return false; }
if (substr($resp, 0, 3) !== '220') return $fail('banner', trim($resp));
// EHLO
fwrite($fp, "EHLO " . gethostname() . "\r\n");
@@ -655,9 +666,9 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
if ($encryption === 'tls') {
fwrite($fp, "STARTTLS\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '220') { fclose($fp); error_log("SMTP STARTTLS: $resp"); return false; }
$crypto = stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT);
if (!$crypto) { fclose($fp); error_log("SMTP TLS negotiation failed"); return false; }
if (substr($resp, 0, 3) !== '220') return $fail('STARTTLS', trim($resp));
$crypto = @stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT);
if (!$crypto) return $fail('TLS', 'TLS negotiation failed');
// EHLO uudelleen TLS:n jälkeen
fwrite($fp, "EHLO " . gethostname() . "\r\n");
while ($line = fgets($fp, 512)) {
@@ -669,19 +680,19 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
if ($user !== '') {
fwrite($fp, "AUTH LOGIN\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '334') { fclose($fp); error_log("SMTP AUTH: $resp"); return false; }
if (substr($resp, 0, 3) !== '334') return $fail('AUTH', trim($resp));
fwrite($fp, base64_encode($user) . "\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '334') { fclose($fp); error_log("SMTP AUTH user: $resp"); return false; }
if (substr($resp, 0, 3) !== '334') return $fail('AUTH user', trim($resp) . " (user={$user})");
fwrite($fp, base64_encode($pass) . "\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '235') { fclose($fp); error_log("SMTP AUTH pass: $resp"); return false; }
if (substr($resp, 0, 3) !== '235') return $fail('AUTH pass', trim($resp));
}
// MAIL FROM
fwrite($fp, "MAIL FROM:<{$fromEmail}>\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '250') { fclose($fp); error_log("SMTP MAIL FROM: $resp"); return false; }
if (substr($resp, 0, 3) !== '250') return $fail('MAIL FROM', trim($resp) . " (from={$fromEmail})");
// RCPT TO
$allRecipients = array_filter(array_map('trim', explode(',', $to)));
@@ -692,14 +703,14 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
fwrite($fp, "RCPT TO:<{$rcpt}>\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '250' && substr($resp, 0, 3) !== '251') {
fclose($fp); error_log("SMTP RCPT TO: $resp"); return false;
return $fail('RCPT TO', trim($resp) . " (rcpt={$rcpt})");
}
}
// DATA
fwrite($fp, "DATA\r\n");
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '354') { fclose($fp); error_log("SMTP DATA: $resp"); return false; }
if (substr($resp, 0, 3) !== '354') return $fail('DATA', trim($resp));
// Rakennetaan viesti
$messageId = '<' . uniqid('msg_', true) . '@' . (explode('@', $fromEmail)[1] ?? 'localhost') . '>';
@@ -722,7 +733,7 @@ function sendViaSMTP(string $to, string $subject, string $body, string $fromEmai
fwrite($fp, $msg);
$resp = fgets($fp, 512);
if (substr($resp, 0, 3) !== '250') { fclose($fp); error_log("SMTP send: $resp"); return false; }
if (substr($resp, 0, 3) !== '250') return $fail('send', trim($resp));
// QUIT
fwrite($fp, "QUIT\r\n");
@@ -2275,7 +2286,9 @@ switch ($action) {
if (!$sent) {
http_response_code(500);
echo json_encode(['error' => 'Sähköpostin lähetys epäonnistui']);
$smtpErr = $GLOBALS['smtp_last_error'] ?? '';
$detail = $smtpErr ? " ({$smtpErr})" : '';
echo json_encode(['error' => "Sähköpostin lähetys epäonnistui{$detail}"]);
break 2;
}