Initial commit
This commit is contained in:
163
app/Mail/UniversalBounceHandler.php
Normal file
163
app/Mail/UniversalBounceHandler.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user