Chapter 12: Sending Email

Google

Basic Email Headers

Some systems will provide default headers; others won't. Since spammers often send email with missing or deformed headers, it's best to be certain that your headers are correct. Also, a number of mail systems won't deliver messages without proper headers - as part of their effort to reduce spam.

Have you ever gotten emails with your email address showing as the From: address or Subject: line? I've gotten thousands like that. The headers have been "spoofed" by spammers so that the mail system accepts the subject and/or headers passed to the mail function (in variables) instead of its defaults. That's pretty stupid - and lazy - if you ask me. It's a dead giveaway that the message is spam.

Send a test message to yourself, forward it to a friend who will forward it back to you. You'll see loads of header information that has been added by the systems it passed through. That's a good way to test whether your script is putting in the right headers. For this test, be sure that your email client is set to show the "full headers" as opposed to just To:, From: and Subject: - you can always switch it back later.

Of course, the bottom line is that you need to know how to write headers for emails you're sending from a PHP script. The header I used in the first section of this chapter was:

"Content-Type: text/plain; charset=us-ascii\nFrom:
  $name <$from>\nReply-To: $from\nReturn-Path: $from";

Let's look at the parts of the header. We've already covered the MIME type(s) and that's the first part of the header. If you need to use more than one MIME type, you separate them with semi-colons. The next part is the character set. Here I've used "charset=us-ascii" to say we want our message displayed in only U.S. ASCII characters.

The \n (newline) character tells the email reader program (Outlook, Eudora, etc.) that the next part of the header is on a separate line. Don't use any line breaks in your text editor because that won't work. Then we have the From: address and (optionally) the name of the person sending the message.

Some email readers prefer that the From address ($from in my examples) be enclosed by angle-brackets. Some don't like the brackets and others don't care one way or the other. Experiment to get the best results with as many email readers as you can.

The From: address is followed by a newline character (it's just a rule you have to follow) and then the Reply-To: address. Most of the time From: and Reply-To are the same address but you can make them different addresses if you have some reason to do that.

Sometimes you want to send a copy of a message to another address. There are two ways of doing that. One is to use a CC: field in the header. CC stands for Carbon Copy. The downside of CC is that everyone who gets a copy will see the all email addresses in the CC: field. And, yes, there can be more than one address in the CC: field (separated by commas). Here's some code to add a CC: field to the headers:

$Cc = copy2@a_domain.com,copy3@b_domain.com";
$additional_headers .= $Cc\n;

The other way is to use BCC: or Blind Carbon Copy. You can put two or more addresses in the BCC: field if you separate them with commas. This time, however, the extra addresses are not shown to everyone who gets a copy of the message. This is often done when you want to send a message to a list of different addresses and not expose everyone's address to everyone else on the list. Here's some code for adding a BCC: field:

$Bcc = copy2@a_domain.com,copy3@b_domain.com";
$additional_headers .= $Bcc\n;

The CC: and / or BCC fields should be inserted into the header right after the Reply-To: field. Notice that both of these fields are followed by a newline character. If you are sending a message automatically (as part of the action of a form, for example), you may have gotten extra addresses to use in the CC or BCC fields.

In that case, you wouldn't want to assign values to the $Cc or $Bcc variables in side a function. You'd pass the variable(s) in when you call the function. But if you were only using the CC or BCC field to send yourself a copy of the message, you could assign a value to one of the fields inside the function. Why? Because it would always be the same address. No need to pass it in every time, right?

Reply-To: is followed by a newline and the Return-Path. Again, this is usually the same address as From: and Reply-To: but you can change them if you want to. It's probably more trouble than it's worth unless you want replies to your messages sent to a special email address.

More complex messages require more complex headers, as you'll see in the next section.


Complex Email Headers

So you want to send something fancier? OK, but it's going to take a little more work. Let's start with a text message with a ZIP file attached. The first thing you have to do is "encode" the file in a way that can be sent as email.

You have to do that because a ZIP file is "binary data" and has a bunch of non-text characters in it. Those characters have meaning in a ZIP file but the email system doesn't understand them. So email uses base64 encoding to substitute text characters it does know how to handle. Here's how you do it:

$FileName = "goodies.zip";
$fp = fopen($FileName,"r");
$data = fread($fp, filesize($FileName));
fclose($fp);
$FileContents = chunk_split(base64_encode($data));

Whoa! What did we just do? Let's take it one step at a time. We assigned the actual name of the file (goodies.zip) to a variable we named $FileName. Then we used fopen() to open the file for reading and get a file pointer, $fp, to the file. Using the pointer, we read the contents of the file into $data. Since we didn't know the exact size of the file, we told the fread() function to use the number of bytes that filesize() reported as the number of bytes to read.

We closed the file, leaving its contents in the variable $data. The function base64_encode( ) took care of encoding the data into base64 format so we could send it. Then we used the chunk_split( ) function to break up the encoded data into small "chunks" that won't clog up the Internet's pipes. Finally, we copied the encoded and chunked data into the variable $FileContents. Now it's ready to send out as an email attachment.

You'll want the person who receives the message to know why they got an email with a file attached. So you should have at least a little bit of text to say that. There are many emails arriving with nasty things attached these days. You don't want your friends or customers to just delete the message because they thought it was a virus or something, do you?

Now you're going to see why the headers get complicated when you do this sort of thing. First, the message is in two parts: some text and an attached file. There has to be a main header to tell the person's email client what kind of message is coming and each part of the message has to have its own header.

Everything in the message now has to be part of the headers themselves and there has to be some way for the email reader to know where one part ends and another part begins. The thing that separates the parts of the message is called a boundary. The boundary can be whatever you want it to be. But it's a good idea to make some part of it unique to each message.

$separator = md5($name);

What I did here was to call the md5( ) function on the person's name to get a string of 32 letters and numbers. This is known as an md5 hash. It's supposed to be a unique sequence for any string you pass to it. It's certainly good enough for a boundary. Now we can start making a set of headers.

$additional_headers = "From: $name <$from>\n";Reply-To: <$from>\nReturn-Path: <$from>\n";
$additional_headers .= "MIME-Version: 1.0\n";
$additional_headers .= "Content-Type: multipart/mixed; boundary=\"{$separator}\";
$additional_headers .= "Content-Transfer-Encoding: 7bit\n";

I've added the MIME version, the content type and the name of the boundary. Then I told the system to expect 7-bit encoded data. Here's the next part of the header:

$additional_headers .= "This is a multi-part message in MIME format.\n\n" .
"--{$separator}\n" . "Content-Type:text/plain;
charset=\"iso-8859-1\"\n" . "Content-Transfer-Encoding: 7bit\n\n";

Here I've changed the character set to iso-8859-1 to make it look a little nicer for the reader. The "dot operator ( . ) lets us join strings together. The -- in front of the boundary is required here. This is going to be the text part of the message so I set the content type to text/plain. The text of the message has to be sent in this part of the header, so I'll add that next:

$additional_headers .= "Hello name,\n\nDo not delete this message!\n" .
"It contains the product you just paid for.\n\n";

Now we need another boundary marker and the information for sending the attached file.

$additional_headers .= "--{$separator}\n" .
"Content-Type: application/zip; name=\"{$FileName}\"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$FileContents . "\n\n" .
"--{$separator}--\n";

Notice that that the boundary marker is repeated at the end of the last part and that we have to add -- at the very end, just before the newline character. The string parts are all enclosed in parentheses but since $FileContents isn't technically a string it doesn't need them.

It would still be a good idea to test whether there is an email address to send your "package" to before trying to send it. I covered that already so I won't do it again here.

You can send as many attachments as you like. Just add another section to the headers for each file you want to attach. Set the Content-Type to the right MIME type for each attachment and remember that the last part is special. It has to end with:

--{$separator}--\n

Now the entire message is packed into the headers and all we have to do is call the mail() function to send it out:

mail($recipient,$subject,$body,$additional_headers);

Don't forget to pass a subject line to the mail() function. It won't need a body argument because the message body is already in the headers. $body can be empty or it can be left out altogether.

By following the steps I showed you for creating a function to send a text message, you can create one that sends attachments, too. Make it as specific or generic as you want to. It's your decision. You can pass in what you need from outside (the filename you want to send, etc.) and call mail() from inside your function after you've built up the headers.

A function like that can be called an autoresponder. It's capable of sending out an email with or without attachments. It can be triggered by some action a user takes, so it really is an automated email device.

Autoresponders

First, let me clarify something that a lot of people get confused about. There are two basic types of autoresponders - single or "one-shot" and sequential or "followup". Many hosting companies claim they are giving you "unlimited autoresponders". But most of the time, these are the one-shot type. They only send one message. You may be able to create as many as you want, but it's not a followup system.

They're handy for sending an automatic reply just to let someone know you actually got their email. With so much legitimate email being trashed by over-zealous spam filters these days, it's a good practice to confirm receipt. Why should someone have to wait for days to get an answer to an email that wasn't received? There's no reason for that at all.

One-shots are good for delivering reports, sending you form results and as a way of getting support requests reliably. So don't dismiss them as useless. But let's move on; there's a discussion of the sequential or followup variety of autoresponders in Chapter 18.

Previous Page   Table of Contents   Next Page

Copyright © 2004 Steve Humphrey