Sendmail with attachment

Hi, I have a script I put together for one of my websites to email the form contents including an uploaded file as an attachment using sendmail. It seems to work fine on my local dev machine, but not when uploaded to Dreamhost. Anyone have any suggestions? I can post the code if needed.

The sendmail here is actually Postfix. Is your local sendmail really sendmail?


Hi Scott, I’m using XAMMP, and my php.ini file just says that “Internal Sendmail Support for Windows” is enabled.

If you don’t mind, go ahead and post the code with any sensitive information blotted out.


<?php // Variables from form page $fname = $_REQUEST['fname'] ; $lname = $_REQUEST['lname'] ; $name = $fname." ".$lname; $maddress1 = $_REQUEST['maddress1']."\n" ; if ($_REQUEST['maddress2'] != "") { $maddress2 = $_REQUEST['maddress2']."\n" ;} $mcity = $_REQUEST['mcity']."\n" ; $mpostal = $_REQUEST['mpostal']."\n" ; $phone = $_REQUEST['phone']."\n" ; if ($_REQUEST['email'] != "") { $email = $_REQUEST['email'] ;} else { $email = "No email given";} $jaddress1 = $_REQUEST['jaddress1']."\n" ; if ($_REQUEST['jaddress2'] != "") { $jaddress2 = $_REQUEST['jaddress2']."\n" ;} $jcity = $_REQUEST['jcity']."\n" ; $delivery = $_REQUEST['delivery']."\n" ; $description = $_REQUEST['description']."\n" ; // Obtain file upload vars $file_upload = $_FILES['file_upload']['tmp_name']; $file_upload_type = $_FILES['file_upload']['type']; $file_upload_name = $_FILES['file_upload']['name']; // Mail variables $to = "" ; $subject = "KE RFQ"; $from = "From: $name ($email)\n" ; $headers = $from; $header = "Location: quote.php" ; $message = $from."\r\n\r\nMailing Address:\n".$maddress1.$maddress2.$mcity.$mpostal."\r\n\r\nContact Info:\n".$phone.$email."\r\n\r\nJob Location:\n".$jaddress1.$jaddress2.$jcity."\r\n\r\nJob Details:\n"."Deliver by ".$delivery.$description."Attached File - ".$file_upload_name; if (is_uploaded_file($file_upload)) { // Read the file to be attached ('rb' = read binary) $file = fopen($file_upload,'rb'); $data = fread($file,filesize($file_upload)); fclose($file); // Generate a boundary string $semi_rand = md5(time()); $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x"; // Add the headers for a file attachment $headers .= "\nMIME-Version: 1.0\n" . "Content-Type: multipart/mixed;\n" . " boundary=\"{$mime_boundary}\""; // Add a multipart boundary above the plain message $message = "This is a multi-part message in MIME format.\n\n" . "--{$mime_boundary}\n" . "Content-Type: text/plain; charset=\"iso-8859-1\"\n" . "Content-Transfer-Encoding: 7bit\n\n" . $message ."\n\n"; // Base64 encode the file data $data = chunk_split(base64_encode($data)); // Add file attachment to the message $message .= "--{$mime_boundary}\n" . "Content-Type: {$file_upload_type};\n" . " name=\"{$file_upload_name}\"\n" . "Content-Disposition: attachment;\n" . " filename=\"{$file_upload_name}\"\n" . "Content-Transfer-Encoding: base64\n\n" . $data . "\n\n" . "--{$mime_boundary}--\n"; } if (empty($fname) || empty($lname) || empty($maddress1) || empty($mcity) || empty($mpostal) || empty($phone) || empty($jaddress1) || empty($jcity) || empty($description)) { header( $header."?mess=missing"); } else { // $sendmail = mail($to, $subject, $message, $headers); // if ($sendmail) { header( $header."?mess=success" ); // } // else { // header( $header."?mess=fail"); // } } ?>

Two programming mistakes that I see so far:

  1. You need to specify an absolute URI for the Location header:

$absoluteURI = 'http://' . $_SERVER['HTTP_HOST'] . '/quote.php'; header('Location: ' . $absoluteURI);2. It is pointless to check if the variables are “empty” after having assigned them a non-empty value.

:cool: -//-

HI Atropos7, I tried adding the absolute URL but it made no difference in processing my attachment - it still shows as binary in the email body online, but works fine on my local server. I also have other email scripts running fine without this variable. Do you mean it needs to be there for attachments or just for good practice?

Also, I assume you’re referring to the ‘if’ statements for the email and address variables in your 2nd comment. How should I be supplying an alternate variable if they come in emtpy?

Nope. I’m talking about silly stuff like -

$jaddress1 = $_REQUEST[‘jaddress1’]."\n";
if (… empty($jaddress1) …)

Uh, with an assignment like that $jaddress1 would never be empty. If the linebreaks are for formatting the message body, put them in the message body instead.

I don’t mean either one. I mean that you didn’t follow the HTTP 1.1 specification -

[b]14.30 Location

The Location response-header field is used to redirect the recipient to a location other than the Request-URI for completion of the request or identification of a new resource. For 201 (Created) responses, the Location is that of the new resource which was created by the request. For 3xx responses, the location SHOULD indicate the server’s preferred URI for automatic redirection to the resource. The field value consists of a single absolute URI.

Location = “Location” : " absoluteURI"[/b]

It works because browser developers have to “cover up” the mistakes of other people sometimes in order to avoid having to point out that mistakes were made. Ask Microsoft about that :wink:

Oh, you didn’t mention that earlier. Interesting.

:cool: -//-

Yes, I guess I was in a hurry and neglected to include the actual problem with the form. It submits fine and includes the uploaded file, but on Dreamhost it just includes it as binary in the message body, whereas on my own test server it is attached properly to the email.

I wonder if it is a mime issue. What happens if you zip the file?

My website

I re-wrote the script and it works for me - but I’m using Firefox and Thunderbird on Windows.


It sounds like either you goofed on the message format or your mail client doesn’t understand the MIME type of the file.

To clarify, please let us know what you mean. Your script reads binary data from a file into memory as string, then encodes that data using base64 into a string too. It then sends an email message with this base64 string inside. Can you confirm that the message arrives with the base64 string inside? Because I’m not sure if you mean to say your seeing binary or base64.

In other words, if you look at a “raw” version of the message, you should see:

Content-Type: image/jpeg;
Content-Disposition: attachment;
Content-Transfer-Encoding: base64

Where you can tell the data has been encoded because the “gibberish” only includes letters, numbers and and slashes and equal signs.

:cool: -//-

silkrooster, I’m not sure how to zip the file first, but i’ll look into it and see if I can figure it out.

Atropos7, i’m not sure if it’s binary or base64, I’m pasting a sample below here, but to clarify, the only variable has been the host, as when it works fine on my own server, i’m testing with firefox and gmail, and the same on dreamhost when it doesn’t work.

When you rewrote the script, did you change anything else that might work for me?

MIME-Version: 1.0
Content-Type: multipart/mixed;

This is a multi-part message in MIME format.

Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

From:justin (No email given)

Mailing Address:
mail address
mail city
mail post code

Contact Info:
contact number
No email given

Job Location:
location address
location city

Job Details:
Deliver by
test online with attachment
Attached File - mail.php

Content-Type: application/octet-stream;
Content-Disposition: attachment;
Content-Transfer-Encoding: base64



The message format is correct, so its not a problem with the script.

To clarify, binary data will be made up of all 256 characters. When you encode binary data in base64, it is changed so that only 64 characters are used instead. What you are seeing in the raw message is obviously base64 so we can tell the problem is not with the script. I even decoded the data in the sample you posted successfully.

The message format is correct and valid. You are probably experiencing an issue with the MIME type being used. PHP files are text - so the message should have said “text/plain” instead of “application/octet-stream”, but of course that value came from your browser, not the script. Try uploading a zip file or image file and see if your browser uses a different MIME type.

Even then, the messages are sent to a mail server and you are using a mail client to read them, right? Well the mail client can decide to parse the message different ways to. So what mail client are you using anyways? Have your script send a message to

:cool: -//-

Ok, I sent you two messages, one from my dev server and a second from dreamhost, indicated in the message body

When I send the message with attachment using the script on my server via firefox, and receive that message in gmail, it works fine.

When I send the same attachment using the same script on dreamhost via firefox and receive in gmail, it shows as base64 in the body instead of attached.

Did you have different results?

[quote]When I send the message with attachment using the script on my server via firefox, and receive that message in gmail, it works fine.

When I send the same attachment using the same script on dreamhost via firefox and receive in gmail, it shows as base64 in the body instead of attached.[/quote]

Finally figured out what the problem was - another programmer error.

And once again the problem you have is due to you adding linebreaks to the end of strings. Here is the relevant code from your version:$from = "From: $name ($email)\n" ; $headers = $from; ... // Add the headers for a file attachment $headers .= "\nMIME-Version: 1.0\n" . "Content-Type: multipart/mixed;\n" . " boundary=\"{$mime_boundary}\"";You added the linebreak at the end of the assignment to $from. By doing so you broke the header section by adding a blank line. ie,

[code]From: name

MIME-Version: 1.0
Content-Type: multipart/mixed;
Personally, this is how I would handle things:[code]function add_linebreak($str) {
// Add the correct linebreak sequence to a string
return $str . “\r\n”;

// Specify the header lines as an array - without linebreaks!
$headers = array(
‘From: name ‘,
‘MIME-Version: 1.0’,
‘Content-Type: mixed;’,
’ boundary=“boundary”’

// Add linebreaks and join into a single string
$header_section = implode(array_map(‘add_linebreak’, $headers));[/code]This way the need to put special characters in certain spots is hard-wired into the logic instead of a series of convoluted quoted-strings.

:cool: -//-

That was it, taking out that line break worked perfectly, thanks so much!

I understand my code is probably poorly written, I am just getting into PHP, so I appreciate people like yourself taking time to help us beginners. I also used some of your tips to rewrite my code properly.

I am still curious as to why the script worked and attached the email normally on my dev server with the error in the code, but as long as it’s working, I can move on.

Thanks again!

You should probably bookmark and make a habit of looking everything up on that web site first then.

You’d notice for the mail() function it says that on Windows mail is handled completely different than on Linux/UNIX machines. No doubt someone who did the Windows code probably “covered up” the mistake of extra linebreaks in the headers too.

:cool: -//-

Thanks for the tip. I do make a habit of researching heavily before asking questions, including on, but this must have slipped past me or not registered.

Either way, sometimes it takes a personal explanation to understand something.