mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-07 03:29:19 +02:00
Handle SMTP 550 recipient-rejected errors with structured SmtpSendException
- Add SmtpSendException with smtpCode/smtpResponse/isRecipientRejected() - smtpSend() $expect closure throws SmtpSendException (with code) instead of RuntimeException - SmtpRelay::send() re-throws SmtpSendException so callers can inspect it - request-access.php (new): catch 550 → roll back token+approval, return HTTP 422 with FR user message - request-access.php (resend): catch 550 → HTTP 422 instead of silently claiming success - StudentEmail::sendConfirmation(): catch SmtpSendException → log+false (submission not aborted) - admin/actions/access-request.php: catch SmtpSendException post-approval → flash warning (recipient-rejected vs transient)
This commit is contained in:
@@ -16,6 +16,31 @@ class SmtpProbeException extends \RuntimeException {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Structured exception for SMTP send failures.
|
||||
* Carries the 3-digit SMTP response code and the full server response.
|
||||
*
|
||||
* Notable codes:
|
||||
* 550 / 551 / 553 — recipient address rejected (unknown user, policy, etc.)
|
||||
* 421 / 450 / 451 — transient failures (try again later)
|
||||
* 530 / 535 — authentication failure
|
||||
*/
|
||||
class SmtpSendException extends \RuntimeException {
|
||||
public function __construct(
|
||||
string $message,
|
||||
public readonly int $smtpCode = 0,
|
||||
public readonly string $smtpResponse = ''
|
||||
) {
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
/** True when the SMTP server permanently rejected the recipient address. */
|
||||
public function isRecipientRejected(): bool
|
||||
{
|
||||
return in_array($this->smtpCode, [550, 551, 552, 553, 554], true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP Relay — credentials stored in the DB, sending via a native PHP socket
|
||||
* client (no external dependencies).
|
||||
@@ -391,6 +416,9 @@ class SmtpRelay {
|
||||
|
||||
try {
|
||||
return self::smtpSend($s, $to, $rawMessage);
|
||||
} catch (SmtpSendException $e) {
|
||||
error_log('[SmtpRelay] ' . $e->getMessage());
|
||||
throw $e; // propagate structured exception so callers can react
|
||||
} catch (\Throwable $e) {
|
||||
error_log('[SmtpRelay] ' . $e->getMessage());
|
||||
return false;
|
||||
@@ -463,7 +491,12 @@ class SmtpRelay {
|
||||
$expect = function (string $cmd, string $code) use ($send): string {
|
||||
$resp = $send($cmd);
|
||||
if (strncmp($resp, $code, strlen($code)) !== 0) {
|
||||
throw new \RuntimeException("SMTP unexpected response to '{$cmd}': {$resp}");
|
||||
$respCode = (int) substr(trim($resp), 0, 3);
|
||||
throw new SmtpSendException(
|
||||
"SMTP unexpected response to '{$cmd}': " . trim($resp),
|
||||
$respCode,
|
||||
trim($resp)
|
||||
);
|
||||
}
|
||||
return $resp;
|
||||
};
|
||||
|
||||
@@ -99,7 +99,13 @@ class StudentEmail {
|
||||
$subject = 'Merci — ton TFE a bien été enregistré';
|
||||
$htmlBody = self::buildHtml($thesis);
|
||||
|
||||
$result = SmtpRelay::send($db, $to, $subject, $htmlBody);
|
||||
try {
|
||||
$result = SmtpRelay::send($db, $to, $subject, $htmlBody);
|
||||
} catch (SmtpSendException $e) {
|
||||
// Confirmation email failure must not abort the successful submission.
|
||||
error_log("[StudentEmail] SMTP error sending to {$to} for thesis #{$thesisId}: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
error_log("[StudentEmail] Confirmation sent to {$to} for thesis #{$thesisId}");
|
||||
|
||||
Reference in New Issue
Block a user