Sendmail with attachment

apps

#1

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.


#2

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

-Scott


#3

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


#4

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

-Scott


#5
<?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 = "example@example.com" ; $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"); // } } ?>

#6

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: openvein.org -//-


#7

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?


#8

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: openvein.org -//-


#9

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.


#10

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

My website


#11

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

See http://atropos.openvein.org/Web/PHP/MailAttachment.txt

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

Edit.
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:

[code]–==Multipart_Boundary_x{1e5171cf1975489ae1c2f7de0b2342db}x
Content-Type: image/jpeg;
name="cover.jpg"
Content-Disposition: attachment;
filename="cover.jpg"
Content-Transfer-Encoding: base64

/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAcFBQYFBAcGBQYIBwcIChELCgkJChUPEAwRGBUaGRgV
…[/code]
Where you can tell the data has been encoded because the “gibberish” only includes letters, numbers and and slashes and equal signs.

:cool: openvein.org -//-


#12

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;
boundary="==Multipart_Boundary_xb9125cdf2f6556f836bc6209e455a6b7x"

This is a multi-part message in MIME format.

–==Multipart_Boundary_xb9125cdf2f6556f836bc6209e455a6b7x
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

–==Multipart_Boundary_xb9125cdf2f6556f836bc6209e455a6b7x
Content-Type: application/octet-stream;
name="mail.php"
Content-Disposition: attachment;
filename="mail.php"
Content-Transfer-Encoding: base64

PGh0bWw+DQo8aGVhZD4NCjx0aXRsZT4gU2VuZGluZyBFbWFpbCA8L3RpdGxlPg0KPC9oZWFkPg0K
PGJvZHk+DQo8P3BocA0KLy8gUmVhZCBQT1NUIHJlcXVlc3QgcGFyYW1zIGludG8gZ2xvYmFsIHZh
cnMNCiR0byAgICAgID0gJF9QT1NUWyd0byddOw0KJGZyb20gICAgPSAkX1BPU1RbJ2Zyb20nXTsN
CiRzdWJqZWN0ID0gJF9QT1NUWydzdWJqZWN0J107DQokbWVzc2FnZSA9ICRfUE9TVFsnbWVzc2Fn
ZSddOw0KDQovLyBPYnRhaW4gZmlsZSB1cGxvYWQgdmFycw0KJGZpbGVhdHQgICAgICA9ICRfRklM
RVNbJ2ZpbGVhdHQnXVsndG1wX25hbWUnXTsNCiRmaWxlYXR0X3R5cGUgPSAkX0ZJTEVTWydmaWxl
YXR0J11bJ3R5cGUnXTsNCiRmaWxlYXR0X25hbWUgPSAkX0ZJTEVTWydmaWxlYXR0J11bJ25hbWUn
XTsNCg0KJGhlYWRlcnMgPSAiRnJvbTogJGZyb20iOw0KDQppZiAoaXNfdXBsb2FkZWRfZmlsZSgk
ZmlsZWF0dCkpIHsNCiAgLy8gUmVhZCB0aGUgZmlsZSB0byBiZSBhdHRhY2hlZCAoJ3JiJyA9IHJl
YWQgYmluYXJ5KQ0KICAkZmlsZSA9IGZvcGVuKCRmaWxlYXR0LCdyYicpOw0KICAkZGF0YSA9IGZy
ZWFkKCRmaWxlLGZpbGVzaXplKCRmaWxlYXR0KSk7DQogIGZjbG9zZSgkZmlsZSk7DQoNCiAgLy8g
R2VuZXJhdGUgYSBib3VuZGFyeSBzdHJpbmcNCiAgJHNlbWlfcmFuZCA9IG1kNSh0aW1lKCkpOw0K
ICAkbWltZV9ib3VuZGFyeSA9ICI9PU11bHRpcGFydF9Cb3VuZGFyeV94eyRzZW1pX3JhbmR9eCI7
DQogIA0KICAvLyBBZGQgdGhlIGhlYWRlcnMgZm9yIGEgZmlsZSBhdHRhY2htZW50DQogICRoZWFk
ZXJzIC49ICJcbk1JTUUtVmVyc2lvbjogMS4wXG4iIC4NCiAgICAgICAgICAgICAgIkNvbnRlbnQt
VHlwZTogbXVsdGlwYXJ0L21peGVkO1xuIiAuDQogICAgICAgICAgICAgICIgYm91bmRhcnk9XCJ7
JG1pbWVfYm91bmRhcnl9XCIiOw0KDQogIC8vIEFkZCBhIG11bHRpcGFydCBib3VuZGFyeSBhYm92
ZSB0aGUgcGxhaW4gbWVzc2FnZQ0KICAkbWVzc2FnZSA9ICJUaGlzIGlzIGEgbXVsdGktcGFydCBt
ZXNzYWdlIGluIE1JTUUgZm9ybWF0LlxuXG4iIC4NCiAgICAgICAgICAgICAiLS17JG1pbWVfYm91
bmRhcnl9XG4iIC4NCiAgICAgICAgICAgICAiQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy
c2V0PVwiaXNvLTg4NTktMVwiXG4iIC4NCiAgICAgICAgICAgICAiQ29udGVudC1UcmFuc2Zlci1F
bmNvZGluZzogN2JpdFxuXG4iIC4NCiAgICAgICAgICAgICAkbWVzc2FnZSAuICJcblxuIjsNCg0K
ICAvLyBCYXNlNjQgZW5jb2RlIHRoZSBmaWxlIGRhdGENCiAgJGRhdGEgPSBjaHVua19zcGxpdChi
YXNlNjRfZW5jb2RlKCRkYXRhKSk7DQoNCiAgLy8gQWRkIGZpbGUgYXR0YWNobWVudCB0byB0aGUg
bWVzc2FnZQ0KICAkbWVzc2FnZSAuPSAiLS17JG1pbWVfYm91bmRhcnl9XG4iIC4NCiAgICAgICAg
ICAgICAgIkNvbnRlbnQtVHlwZTogeyRmaWxlYXR0X3R5cGV9O1xuIiAuDQogICAgICAgICAgICAg
ICIgbmFtZT1cInskZmlsZWF0dF9uYW1lfVwiXG4iIC4NCiAgICAgICAgICAgICAgLy8iQ29udGVu
dC1EaXNwb3NpdGlvbjogYXR0YWNobWVudDtcbiIgLg0KICAgICAgICAgICAgICAvLyIgZmlsZW5h
bWU9XCJ7JGZpbGVhdHRfbmFtZX1cIlxuIiAuDQogICAgICAgICAgICAgICJDb250ZW50LVRyYW5z
ZmVyLUVuY29kaW5nOiBiYXNlNjRcblxuIiAuDQogICAgICAgICAgICAgICRkYXRhIC4gIlxuXG4i
IC4NCiAgICAgICAgICAgICAgIi0teyRtaW1lX2JvdW5kYXJ5fS0tXG4iOw0KfQ0KDQovLyBTZW5k
IHRoZSBtZXNzYWdlDQokb2sgPSBAbWFpbCgkdG8sICRzdWJqZWN0LCAkbWVzc2FnZSwgJGhlYWRl
cnMpOw0KaWYgKCRvaykgew0KICBlY2hvICI8cD5NYWlsIHNlbnQhIFlheSBQSFAhPC9wPiI7DQp9
IGVsc2Ugew0KICBlY2hvICI8cD5NYWlsIGNvdWxkIG5vdCBiZSBzZW50LiBTb3JyeSE8L3A+IjsN
Cn0NCj8+DQo8L2JvZHk+DQo8L2h0bWw+DQo=

–==Multipart_Boundary_xb9125cdf2f6556f836bc6209e455a6b7x–


#13

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 mystic.marauder@openvein.org

:cool: openvein.org -//-


#14

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?


#15

[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;
boundary=“boundary”;[/code]
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: openvein.org -//-


#16

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!


#17

You should probably bookmark www.php.net 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: openvein.org -//-


#18

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

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