Problem/Motivation
Originally, the email sent by the Webform module was delivered as plain text instead of HTML. It turns out the mail headers were broken for no reasons, one space was added before every header line :
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8Bit
X-Mailer: Drupal
Return-Path: <****>
Sender: ****
From: =?utf-8?Q?Th=C3=A9=C3=A2tre?= du Jorat <info@theatredujorat.ch>
Reply-to: ****
After some researches, Webform is not guilty, and everything is running fine until the call to Drupal\Core\Mail\Plugin\Mail\PhpMail :
// For headers, PHP's API suggests that we use CRLF normally,
// but some MTAs incorrectly replace LF with CRLF. See #234403.
$mail_headers = str_replace("\r\n", "\n", $headers->toString());
$mail_subject = str_replace("\r\n", "\n", $mail_subject);
If I understand correctly, the "mail" function is replacing CRLF characters by LF characters, because some MTA may misinterpret CRLF and convert CRLF into CRCRLF.
This trick is working fine with PHP 7.4, but from PHP 8.0 and beyond, it is causing this problem, as also reported here : https://stackoverflow.com/questions/70502065/php8-mail-function-adding-s...
This PHP issue may also be related, as it is mentioned to work with PHP 7.4 but not on PHP 8.0, because of LF char in headers : https://github.com/php/php-src/issues/7902 .
A comment on PHP bug tracker also seems to enforce the fact that PHP 8 would not be tricky-compliant : https://bugs.php.net/bug.php?id=81158
The fix for the bug, which has been reported more than ten years
ago, had deliberately been delayed to PHP 8 (a new major version).
It should probably get a changelog entry, but I am against
reverting this fix. If your mail software replaces CRLF with
CRLFCRLF, or whatever, in mail headers, consider to report that
bug upstream.
I can confirm this behavior is happening on PHP 8.0 and 8.1, but not on PHP 7.4. My MTA is Exim 4.92 but it may not be related.
Steps to reproduce
Use Mailsystem through PhpMail implementation with custom mail headers. Show the source of the received mail, there is a space before each header line.
Proposed resolution
Remove the trick :
$mail_headers = $headers->toString();
or ensure the line ending are CRLF characters by using implode and CRLF characters.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #60 | 3270647-60-d10-only.patch | 3.61 KB | P44T |
| #60 | 3270647-60-d10.patch | 4.56 KB | P44T |
| #59 | 3270647-59-d9.patch | 4.13 KB | P44T |
| #59 | 3270647-59-d10-only.patch | 3.59 KB | P44T |
| #59 | 3270647-59-d10.patch | 4.54 KB | P44T |
Issue fork drupal-3270647
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
cilefen commentedThanks for this. This issue is probably the root cause of a few issues that I just related.
Comment #5
rogerrogers commentedI'm writing in this thread for now as it is the closest to what I can find that matches my problem, but maybe someone can direct me to post elsewhere?Clean Drupal install plus htmlmail, mailsystem, mimemail (for testing). Sending mail works fine up until PHP version 7.4.16. PHP version 7.4.17 doesn't exist, next is PHP 7.4.18. From this version until the current version (7.4.29 at time of this post), mail sending does not work from Drupal. Again, this is Windows Server / FastCGI. This appears to work fine on ubuntu/linux with the latest version of PHP.
PHP 7.4.18 has a fix: https://bugs.php.net/bug.php?id=80710 that deals with a CRLF header injection vulnerability. I'm seeing this thread seems somewhat related. I'm wondering if anyone else is successfully running Drupal 7 on Windows Server / latest PHP and successfully sending mail?
I the problem might be to do with mime_header_encode() function, I see a comment "Using \n as the chunk separator may cause problems on some systems and may have to be changed to \r\n or \r."
This took quite a while to track down, so I'm hoping I can save someone else the pain and maybe even find the culprit.
Removing as I've further tracked this down to the mimemail module, as disabling that module permits Drupal to successfully send a password reset email (for testing purposes), thus core appears to be working fine.
Comment #6
tr commented@RogerRogers: You've got it backwards - Mime Mail other modules such as Weform have this problem on some sites because of core Drupal, not the other way around.
Comment #7
rogerrogers commented@TR, I'm not sure which is root cause of the problem. Whatever the case, when I disable mimemail sending works again. I posted a more thorough explanation here: https://www.drupal.org/project/mimemail/issues/3283706.
Comment #8
rogerrogers commented@TR, I stand corrected. Mimemail is not the only problem, and I'm therefore guessing you might be correct that this is a broader issue. I commented out a few lines in mimemail and was able to get basic emails going, e.g. password resets, but sending an htmlmail test fails.
Comment #9
jshimota01 commentedI'm speechless - after a full day of pulling my hair out - to find it's an ancient problem. I was cleaning up a site design for a client and needed html and couldn't get it working correctly. I ran numerous tests and was nearly given up when I stumbled on this notation. Sure enough, degrading my environment to 7.4 cleared the problem.
Using webforms with MimeMail just gave a text email no matter what I did.
There is a comment in the mimemail project about a modification to a file as a possible workaround, however it didn't help me.
see https://www.drupal.org/project/mimemail/issues/3261886 #2.
I see this marked as closed but I'm unclear about how to solve this, or workaround it, patch it,etc...
Running PHP 8.1.7 d9.4.3 (except now I'm on php 7.4xx)
edit -
Digging further I found this:
]
as well - in a dialog by much smarter people - there was a reference to a potential method to give a temporary solution like giving a php.ini value (slightly different than above).
I tracked down the text I wanted to share : from https://bugs.php.net/bug.php?id=81158
Alternately, you could allow systems to specify the line ending to use in php.ini so that they could configure it together with sendmail. Then just do something like `if (ini_get('sendmail.fix-line-endings') ) $email = str_replace(["\r\n", "\r", "\n"], ini_get('sendmail.line-ending'), $email)` to fix the line endings. Most frameworks use some kind of solution like this, controlling the behavior with configuration.Edit - tried every iteration I could think of, couldn't get it to work setting the Mail line ending. I could affect it, but it didn't fix regardless of value I used (\r\n, \n) :
so my choice is html in emails with the slower 7.4 php or 8.0 with plaintext.
Hope this helps someone
Comment #10
jshimota01 commentedComment #11
slangston commentedWas able to get it to work just by adding a space to a core file.
D 9.4.5
PHP 8.1.8
WebForm 6.1.3
Was getting the same issue. PHP 7.4 worked fine sending an HTML email from WebForm. Switched to PHP 8.1 and the HTML was no longer formatting correctly in the received email. Checked the headers for the issue that people say prevented it (a space being added to the beginning of content-type, etc.), and sure enough, those spaces were there. They can be removed in the core file:
/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
Line 104, add a space AFTER the \r\n and it works!
Before:
$mail_headers = str_replace("\r\n", "\n", $headers->toString());After:
$mail_headers = str_replace("\r\n ", "\n", $headers->toString());Comment #12
jshimota01 commentedExcellent! confirm #11 worked for me!
so I was trying to set a settings.php value before and could tickle it but it never worked:
$settings['mail_line_endings'] = "\r\n ";
would it be possible to figure out a way to do something with a settings.php variable ala {$settings['mail_headers'] = "\r\n ";} or something?
Since a) modding core can be perilous b) each upgrade I'll have to hand edit this... It'd be nice to have a semi-permanent workaround!!!
I'm just happy to get off 7.4 php!
Comment #13
slangston commentedWebForm needs to be updated to work with 8.1, as after I implemented this core file change, then went to EDIT a content page, I received deprecation errors:
Deprecated function: Calling static trait method Drupal\webform\Element\WebformEntityTrait::setOptions is deprecated, it should only be called on a class using the trait in Drupal\webform\Plugin\WebformElement\WebformEntitySelect->setOptions() (line 395 of modules/webform/src/Plugin/WebformElement/WebformEntityReferenceTrait.php).Commented out that line (395) and the error went away, not sure what damage that does, though.
Comment #14
joelseguinIf #11 is in fact the solution - any chance that someone can create a proper patch? I'd be happy to test further as well from there.
I noticed the issue once I moved to PHP 8.1 as well. I'm seeing mail header issues when using MimeMail at this point (haven't tested further yet with other modules such as Swiftmailer).
** @slangston - in regards to the Webform specific issue and PHP 8.1, have you applied the Webform PHP 8.1 patches here:
https://www.drupal.org/project/webform/issues/3283614
Comment #15
tr commentedNo, #11 is not the solution.
Comment #16
jshimota01 commentedAs @tr states, "#11 is not the solution" - I think that was a bit short.
#11 did in fact work - however- there is no doubt this is bigger issue - since moving to 8/8.1 on different platforms gives different results. There will need to be either an override option for those whose environments fail or more logic in the replacement to determine when/if it's needed.
BUT - I now have TWO sites that are fixed by this single character edit. What I've done is set the file to read only on the servers and lock the file after I edit it. when the recent 9.4.5 came out I broke myself inadvertently. This is also a horrible idea, I know, since should the phpmailer.php plugin file get tweaked, I will break again.
Comment #17
joelseguinI can share that after some additional testing that the Symfony Mailer module works well with PHP 8+. I'm not sure if it makes use of Drupal\Core\Mail\Plugin\Mail\PhpMail or not though. The headers certainly look fine and work perfectly. Also, it might be worth mentioning that the module does not use the Mail System module like Mime Mail does.
** For now, I will have to proceed with using Symfony Mailer when bumping up sites to PHP 8+. Some of my projects rely on Mime Mail as it offers better integration with other modules (such as Queue Mail, Views Send) since it has simply been around longer.
Comment #20
larryla commentedAfter many tests outside Drupal on the same server, PHP8 do not tolerate the simple "\n" contrary to PHP7 to add multiple headers with mail() function. It is therefore absolutely necessary to transmit "\r\n" as the header separator, otherwise a space is added to disable the header.
I found the following solution : Replace Into Drupal file /core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php :
$mail_headers = str_replace("\r\n", "\n", $headers->toString());by
$mail_headers = str_replace("\r\r\n", "\r\n", $headers->toString());Comment #21
albert volkman commentedHere's #11 in patch form.
Comment #22
progzy commented@albert, looks like it is #20 not #11.
Comment #23
joelseguin@albert - thanks for the patch. Seems to work nicely on PHP 8.1 site.
Comment #24
cilefen commentedComment #25
slangston commentedStill not fixed with Drupal 9.4.6
Comment #26
cilefen commented@slangston How would it be fixed while this issue is open, in need of review, and not yet committed to Drupal Core?
Comment #27
aad van esschoten commentedPatch #21 seems to work for 9.4.1 on PHP 8.1.
Comment #28
jshimota01 commentedAppreciate the patch - I've tested it now on 2 sites, worked nicely! (PHP 8.1.6 and PHP 8.1.11 both with D9.4.7)
Comment #29
albert volkman commentedHave also tested this on 3 of my sites. Fixed the issue perfectly.
Comment #30
sivaji_ganesh_jojodae commentedChanging the status based on the comments above.
Comment #31
catchI don't think we'll have headers with "\r\r\n" so shouldn't this entire str_replace() call be removed rather than changed to something that will likely do nothing?
Also if we're removing it, we should also remove the comment just above.
No tests with this but I can' think of a test that would actually be useful here.
Comment #32
nitin shrivastava commented@catch made changes as per the comment #31
Comment #33
kim.pepperFix is straightforward and has had multiple reviews.
Comment #34
alexpottDon't we need to do the same thing for
$mail_subject- also I think this is testable.Plus should we be keeping behaviour the same on PHP 7 given that the behaviour is changed in PHP 8 only?
We could also choose to add test coverage of this method here. We can add a
mailfunction in theDrupal\Core\Mail\Plugin\Mailnamespace in order to mock themailfunction.Comment #35
anchal_gupta commentedI have uploaded the patch accordingly to address #34.
Please review it
Comment #36
poker10 commentedThe patch #35 is not correct.
$mail_subjectis already a string, not an object.The patch also contains unrelated test class
ExceptionLoggingSubscriberTest.Comment #37
biigniick commented#11 worked for me
Comment #39
sander edwards van muijen commentedI think it should just be this, right?
Any chance this will be in the next update any time soon? We're running several sites on PHP 7.4 and cannot update to 8.1 because of this issue.
Comment #40
sander edwards van muijen commentedbugfix in previous patch
Comment #41
sander edwards van muijen commentedComment #42
poker10 commentedThe patch in #40 seems to be almost the same as the one in #32 and does not address all points raised by @alexpott from #34. Therefore I think it should stay as Needs work.
Comment #43
tr commentedYes. Exactly what I was just about to say.
Comment #44
mlzrSame error on my website, after update PHP to 8.1 and Drupal to 9.4.9 the 'reply' function didn't work anymore.
I apply'd the patch #40 and this works.
Tnx!
Comment #45
aliparlatti commentedError: Call to undefined function Drupal\Core\Mail\Plugin\Mail\mail() in Drupal\Core\Mail\Plugin\Mail\PhpMail->mail() (line 120 of core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php).
I applied the solution in #40, no change, the error persists.
PHP 8.1
Drupal 9.5.3
Comment #46
c.altosax commentedI assume this issue is still present in Drupal 10 (since it's very similar to Drupal 9.5), but I haven't actually tested Drupal 10 yet. Has anyone confirmed yet whether this is still an issue?
Comment #47
christophedg commentedI believe so... My drupal 10 webform handler is set to use html, but in my emails I see all the html tags.
Comment #48
rclemings commentedPatch in #40 works for me using mime_mail but with htmlmail it's the same as without the patch. Not clear why.
Drupal core 9.5.5
Mime Mail 8.x-1.0-alpha5
HTML Mail 8.x-3.0-alpha3
Comment #49
P44T commentedHere's another patch, which addresses the concern of breaking the current behaviour for PHP < 8 raised by @alexpott in #34. Since Drupal 10 doesn't support PHP versions lower than 8, the check can be removed again in the 10.x branch.
I've tested the patch on a Drupal 9.5.8 site with PHP 8.1 on Debian and it works: the webform module is able to send HTML mails again.
It was also brought up that this needs a unit test, but I could use some help here, as I'm not really a PHP developer anymore these days. When I look at
\Drupal\Tests\Core\Mail\Plugin\Mail\PhpMailTest.php, I see how thedoMail()method was mocked and I could probably verify the arguments passed to the mock somehow, but since the code now checks the constantPHP_VERSIONto determine how to behave, this makes unit testing hard or impossible. Is there some way of injecting the PHP version as a dependency of the PhpMail class? It would seem weird to me write a unit test that makes different assertions based on which PHP version the test runs on, right? If anyone could give me some pointers here, I'd be happy to give it another try. (For Drupal 10, a test would be more straighforward.)Comment #50
tr commentedAs a co-maintainer of HTML Mail, I don't think this observation should be a reason to block or postpone the current issue. The HTML Mail module will not be supported going forward because it relies on a very old and unsupported PECL package. HTML Mail is being abandoned in favor of Mime Mail, and Mime Mail *relies* on this current issue being fixed in core.
Comment #51
andyf commentedThanks all for the work on this so far, here's a first go at a test.
Comment #52
andyf commentedAnd again, slightly more verbose :p
Thanks!
Comment #54
andyf commentedJust wanted to add:
Reply-Tobecause the behavior varies based onsendmail_pathwhich is aPHP_INI_SYSTEMconfig, and I'm not sure the best way to test that, and I think it's out of scope. (Whereas just adding checks for the other static values seems fine imho.)sendmail_pathstuff.)Thanks!
Comment #55
smustgrave commentedNot sure the expanding test coverage is needed as this fix is only needed for 9.5.
Comment #56
catchI don't see how this is only needed for 9.5.x:
Doesn't some of this code need to be removed now we only support PHP 8+? i.e. what #32 does?
We should get a correct patch into 10.x first.
Comment #57
P44T commentedIt's not. The fix in the patch is also very relevant for Drupal 10+. The backwards compatibility for PHP < 8 is only relevant for Drupal 9.5 (which is still supported, I think), though. Since Drupal 10 doesnt support PHP 7. That concerns these lines:
In Drupal 10 and onwards, this particular block can be removed, but the rest of the patch is still needed.
Could you also please elaborate why this issue has been reverted to 'Needs work', since there is a working patch with test coverage (#52, 3270647-52-d9.patch)? You seem to suggest the patch is incorrect, but I don't see why.
Comment #58
catch@P44T it's needs work because issues need to go into Drupal 10 before they can go into Drupal 9, i.e. we should commit a version like "in Drupal 10 and onwards, this particular block can be removed, but the rest of the patch is still needed." which I don't think is on this issue yet and certainly not with a recent test run.
Comment #59
P44T commented@catch, thank you for clarifying. I've added new patch files:
Please let me know if there's anything else I can do!
Comment #60
P44T commentedPatches have been updated to address a coding standards issue.
Comment #61
smustgrave commentedGot my wires crossed but just to reiterate/confirm we want
D9 = 3270647-52-d9.patch from #52
D10/11.x = 3270647-60-d10-only.patch from #60
Comment #66
catchCommitted/pushed the patches from #60 and 59 to 11.x/10.1.x/10.0.x/9.5.x. Did my best with issue credits but a lot of people on this issue so apologies for any ommisions. Thanks!
Comment #68
liquidcms commentedUnfortunate this was closed as what ended up in 9.5 does not work. The solution from #20 does work for us.
Comment #69
larryla commentedHello,
has it not been fixed on v9.5.10 - Issue #3270647 ???