Jump to content

Recommended Posts

Hi All

 

I'm trying to send an email with an attachment, but all I get is the content and the MIME headers coming through as plain text in the body.  It needs to be HTML with a pdf attachment.

 

Any thoughts what I'm doing wrong.  My knowledge of MIME headers etc is as limited as cutting and pasting this code from Google, and amending it to try to do what I need.

 

function emailJI($to, $subject, $attachment, $content){


  /* Email Detials */
$mail_to = "$to";
$from_mail = "Admin@*****.co.uk";
$from_name = "***** Administrator";
$reply_to = "*****.*****@*****.com";
$subject = "$subject";
$message = "$content";

/* Attachment File */
// Attachment location
$file_name = "prebrief.pdf";
$path = "prebrief.pdf";

// Read the file content
$file = $path.$file_name;
$file_size = filesize($file);
$handle = fopen($file, "r");
$content = fread($handle, $file_size);
fclose($handle);
$content = chunk_split(base64_encode($content));

/* Set the email header */
// Generate a boundary
$boundary = md5(uniqid(time()));

// Email header
$header = "From: ".$from_name." <".$from_mail.">\r\n";
$header .= "Reply-To: ".$reply_to."\r\n";
$header .= "MIME-Version: 1.0\r\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
$header .= "This is a multi-part message in MIME format.\r\n";
$header .= "--".$boundary."\r\n";

// Email content
// Content-type can be text/plain or text/html
//$header .= "Content-type:text/plain; charset=iso-8859-1\r\n";
$header .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
$header .= "$message\r\n";
$header .= "--".$boundary."\r\n";

// Attachment
// Edit content type for different file extensions
$header .= "Content-Type: application/pdf; name=\"".$file_name."\"\r\n";
$header .= "Content-Transfer-Encoding: base64\r\n";
$header .= "Content-Disposition: attachment; filename=\"".$file_name."\"\r\n\r\n";
$header .= $content."\r\n";
$header .= "--".$boundary."--";

// Send email
if (mail($mail_to, $subject, "", $header)) {
	echo "Sent";
} else {
	echo "Error";
}



}

Link to comment
https://forums.phpfreaks.com/topic/259262-email-with-attachemnt/
Share on other sites

Ok, attachment coming through now, but it is saying it cannot be opened as it wasn't encoded properly, and the message is still plain text and not HTML.

 

Here's my amended code

 

/* Attachment File */
// Attachment location
$file_name = "prebrief.pdf";
$path = "prebrief.pdf";

// Read the file content
$file = $path.$file_name;
$file_size = filesize($file);
$handle = fopen($file, "r");
$content = fread($handle, $file_size);
fclose($handle);
$content = chunk_split(base64_encode($content));

/* Set the email header */
// Generate a boundary
$boundary = md5(uniqid(time()));

// Email header
$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "Reply-To: ".$reply_to."\n";
$header .= "MIME-Version: 1.0\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\n";
$header .= "This is a multi-part message in MIME format.\n";
$header .= "--".$boundary."\r\n";

// Email content
// Content-type can be text/plain or text/html
//$header .= "Content-type:text/plain; charset=iso-8859-1\r\n";
$header .= "Content-Transfer-Encoding: 7bit\n";
$header .= "$message\n";
$header .= "--".$boundary."\n";

// Attachment
// Edit content type for different file extensions
$header .= "Content-Type: application/pdf; name=\"".$file_name."\"\n";
$header .= "Content-Transfer-Encoding: base64\n";
$header .= "Content-Disposition: attachment; filename=\"".$file_name."\"\n";
$header .= $content."\n";
$header .= "--".$boundary."--";

// Send email
if (mail($mail_to, $subject, "", $header)) {
	echo "Sent";
} else {
	echo "Error";
}



}

It's been a while since I did this, but:

 

1) The specification says the headers are terminated with CRLF not just LF

 

2) The mime message is the BODY not a header (see my comment line below):

 

	// Email header
$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "Reply-To: ".$reply_to."\n";
$header .= "MIME-Version: 1.0\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\n";

# I BELIEVE EVERYTHING BELOW HERE SHOULD BE THE BODY NOT PART OF THE HEADERS

$header .= "This is a multi-part message in MIME format.\n";
$header .= "--".$boundary."\r\n";

// Email content
// Content-type can be text/plain or text/html
//$header .= "Content-type:text/plain; charset=iso-8859-1\r\n";
$header .= "Content-Transfer-Encoding: 7bit\n";
$header .= "$message\n";
$header .= "--".$boundary."\n";

// Attachment
// Edit content type for different file extensions

 

You are putting everything in the headers and sending an empty body. In a raw email stream, the first blank line separates the headers from the body. So putting everything in the headers might work, if the blank line is put in the right place. However, I don't know what PHP's mail() function does with the headers --- does it remove extra blank lines? --- so I would separate them.

OK, the message comes through nicely as HTML now, however the attachment is still saying it's corrupted and not been decoded correctly

 

/* Attachment File */
// Attachment location
$file_name = "prebrief.pdf";
$path = "prebrief.pdf";

// Read the file content
$file = $path.$file_name;
$file_size = filesize($file);
$handle = fopen($file, "r");
$att = fread($handle, $file_size);
fclose($handle);
$att = chunk_split(base64_encode($att));

/* Set the email header */
// Generate a boundary
$boundary = md5(uniqid(time()));

// Email header
$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "Reply-To: ".$reply_to."\n";
$header .= "MIME-Version: 1.0\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\n";
$header .= "This is a multi-part message in MIME format.\n";
$header .= "--".$boundary."\n";

// Email content
// Content-type can be text/plain or text/html
$header .= "Content-type:text/html; charset=iso-8859-1\n";
$header .= "Content-Transfer-Encoding: 7bit\n";
$header .= "$message\n";
$header .= "--".$boundary."\n";

// Attachment
// Edit content type for different file extensions
$header .= "Content-Type: application/pdf; name=\"".$file_name."\"\n";
//$header .= "Content-Transfer-Encoding: base64\n";
$header .= "Content-Disposition: attachment; filename=\"".$file_name."\"\n";
$header .= $att."\n";
$header .= "--".$boundary."--";

// Send email
if (mail($mail_to, $subject, "", $header)) {
	echo "Sent";
} else {
	echo "Error";
}

it could be a problem with the file loading, try sending some raw html as the attachment (just remember to change the Content-Type for the attachment before you send it.  Also, add another \n after the attachment so it looks like this:

// Edit content type for different file extensions
$header .= "Content-Type: text/html; name=\"test.html\"\n";
$header .= "Content-Transfer-Encoding: base64\n";
$header .= "Content-Disposition: attachment; filename=\"".$file_name."\"\n";
$att= "<html><head><title></title></head><body>Test attachment for mail delivery</body></html>";
        $header .= $att."\n\n";

....

1) The specification says the headers are terminated with CRLF not just LF

....

the specification for what? because the only reliable way I could get a multipart message to send when writing my mail function was to use \n and NOT \r\n

 

RFC 5322 (and its predecessor, RFC 2822) state:

 

A message consists of header fields (collectively called "the header section of the message") followed, optionally, by a body.  The header section is a sequence of lines of characters with special syntax as defined in this specification.  The body is simply a sequence of characters that follows the header section and is separated from the header section by an empty line (i.e., a line with nothing preceding the CRLF).

 

and

 

Header fields are lines beginning with a field name, followed by a colon (":"), followed by a field body, and terminated by CRLF.  A field name MUST be composed of printable US-ASCII characters (i.e., characters that have values between 33 and 126, inclusive), except colon.  A field body may be composed of printable US-ASCII characters as well as the space (SP, ASCII value 32) and horizontal tab (HTAB, ASCII value 9) characters (together known as the white space characters, WSP).  A field body MUST NOT include CR and LF except when used in "folding" and "unfolding", as described in section 2.2.3.  All field bodies MUST conform to the syntax described in sections 3 and 4 of this specification.

 

Edit As with most internet content, many readers (i.e. browsers and email clients) choose to interpret the standard loosely. So some readers may not care if the headers have the CR part. But, since the headers have to be read by the email transport servers -- that is, all servers handling the mail along the way -- it is a good idea to follow the standard. Some transport servers may not handle the headers properly without the CR in the line terminator; and some readers (clients) may not handle the headers properly without it.

ahh....yeah, that doesn't reliably work in practice though.  I only spent two full days trying before I managed to get a working function using only the \n and not the \r\n, and it may very well be down to something as distant as how windows handles a new line over how linux does, but I'm just sticking with what I know.

Just checked the layout on my function, I think it's the order you have things in your header, this is the working order I use :

$message .= "Content-type:application/octet-stream;"; //set content type to suitable type for attachment (octet-stream is for generic binary data other are available) 
$message .= "name=\"$fName\"".$e; //set the file name
$message .= "Content-disposition:attachment;"; //use attachment to infer data that is being passed as a file 
$message .= "filename=\"$fName.xml\";".$e; //set the filename as it will be recieved 
$message .= "Content-transfer-encoding:base64".$e.$e; // encode the file for transfer
$message .= $attachment.$e.$e; // add attachment contents 

$e contains \n for line break.

function emailJI($to, $subject, $attachment, $content){

/* Email Detials */
$mail_to = "$to";
$from_mail = "Admin@********.co.uk";
$from_name = "*******Administrator";
$reply_to = "******.******@******.com";
$subject = "$subject";
$message = "$content";

/* Attachment File */
// Attachment location
$file_name = "prebrief.pdf";
$path = "";

// Read the file content
$file = $path.$file_name;
$file_size = filesize($file);
$handle = fopen($file, "r");
$att = fread($handle, $file_size);
fclose($handle);
$att = chunk_split(base64_encode($att));

/* Set the email header */
// Generate a boundary
$boundary = md5(uniqid(time()));

// Email header
$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "Reply-To: ".$reply_to."\n";
$header .= "MIME-Version: 1.0\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\n";
$header .= "This is a multi-part message in MIME format.\n";
$header .= "--".$boundary."\n";

// Email content
// Content-type can be text/plain or text/html
$header .= "Content-type:text/html; charset=iso-8859-1\n";
$header .= "Content-Transfer-Encoding: 7bit\n";
$header .= "$message\n";
$header .= "--".$boundary."\n";

// Attachment
// Edit content type for different file extensions
$header .= "Content-Type: text/pdf;"; 
$header .="name=\"prebrief.pdf\"\n";
$header .= "Content-Disposition: attachment; filename=\"".$file_name."\"\r\n";
$header .= "Content-Transfer-Encoding:base64\r\n";
//$att= "<html><head><title></title></head><body>Test attachment for mail delivery</body></html>";
$header .= $att."\n\n";
$header .= "--".$boundary."--";

// Send email
if (mail($mail_to, $subject, "", $header)) {
	echo "Sent";
} else {
	echo "Error";
}



}

ok, I've been through it and compared it to how my function looks, There was a lot of white space in the strings that shouldn't have been, some of the \n's were still \r\n, some lines didn't have enough \n's and you didn't wrap the charset value in double quotes, oh, and you missed out the To and Subject headers altogether (may only matter on some servers with a spam overwatch, but I put them in anyway).  I think it should work now, if you want to see my function for comparison it's on my blog in my sig, but here's the revised code anyway:

<?php
function emailJI($to, $subject, $attachment, $content){

/* Email Detials */
$mail_to = "$to";
$from_mail = "Admin@********.co.uk";
$from_name = "*******Administrator";
$reply_to = "******.******@******.com";
$subject = "$subject";
$message = "$content";

/* Attachment File */
// Attachment location
$file_name = "prebrief.pdf";
$path = "";

// Read the file content
$file = $path.$file_name;
$file_size = filesize($file);
$handle = fopen($file, "r");
$att = fread($handle, $file_size);
fclose($handle);
$att = chunk_split(base64_encode($att));

/* Set the email header */
// Generate a boundary
$boundary = md5(uniqid(time()));

// Email header
$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "To: ".$mail_to."\n";
$header .= "Subject: ".$subject."\n";
$header .= "Reply-To: ".$reply_to."\n";
$header .= "MIME-Version: 1.0\n";

// Multipart wraps the Email Content and Attachment
$header .= "Content-Type: multipart/mixed;boundary=\"".$boundary."\"\n\n";
$header .= "This is a multi-part message in MIME format.\n";
$header .= "--".$boundary."\n";

// Email content
// Content-type can be text/plain or text/html
$header .= "Content-type:text/html;charset=\"iso-8859-1\";\n";
$header .= "Content-Transfer-Encoding:7bit\n\n\n";
$header .= "$message\n\n";
$header .= "--".$boundary."\n";

// Attachment
// Edit content type for different file extensions
$header .= "Content-Type:text/pdf;"; 
$header .="name=\"prebrief.pdf\"\n";
$header .= "Content-Disposition:attachment;filename=\"".$file_name."\"\n";
$header .= "Content-Transfer-Encoding:base64\n\n";
//$att= "<html><head><title></title></head><body>Test attachment for mail delivery</body></html>";
$header .= $att."\n\n";
$header .= "--".$boundary."--\n";

// Send email
if (mail($mail_to, $subject, "", $header)) {
	echo "Sent";
} else {
	echo "Error";
}



}
?>

Sadly I can't test this just now as all mail traffic here is routed through the server and requires authentication. Good luck though, let me know how it goes.

 

P.S. The reason you couldn't open the PDF when the transfer-encoding was commented out was because you still had it going through the base64 encode at the top of the function.

there are probably a couple of ways, but I would work with an array of values, content type, attachment name, file name, file, and then use a foreach to build the header to include each of them, you would have all these lines in the foreach:

$header .= "--".$boundary."\n";
$header .= "Content-Type:text/pdf;"; //array value for content type after the : 
$header .="name=\"prebrief.pdf\"\n"; //array value for attachment name (remember file extension)
$header .= "Content-Disposition:attachment;filename=\"".$file_name."\"\n"; //aray value for file name
$header .= "Content-Transfer-Encoding:base64\n\n";
$header .= $att."\n\n"; //aray value for file

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.