message; // Extract all recipients from the message $allRecipients = $this->extractAllRecipients($message); $suppressedRecipients = []; // Check each recipient for suppression foreach ($allRecipients as $email) { if (MailingBounce::isSuppressed($email)) { $suppressedRecipients[] = $email; } } // If any recipients are suppressed, log and potentially block the email if (!empty($suppressedRecipients)) { Log::info("Email contains suppressed recipients", [ 'total_recipients' => count($allRecipients), 'suppressed_recipients' => $suppressedRecipients, 'subject' => $message->getSubject(), ]); // If ALL recipients are suppressed, block the email entirely if (count($suppressedRecipients) === count($allRecipients)) { Log::info("All recipients suppressed, blocking email", [ 'suppressed_recipients' => $suppressedRecipients, 'subject' => $message->getSubject(), ]); return false; // Block the email } // If only some recipients are suppressed, remove them from the message $this->removeSuppressedRecipients($message, $suppressedRecipients); } // Add bounce tracking headers to all outgoing emails $this->addBounceTrackingHeaders($message); return true; // Allow the email to be sent } /** * Extract all recipient email addresses from the message */ protected function extractAllRecipients($message): array { $recipients = []; // Get TO recipients if ($message->getTo()) { foreach ($message->getTo() as $address) { $recipients[] = $address->getAddress(); } } // Get CC recipients if ($message->getCc()) { foreach ($message->getCc() as $address) { $recipients[] = $address->getAddress(); } } // Get BCC recipients if ($message->getBcc()) { foreach ($message->getBcc() as $address) { $recipients[] = $address->getAddress(); } } return array_unique($recipients); } /** * Remove suppressed recipients from the message */ protected function removeSuppressedRecipients($message, array $suppressedEmails): void { // Remove from TO recipients $toAddresses = $message->getTo(); if ($toAddresses) { $filteredTo = []; foreach ($toAddresses as $address) { if (!in_array($address->getAddress(), $suppressedEmails)) { $filteredTo[] = $address; } } $message->to(...$filteredTo); } // Remove from CC recipients $ccAddresses = $message->getCc(); if ($ccAddresses) { $filteredCc = []; foreach ($ccAddresses as $address) { if (!in_array($address->getAddress(), $suppressedEmails)) { $filteredCc[] = $address; } } $message->cc(...$filteredCc); } // Remove from BCC recipients $bccAddresses = $message->getBcc(); if ($bccAddresses) { $filteredBcc = []; foreach ($bccAddresses as $address) { if (!in_array($address->getAddress(), $suppressedEmails)) { $filteredBcc[] = $address; } } $message->bcc(...$filteredBcc); } } /** * Add bounce tracking headers to outgoing emails */ protected function addBounceTrackingHeaders($message): void { $bounceEmail = timebank_config('mailing.bounce_address'); if (!$bounceEmail) { return; } if ($message instanceof Email) { $headers = $message->getHeaders(); // Add Return-Path for bounce routing (if not already set) if (!$headers->has('Return-Path')) { $headers->addPathHeader('Return-Path', $bounceEmail); } // Add bounce tracking headers $headers->addTextHeader('X-Bounce-Tracking', 'universal'); $headers->addTextHeader('X-Bounce-Handler', 'timebank-cc'); // Add recipient tracking for the first recipient (for bounce processing) $recipients = $this->extractAllRecipients($message); if (!empty($recipients)) { $headers->addTextHeader('X-Primary-Recipient', $recipients[0]); } // Set Return-Path on the message $message->returnPath($bounceEmail); } } }