Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

email attachments break. during the failover retries due to state contamination #57474

Copy link
Copy link
Closed as not planned
@137-rick

Description

@137-rick
Issue body actions

I extend my heartfelt gratitude to the Symfony community for your unwavering dedication and perseverance.

Background

I was using the Laravel 10 mailer along with Symfony Mailer and encountered a bug related to failover functionality when the first mailer failed to send an email.

Failover Configuration

My failover setup is as follows:

Primary Mailer Driver: Mailgun

Secondary Mailer Driver: Amazon SES

Issue Description

When an email fails to send using Mailgun, the system attempts to send the email via Amazon SES. However, I discovered that emails sent through Amazon SES have broken image sources (src), causing the images to not display correctly in the emails.

Package version

symfony/mailer 6.4.8

symfony/mailgun-mailer 7.1.1

Try to Fix

I tried to fix the issue in the file located at vendor/symfony/mailer/Transport/RoundRobinTransport.php.

It worked when I made the following changes:

It seems that the $message attachments are a reference. The clone operation can't properly clone the attachments objects. Therefore, I changed it to use deep_copy to perform a deep clone for each send operation.

I also found that the Mailgun driver changes the attachments' CIDs.

Here is the modified section of the code:

public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
    $exception = null;

    while ($transport = $this->getNextTransport()) {
        try {
            $currentMessage = deep_copy($message); //here i change
            return $transport->send($currentMessage, Envelope::create($currentMessage)); //here i change
        } catch (TransportExceptionInterface $e) {
            $exception ??= new TransportException('All transports failed.');
            $exception->appendDebug(sprintf("Transport \"%s\": %s\n", $transport, $e->getDebug()));
            $this->deadTransports[$transport] = microtime(true);
        }
    }

    throw $exception ?? new TransportException('No transports found.');
}

My Local debug test

When I change the first driver to SMTP, it works well.

I do this before the FailoverTransport class, prior to the first send.

$s = new Symfony\Component\Mailer\SentMessage($message,$envelope);
$s->toString();

The attachment will have the wrong src path.

When I deep copy the message, everything worked well !!!

$current = deep_copy($message)
$s = new Symfony\Component\Mailer\SentMessage($current,$envelope);
$s->toString();

The Next Info for Reference

Here is the first email source.

It's normal and works well.

This will be used to compare with the second email source.

The image source is cid:V9J4rKBvmB and it will be replaced with cid:d3a788f4cbf3a6aab32dcacde69a2e34@symfony, resulting in the following output:

Content-Type: multipart/related; 
... 
\<img id=3D"logo" width=3D"190" height=3D"40" style=3D"width:=
190px; height: 40px; -ms-interpolation-mode: bicubic;" src=3D"cid:d3a788f4cbf3a6aab32dcacde69a2e34@symfony" alt=3D""\> 
....
\--KGCwwzRU
Content-ID: d3a788f4cbf3a6aab32dcacde69a2e34@symfony
Content-Type: image/png; name="d3a788f4cbf3a6aab32dcacde69a2e34@symfony"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
name="d3a788f4cbf3a6aab32dcacde69a2e34@symfony"; filename=V9J4rKBvmB

Here is the second image. The source is incorrect, so it can't be displayed.

The problematic image email source is here:

The image source is cid:L7HALxfBwi.

It should be updated to Content-ID cid:2eabc9240b3557c72ad65e92a60f7f57@symfony, but the result is still cid:L7HALxfBwi.

Content-Type: multipart/mixed;
\<img id=3D"logo" width=3D"190" height=3D"40" style=3D"width:=
190px; height: 40px; -ms-interpolation-mode: bicubic;" src=3D"cid:L7HALxfBwi" alt=3D""\>
...
\--TG5g10Dt
Content-ID: 2eabc9240b3557c72ad65e92a60f7f57@symfony
Content-Type: image/png; name="2eabc9240b3557c72ad65e92a60f7f57@symfony"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
name="2eabc9240b3557c72ad65e92a60f7f57@symfony"; filename=L7HALxfBwi

I found that this issue might be related to the Symfony\Component\Mailer\Transport\AbstractTransport class, specifically in the send function.

$message = clone $message; // this clone haven't clone the inner object var

fake code for my test

I use Symfony Mailer in Laravel 10. Here is some example code (sorry for the demo code):

// Set the mailgun always fail
config()->set('services.mailgun.endpoint', '127.0.0.1');

// Set the mail mailers configuration for failover
config()->set('mail.mailers.failover.mailers', ['mailgun', 'ses']);

// Get the mail manager instance and set the Symfony transporter
$mail_manager = app()->get('mail.manager');
$mail_manager->setSymfonyTransport($mail_manager->createSymfonyTransport(config('mail.mailers.failover')));

// Send the email using the specified driver
Mail::mailer($driver)->send(new MailableView(1, "The $driver mailable view test email"));

// Use Illuminate\Mail\Mailable to define your Mailable class
use Illuminate\Mail\Mailable;

class Mailable extends IlluminateMailable
{
    public function build()
    {
        // Compose the email view
        return $this->view('emails.mailable_view_test.email_view')
                    ->text('emails.mailable_view_test.email_view_text')
                    ->sender('xxx', $name)
                    ->from('xxx', $name)
                    ->replyTo('xxx', $name)
                    ->to('xxx', 'xxx')
                    ->with([
                        'name' => 'xxx',
                        'url' => 'xxx',
                    ])
                    ->subject($this->title);
    }
}

in the blade template of view

<!-- resources/views/emails/mailable_view_test/email_view.blade.php -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ $subject }}</title>
</head>
<body>
    <h1>Hello, {{ $name }}</h1>
    <p>Welcome to our service. Please visit <a href="{{ $url }}">this link</a> for more information.</p>
    <img src="{{ $message->embedData($logo, 'logo.png', 'image/png') }}" alt="Logo">
</body>
</html>

Additional Context

here is the dump of the $message by laravel dd() function, this content to show clone will not clone the #1826 object of $message->attachments

first email var $message dump, this is normal for compare the second wrong one

-attachments: array:1 [
    0 => Symfony\Component\Mime\Part\DataPart^ {#1826 
      -headers: Symfony\Component\Mime\Header\Headers^ {#1829
        -headers: []
        -lineLength: 76
      }
      #_headers: ? Symfony\Component\Mime\Header\Headers
      -body: Symfony\Component\Mime\Part\File^ {#1818
        -path: "/User/rick/xxxx/img/logo-negative.png"
        -filename: null
      }
      -charset: null
      -subtype: "png"
      -disposition: "inline"
      -name: "4nF4PcToTi"
      -encoding: "base64"
      -seekable: null
      #_parent: ? array
      -filename: "4nF4PcToTi"
      -mediaType: "image"
      -cid: null
    }
  ]
  -cachedBody: null

with broken image src of second email $message dump.

-attachments: array:1 [
    0 => Symfony\Component\Mime\Part\DataPart^ {#1826  //object is same 
      -headers: Symfony\Component\Mime\Header\Headers^ {#1829 //object is same 
        -headers: array:1 [
          "content-id" => array:1 [
            0 => Symfony\Component\Mime\Header\IdentificationHeader^ {#1932
              -name: "Content-ID"
              -lineLength: 76
              -lang: null
              -charset: "utf-8"
              -ids: array:1 [
                0 => "9b83b7043798ef148b6b46ac184ca696@symfony"
              ]
              -idsAsAddresses: array:1 [
                0 => Symfony\Component\Mime\Address^ {#1933
                  -address: "9b83b7043798ef148b6b46ac184ca696@symfony"
                  -name: ""
                }
              ]
            }
          ]
        ]
        -lineLength: 76
      }
      #_headers: ? Symfony\Component\Mime\Header\Headers
      -body: Symfony\Component\Mime\Part\File^ {#1818
        -path: "/User/rick/xxxx/img/logo-negative.png"
        -filename: null
      }
      -charset: null
      -subtype: "png"
      -disposition: "inline"
      -name: "7a7a484a463c3667a9e55d610c2f0641@symfony"
      -encoding: "base64"
      -seekable: null
      #_parent: ? array
      -filename: "4nF4PcToTi"
      -mediaType: "image"
      -cid: "7a7a484a463c3667a9e55d610c2f0641@symfony"
    }
  ]
  -cachedBody: null

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.