-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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
PHPMailer 6 styling conundrum #1705
Comments
Fundamentally, just don't do this. You're going against the way PHPMailer is designed to work, and as you're finding, it will not work out well. The right way is to break out your message into its component body and headers (to do that you may want to use a MIME parser; there's a good one in PEAR), and then pass them into PHPMailer in the way it expects. For headers it's quite straightforward - splitting on line breaks should result in something acceptable to |
When you say "break out", it isn't easy to set different ContentTypes and Encodings for plain-text and Html. One size fits all, sort of, by default. My solution was to keep the Body and altBody together including boundaries. You can pass strings along, no problem, but the process is automated. For example: $messageplainqp = quoted_printable_encode(str_replace("\n", $crlf, (str_replace("\r", "", $messageplain)))); Much more convenient to keep the original string, but PHPMailer wasn't playing nice with the original string. Having it quoted-printable put line breaks in that mangled encrypted download links. I don't know if I'm explaining it well. Essentially, $mheader already concatenates strings. Inserting each string separately into the body or altbody -- how would you go about that? I started working with PHPMailer two days ago, I'm still kind of a newbie. Your reply above however does confirm my suspicions. |
You're trying to get a square peg into a round hole... If you're doing your own QP encoding, you're missing the point of PHPMailer :) It takes care of all that for you. As you've noticed, PHPMailer doesn't allow you to build arbitrary MIME structures, it only has a few presets, and you don't get to control them in such fine detail. The advantage of this is that it keeps it simple, but the downside is a certain lack of flexibility. If you want to control everything, I'd recommend looking at Zend_Mail or SwiftMailer, though be warned that they are much more complex (SwiftMailer has >450 classes vs the 3 in PHPMailer!). |
I preferred several years ago before thinking about SMTP and authentication as I am now, a hands-on approach to formatting emails, since HTML in emails is a mess, stating it mildly. Every client has its own quirky rules. Doubling <p> gaps, ignoring single <br>, using HTML3 tags versus style sheets. To accommodate crazy email clients, coding markup by hand was and is unavoidable. And PHPMailer seemed like a means to add SMTP. It is, but at what cost? Thinking in the opposite direction, would it not be viable to create a simple class to inject SMTP headers into an email and then send it? Like PHPMailer, but different? You're right, if I had to do it again, PHPMailer would offer significant shortcuts. :) (Time to clean up some code now.) |
What you described is pretty much what PHPMailer does. Generally the issues with HTML email are nothing to do with MIME structure or headers, it's purely down to what you put in |
There is an option to enable an inline logo image and send other file attachments, which made I chose Base64 for Html, because it was congruous with file attachments. Finally, I didn’t trust clients not to break encoded as quoted-printable. Some of the Html lines are long-ish. Encoding into a Base64 chunk seemed the safest. So I ended up with mixed encodings. I tried to carry those over into PHPMailer, but saw no way of doing it, conventionally, at which point I started to fool around with the MIME structure. Curious whether PHPMailer 6 -- I was using 5.2.9 -- had settings to leave the Body empty, I downloaded and experimented with it, but the opposite was true. I googled PHPMailer, found a GitHub thread on line breaks, and that led me here to ask this question. Standard text + HTML |
Everything you describe about the structure is available in PHPMailer. It will happily do text + html with embedded and attached images. It uses the most efficient encoding (which is 8bit if that’s applicable, and works well, falling back to QP if line length is an issue), but you can override that. It’s normal to use multipart alternative and mixed at the same time, and PHPMailer does that. PHPMailer doesn’t support format=flowed, but you can’t just declare it in the header, you have to actually do it. I’d also say that using base64 for html is a great way of ending up in spam filters. Have you tried doing it “the PHPMailer way”, or are you determined to keep swimming upstream? |
I'm up for a challenge. I tried to override My former web hosting provider had a bare-basics mail tool that only showed monospace text and no Html. There I found that If encoding Html in It won't be an easy chore. |
This really seems to be a problem of your own making. Really, go with the flow. |
You have convinced me to go with the flow. Can I convince you that Line endings occassionally play tricks on you in plain-text, because they aren't uniform. I like to include a safeguard against orphaned linefeed characters and/or carriage-returns.
In Html markup EOL characters don't matter, but to show that
Result:
At the beginning of the fourth line the dot goes missing. Before Before I forget, thank you for convincing me. :) |
For the line breaks, PHPMailer is very careful to handle line breaks consistently, which is why this method is used all over the place. The missing dots is not a quoted-printable problem; It's because you're not doing SMTP dot stuffing. PHPMailer deals with that. |
Not that I doubt SMTP dot stuffing is somehow the culprit, except another variant that does not delete the dot at the beginning of the line is when instead of replacing single line end characters with PHP_EOL, CRLF (\r\n) is substituted. The mail server might use UNIX line endings -- I don't really know. On Windows CRLF and PHP_EOL are supposed to be the same. Anyhow, to my vast relief, line end sanitizing is going out the window, because as you say, PHPMailer handles it. The above example you correctly guessed was generated by my script. I won't take up any more of your time. Thanks for the replies. I noticed a few parsing improvements in PHPMailer. Umlauts on the recipient line, I always struggled with ... |
Sorry to open the discussion again, Synchro. Correct me if I'm wrong, when you embed an inline image, the top Content-Type must be I strongly suspect it to be causing plain-text to be inserted twice. In the text string I specify in Ah, the code:
|
There are several odd things here:
This doesn't exist. It's against RFC to set a Why set The only time PHPMailer sets the plain text body is if you call Can you show me the example MIME structure you're ending up with? As you say, it should be a |
Oh, the logo is attached. I was initially impressed. Then I switched to plain-text view, ... I don't know how I came up with Now I've omitted |
I just remembered why I enabled |
Ah, I've realised I was wrong about MIME structure; the right structure is what is in there: a It's an RFC contravention to declare an 8-bit charset if your content is 7-bit clean - in the example you provided there are no 8-bit chars, so it automatically downgraded to There is quite a lot of "magic" going in here, but you can see it's all there for a reason! |
Hi. Re: the RFC contravention. At present the email contains only ascii, but if the recipient name has 8-bit characters, those will also be part of the main content. The greeting would read: Hi Herr Hühn, ... Automated emails are unpredictable that way. I think I'm back on square one. Trying to get a square peg into a round hole... |
Sure, and if you had included 8-bit text in your example message, it would not have been downgraded to us-ascii - that's why that check is automated. Are you certain that the second text part is in the sent message, and not being added at the receiving end? It sounds very much like the kind of thing that some mail filters do, and the example you provided does not have a second plain text part. |
Correction: without CID the image of course does not get embedded. It's an attachment. In either case I have the MIME structure: |
We posted at the same time. No, I know it's from the Html. The title "publication download links" only appears there. A mail filter would have to be an AI to copy text verbatim. |
Mail filters often do that - HTML to plain text is pretty easy - PHPMailer 5 used to bundle a converter. I encountered a mail filter at a big corporate site that converted HTML to text by simply stripping tags, and imported and inlined all external images. It made a right mess. |
I don't know. I could try tweaking something in the source code, if you give me time. It's hard to say whether my endeavors will succeed. I could post what happened in a few hours. In the meantime, fresh ideas are welcome. Thanks. edit: I'm skeptical about the filter theory. For one, the same filters were in operation when I used the mail function that produced |
It's very easy to confirm - check that what you sent is what you receive. You can get a copy of the locally-generated message using |
Just switched back to mail(). No problems there. Here it is in RAW. edit: The copy wouldn't tell me very much. By looking at the message source alone, I wouldn't be able to see where the additional plain-text came from. All I'd have is the same message. |
As yet you have not shown me anything that's not behaving as expected. Don't assume; check. The send-side copy would tell you exactly what you want to know, because if it's been altered in transit, what you sent will not be the same as what arrives. Show me PHPMailer sending an unexpected second text/plain part. Sending via |
There is no second To see what it looks like, below is a screenshot. I have to take a break for a while. |
Seriously. Post your code, post example debug output, post exactly what you're passing in to |
SMTP log attached. Do we at least agree that Update: |
No, multipart/mixed is not mandatory; it should be used where it is correct to do so - which is not at the top level of a text + HTML message. I'd normally expect to see it on say a plain-text only message with attachments, for example if you do this: $mail->isHTML(false);
$mail->Body = 'hello';
$mail->addAttachment('somefile.jpg'); In PHPMailer's MIME options, the fact that I extracted the raw message from your SMTP log, and it renders just fine, no stray text parts at all: |
Indeed, the Html is fine. Thanks for your reply and sorry for the delay. I for my part tried to reconstruct the PHPMailer syntax in mail(), to see if it would also render plain-text twice. It does. So it's the syntax. I have no experience with PHPMailer earlier than 5.2.9. I'm tempted to infer, probably an evil habit, that at some point the MIME structure syntax changed. Attachments and inline images were treated as mixed content, both. Related makes sense for inline images for the reason you mentioned yesterday: inline images are part of the Html. In terms of the syntax it was possible also to assign two Content-Types.
Maybe the only approach currently available to embed images (better than linking them), seems to be to override the Why go to all the trouble of converting Html to plain-text by stripping out tags? Wouldn't it be much easier to insert the plain-text string again?
I realize the syntax is wrong, but if $messageplain is the string I really want, can't I re-use it? |
It's really not clear what it is you're trying to do other than making work for yourself! Nowhere have you shown what it is that you're trying to achieve that you can't do currently. You seem to be getting tied up in problems that have been solved - PHPMailer does a good job of handling basic MIME structures like these, and I've not seen any problems relating to that here.
What syntax? I've still not seen your actual data you're passing in, other than what I extracted from the SMTP log. I have seen no code that generates this double plain text rendering in PHPMailer. I don't recall inline images and attachments ever being always treated as mixed, and I've been on this for a while... Multiple content types on the same MIME part? No, definitely not. It makes no sense to do that; it would just confuse clients or be ignored. Indeed those extra operations are unnecessary - the point of converting HTML to plain text is in cases where you don't have a plain text version. If you do, then you don't need to use a converter. If you don't want the conversion, reassign $mail->msgHTML($messagehtml, $basedir = '',true);
$mail->AltBody = $messageplain; Also bear in mind that Do you actually have a clear problem statement that I have a chance of addressing? |
It doesn't work.
I thought I might work to convert Hml to plain-text. On closer examination two plain-texts are being sent yet again. There is a remote possibility that my PHP is sending the plain-text twice. How at the same time it could not be submitting the Html in the same message twice is beyond me. I could post the send form online, provisionally only, because it still has that glitch, and point you to the page, or send you PHP code, three pages worth. It could be anything. What do you advise? |
Define "doesn't work". The empty I recommend doing what I asked - show me a sent message that contains the two text parts. Note a sent message, not a received message - use |
Sent and received always match, but I'm attaching the output. The problem boils down to the client or the syntax. Windows Live Mail 2012 is confused by the syntax. I send encrypted download links to customers on my site who purchase digital items securely via PayPal. In 2016 PayPal updated its requirements for sellers. Emails sent should be PCI compliant and TLS1.2, although it isn't exactly said whether the rules apply to encrypted download links. PHPMailer is worth it. In every way, except for the inline image nuisance (a Windows Live Mail nuisance perhaps). Better than mail() for my limited purposes. You may disagree with this summary. Please never use Windows Live Mail. Here's the |
I looked more into the definition of
That is indeed nonsensical (it's futzing with structure, not content), however, the
PHPMailer doesn't set that, and so a client would have to look inside the multipart to figure out what the child type is. This isn't a problem in itself, but this may be where Live's bug is, so let's give it a try. Change this line from this:
To:
See what happens... |
Although it fixed the duplicate issue, adding that had the undesired effect of displaying the standard text in Html view and in plain-text view. Gmail also displayed the text version. No more Html. |
Odd. It works perfectly in Apple Mail. I just found this Microsoft doc from 2011 that describes exactly this kind of MIME structure, and Outlook's support for it. What we're doing here is similar to "combo #5", except we don't need the overall |
Many emails today adhere more or less to that structure, with or without inline images or attachments. Outlook 2016 no longer has a real read plain-text viewing option. Assuming older Outlook did. What you see instead in Outllook 2016 is the HTML as plain-text. MS was possibly too lazy to fix it. I haven’t checked sent emails from Outlook.
Well, if you need any more testing ...
|
What I meant is, if you feel like any more testing … I would not be averse to a few more experiments, if it were possible to send emails with a combo 5 structure, for instance. You have been terrifically helpful. |
You can get PHPMailer to use a I've just pushed changes to add the |
Off the record I would say that the Where you place After the boundary, MS clients apparently disregard Adding/omitting it made no difference in how messages displayed in clients on Windows. |
I don't think order should be important, but the example in the RFC has
If windows clients are going to be so buggy, you may be better off cutting your losses and reverting to using an externally referenced image instead, avoiding these problems altogether. |
I did it the hard way. (Keeping my options open.)
In PHPMailer.php I disable folding with |
To clarify so that others can see, in 6.0.7 the relevant lines are 3151 and 3152, from this:
To:
Disable folding. The body and headers aren't normally supposed be to replaced. To make room for it, one other change is needed here. Before: And after: A word of caution might be good. Best practice is to leave PHPMailer.php the way it is and not make these changes. The technical term for it, I think is 'unsupported'; some say 'hack'. |
That "fix" strikes me as exactly the kind of thing child classes are for - you shouldn't need to edit any of the original code in order to override bits of it. Anyway, I'm glad you have a solution, and I'm happy we've got an improvement in multipart/related handling, even if that wasn't what you set out to do :) |
Yes. I've grown attached to Windows Live Mail. According to Litmus in 2018, 1% of the market share. |
You should have a semicolon separating the I wasn't sure at first. After the boundary ... before the boundary ... However, the preceding boundary breaks without a semicolon. Gmail displays the email source, rather than the email itself. This is more correct:
|
I wonder also if replacing the tab-space with a space might cause line-length problems in some scenarios. I haven't experienced any (problems), but … email clients do strange things. |
Are you referring to the changes I made for #1714? Thanks for testing the multiple params - It turns out that omitting semi-colons between multiple params is a mistake in the RFCs! Discussion of it here. I've just pushed a fix for that as you described. I don't think I've replaced tab-space with space anywhere, only tabs with spaces (i.e. a single char change), so there should be no line-length changes. |
It wasn't so much testing as stumbling across it, and I had a hunch it might be the I thought you replaced 'line-end'+'tab' with 'space'. Merging lines effectively. But you're right, it is only a tab for a space. Gmail prints it all in one line. |
I have a problem with PHPMailer 6.0.7, WAMP, PHP 7.3.3, which I hesitate to call a problem, since it is of my own making, and yet, the code was fine in PHPMailer 5.2.9.
Instead of generating an email with $mail-> "headers", I use preexisting headers that are designed to work in the PHP mail function. Wanting to retain mail() compatibility, I insert the same headers and almost the full MIMEBody. Thus the message adds up to two strings. $headers and $mheaders.
The Body remains empty (with a space).
$mail->Body = ' ';
In its entirety I send:
I'm attaching two text email source files graphically illustrating the different outputs with PHPMailer 5.2.9 and 6.0.7 respectively, one, a well-formed email, the other, unreadable by email clients that fail to display the message, that is both plain-text and html.
529.txt
607.txt
It isn't precisely a bug. Question: Is there a way of fixing it?
Thanks.
The text was updated successfully, but these errors were encountered: