Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Mime] Fix attached inline part not related #43255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Symfony/Component/Mime/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,12 @@ private function prepareParts(): ?array
foreach ($this->attachments as $attachment) {
foreach ($names as $name) {
if (isset($attachment['part'])) {
if ('inline' === $attachment['part']->getPreparedHeaders()->getHeaderBody('Content-Disposition')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done before the loop over names (to do it even when there is no names being found for <img> tags) and it needs to replace the cid references in the HTML too. Otherwise, the fix is incomplete.

and as said in my comment, we also need a fix for attachments using inline: true without a part already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof maybe I'm missing something, but aren't inline parts handled by this already?

$attachment['inline'] = true;
$inlineParts[$name] = $part = $this->createDataPart($attachment);
$html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html);
$part->setName($part->getContentId());

Or do you mean that someone calls embed but the regex doesn't find the $name in the HTML body so that it drops out of the loop here?

if ($name !== $attachment['name']) {
continue;
}

Because in that case (at least in my uninformed opinion) it seems correct to not make it an inline attachment, since it's actually not embedded anywhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flack no. this handles attachments that are added with a name that matches a name detected in the HTML. It does not handle the case of attachments that are added as inline: true explicitly through ->embed().

And $names is not detecting all usages of CID in the HTML (that's precisely the issue you are marking as closed by this PR, which is not solved by your PR)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof well, I only said this fixes the issue in the second comment of #42921, i.e. the allows me to build a workaround for the original problem in that ticket. It was @fabpot who added "closes #42921" to the description here, don't blame me for that :-)

But I still don't get it: Attachments that are added as inline: true explicitly through ->embed() cannot have $attachment['part'] set:

public function embed($body, string $name = null, string $contentType = null)
{
$this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => true];
return $this;
}

So the handling of those doesn't change in this PR. Of course, if they were ignored before (like in the OP of #42921), they are still ignored. But that seems like a separate issue to me. This PR fixes a bug where you can't add an instantiated DataPart as inline. The other ticket is about the name detection logic being too narrow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the handling of those doesn't change in this PR.

and that's precisely what I'm asking to change, by actually fixing the reported issue instead of fixing only the workaround

$inlineParts[] = $attachment['part'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline parts is an array indexed by name.


continue 2;
}

continue;
}
if ($name !== $attachment['name']) {
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Component/Mime/Tests/EmailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,12 @@ public function testGenerateBody()
// 2 parts only, not 3 (text + embedded image once)
$this->assertCount(2, $parts = $body->getParts());
$this->assertStringMatchesFormat('html content <img src=3D"cid:%s@symfony">', $parts[0]->bodyToString());

$e = (new Email())->from('[email protected]')->to('[email protected]');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be better to create a new test instead of adding one more subtest into this test which does not respect the testing best practices. this has several advantages:

  • the tests will run independently of other tests for the body generation (instead of running only when previous assertions of that god test have passed)
  • the test will be named, which will make it clear what is being tested

$e->html('html content <img src="cid:test.gif">');
$e->attachPart((new DataPart($image, 'test.gif'))->asInline());
$body = $e->getBody();
$this->assertInstanceOf(RelatedPart::class, $body);
}

public function testAttachments()
Expand Down