18

I have a very simple PHP script, something like:

while ($condition){
    echo "<a href="thanks.php?id=".$id."> THANKS </a>";
}

Of course, I have a bit more code but it doesn't matter. Once I had created this piece of code, the script sends an email to the user.

THE INBOX

The links are okay in every single line, except the LAST ONE shows the link this way:

    tha nks.php?id.....

It adds a space in between the code.

This only happens with hotmail. Gmail, yahoo, and everything else work just fine.

femtoRgon
  • 32,893
  • 7
  • 60
  • 87
Andy
  • 525
  • 1
  • 7
  • 18
  • 3
    Sounds like hotmail is "protecting" the user by (semi)obfuscating links. If it's only one situation I doubt it's something you're doing. -- EDIT: Also, FYIW, your syntax is off on the above code (quote-mismatch) but given the link works in 90% of the other situations, I'm _assuming_ it's a typo when you went to post this. Otherwise, I'd look in to making sure you both escape the `"` within the anchor, and make sure you have a closing `"` after the `$id`) – Brad Christie Apr 03 '12 at 19:20
  • yeah that is what i thought, about the typo, i wrote this as example, this is not the real code. On the other hand.. if it is a hotmail issue why it doesn't make the same "protection" to all the links! it just happends with the last one! hotmail makes me want to take pills to turn me crazy! – Andy Apr 03 '12 at 19:26
  • An another good discussion on the topic : http://www.jeremytunnell.com/posts/really-hairy-problem-with-seemingly-random-crlf-and-spaces-inserted-in-emails – toto21 Jan 22 '15 at 15:17

6 Answers6

20

I know this is late but there is an alternate solution that worked for me:

Use this line to encode your entire message using base64:

$message = chunk_split(base64_encode($message));

Then, append this your header:

$headers .= "Content-Transfer-Encoding: base64\r\n\r\n";

That will tell the mail client that your message is base64 encoded.

karancan
  • 2,152
  • 4
  • 23
  • 35
  • 2
    Thank you @karancan, your answer is great :) Had an issue with rundom spaces in linkes in html emails - they're gone. Great job!! – Mr. T Feb 26 '15 at 15:18
  • This is the easiest solution, if you don't want to rearrange your HTML code or if you don't know how many text will be inserted (i. e. if some text parts are taken from a database). – David Gausmann Nov 21 '16 at 13:45
  • OMG thank you!! I was trying to debug this problem for 2 months. Had no idea how to even approach asking how random spaces show up in variable values. – Omar Aug 02 '17 at 13:49
16

karancan's solution should work, but the root cause is what Hobo was saying. The SMTP protocol requires that lines be limited to 1000 characters (or really 998 characters plus a CR-LF). If your line is longer than 1000 characters, a CR-LF will be inserted every 998 characters.

So if you're building up your $message with a bunch of $message .= "more text"; you'll encounter this error once that line is greater than 900 characters long. It's confusing though because the spaces seem to be intermittent, particularly if you're building up an HTML message because sometimes the linebreaks will appear in perfectly benign locations.

The simple solution is to add a \r\n\ to the end of lines.

Interestingly, these forms work:

$message .= "<tr><td>1</td><td>2</td>\r\n";
$message .= '<tr><td>1</td><td>2</td>'."\r\n";

But this does not:

$message .= '<tr><td>1</td><td>2</td>\r\n';

The \r\n must be surrounded by double quotes, otherwise the characters will just get added to the text instead of creating a carriage return/line break.

@karancan is correct though, and you can skip adding the \r\n's if you base64 encode your messages, and add the required header.

Mordred
  • 3,734
  • 3
  • 36
  • 55
  • 1
    Are you sure that this is the PHP mail function and not the SMTP server? If it is PHP -> Is there any PHP bug reported for that? I didn't find any, but this behaviour should be changed (i. e. omit this behaviour or at least a notice if RFC expects short lines). – David Gausmann Nov 21 '16 at 13:49
  • It's been two years since I encountered this, but it seems reasonable/likely that it could be the SMTP server and not the mail function. – Mordred Nov 22 '16 at 05:45
15

Was facing the same problem and tried most of these solutions but it did not work for me. It seems when the line seems too long to mail function it adds space around 900 character mark. So the following solution of adding wordwrap worked for me.

mail($to, $subject, wordwrap($message), $headers);

Pranjal
  • 151
  • 1
  • 2
  • Pretty simple solution, although this could litter your actual content text with unnecessary `
    `s. If that doesn't matter to you, or you can't guarantee places to put the `\r\n`s due to variable text then this is an acceptable solution.
    – Mordred Apr 17 '15 at 19:02
  • This was the only reliable solution for my use case (which was a series of tables of variable length as the "message body") – CSSBurner Jun 25 '18 at 18:14
  • Simple, works great and it's easy to understand. Just for info anyone reading this, it defaults to 75 characters long, but you can change that (read the PHP documentation). – kissumisha Jan 25 '23 at 13:02
3

2019 working answer

The SMTP protocol requires a CRLF after each line which contains more than 998 characters. Basically each line of 1000 must contain at its very end the CRLF characters.

The simplest working solution to this issue is to send the email content encoded in base64 then add a header that tells mail clients that the content was encoded in base 64 and then send the email.

Bellow is an working example. I've tested with a mail which had ~ 27000 chars and it worked perfectly. Before this update, it was breaking critical links in my HTML content.

<?php

// used for date() function
date_default_timezone_set('Europe/Bucharest');


$to = 'john.doe@company.com';
$from = 'john.doe@company.com';
$fromName = 'John Doe';

$subject = "Html email test at ".date('H:i:s');


// Set content-type header for sending HTML email
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
$headers .= "Content-Transfer-Encoding: base64" . "\r\n";


// Additional headers
$headers .= 'From: '.$fromName.'<'.$from.'>' . "\r\n";
$headers .= 'Cc: welcome@example.com' . "\r\n";
$headers .= 'Bcc: welcome2@example.com' . "\r\n";


$extremely_long_html_email = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head>
    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>
    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>
    <title>TEST</title>
    </head>
    <body>
        Very long HTML code. I stripped mine due to copyright issues.
    </body>
</html>";


// Send email
if(mail($to, $subject, base64_encode($extremely_long_html_email), $headers)){
    echo 'Email has sent successfully.';
}else{
    echo 'Email sending failed.';
}
?>

TL;DR; Add extra headers $headers .= "Content-Transfer-Encoding: base64" . "\r\n"; and encode html content before sending it using base64_encode($html_email) function.

besciualex
  • 1,872
  • 1
  • 15
  • 20
2

I've seen sendmail insert characters when lines are too long. That's not the case here (as other clients are handling it fine), but I wonder if hotmail has a line length limit that you're hitting.

Does it make any difference if you insert a newline in your echo, ie

while ($condition){

    echo "<a href=\"thanks.php?id=".$id."\"> THANKS </a>\n";

}
Hobo
  • 7,536
  • 5
  • 40
  • 50
  • nope, no difference at all, besides, they are just 10 thumbnails with links to thanks.php.. the first 9 thumbnails are PERFECT (even in hotmail) the problem is the last one.. and the url has exactly the same length for each one of them. =( – Andy Apr 03 '12 at 19:34
1

If you are using PHPMailer, or Joomla CMS (which uses PHPMailer), the appropriate code to use utf-8 and base64 encode your message (the equivalent of karancan's solution) is:

$mailer->CharSet = 'utf-8';
$mailer->Encoding = 'base64'

Note that you should not chunk_split(base64_encode($emailBody)); because PHPMailer automatically does this for you as soon as you set Encoding to base64, and you do not want to have your email body base64 encoded twice :)

Gavin G
  • 856
  • 6
  • 6
  • Hi Gavin, if you are able to provide Joomla support, please extend your Stack Exchange citizenship to [joomla.se] Stack Exchange. – mickmackusa Nov 02 '20 at 07:56