163 lines
5.1 KiB
PHP
163 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Mail;
|
|
|
|
use App\Models\MailingBounce;
|
|
use Illuminate\Mail\Events\MessageSending;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Symfony\Component\Mime\Email;
|
|
|
|
class UniversalBounceHandler
|
|
{
|
|
/**
|
|
* Handle the message sending event to check for suppressed recipients
|
|
*/
|
|
public function handle(MessageSending $event): bool
|
|
{
|
|
$message = $event->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);
|
|
}
|
|
}
|
|
} |