Introduction to sending email from PHP scripts

Understanding the Mail Function

It is a very common task to send input data from web form to email. In this tutorial, we will look at how to generate and send mail with PHP.

Introduction to sending email from PHP scripts

PHP provides a convenient way to send email through the mail() function. This PHP function is a great way to send mail using a simple and standard interface that hides the complexity and quirks of various system programs that are responsible for sending mail. (Sendmail and Qmail are two examples of popular mail programs you would have to talk to directly if the mail() function did not exist). This tutorial will introduce you to the basics of sending mail from PHP scripts and making a simple feedback form.

The mail() function accepts three required values and one optional value. It returns a value indicating success or failure. The email address of the recipient (or To: field), the subject of the message (or Subject: field), and the message body can all be specified through parameters. An optional string containing extra headers can be specified.

To send a message, you simply fill in the parameters. This example uses literal strings to illustrate the general usage of the function. The first parameter specifies the email address we are sending mail to. The second is the message subject. I used the third optional parameter to set the "From:" header to an address where I wanted replies to go and added a "X-Mailer:" header to help mail administrators identify the message as originating from an automated source.

When you create a custom header string, remember to separate each additional header with a linefeed ( ). Otherwise, the message text may become garbled. Messages sent through the mail() function are in plain text.

Most of the time, you won't be specifying these values using literal strings, but through variables. You may want to obtain these values from an included php script fragment (an "initialization file") or from a form submission. It's likely you'll want to use values submitted by a form or generated programmatically to send a message. In that case, we substitute string variables for the literal strings used in the previous example.

<?php

mail ($strMailTo, $strSubject, $strBody, $strXHeaders);

?>

Adding Extra Header Information

As I mentioned, the mail() function allows you to specify additional header fields to be added to the message header. One use for the extra headers to identify the message as having been generated programmatically. Any script that generates mail can be thought of as a "remailer." Often, it is useful for a form to send mail that appears to come from the person submitting the form. One such use is when setting up a form that subscribes a user to a mailing list. The following code rewrites the From: field to the user's email address and adds a X-Header identifying the mail as being sent from your custom remailer script. Identifying mail as coming from your script gives administrators a heads up that mail is coming from an automated source, if there should be a problem with mail generated by your script. I used the phpversion() function to identify the message as coming from a php script (as the php manual shows).

Some list servers require that the user's first and last names be available in the From: field. To satisfy them, add form variables for the names into the From: field in the extra headers. It's necessary to "escape" the double quotes within a double quote context. This is accomplished by prefixing (or "escaping", which is the fancy term for protecting a character from misinterpretation) the quotes surrounding the first and last names with the backslash character. The email address is enclosed by angle brackets to help differentiate it from the surrounding text, as is usual practice in net email.

When to Use the Mail Function

Having such a simple and uncomplicated function for sending mail is useful when a script must send several messages. It takes only one line of code (unless you create your own function for sending mail), so helps keep code readable if the mail() functions must appear in a conditional statement. It's also nice to be able to communicate with the mail program without worrying about the command syntax or security measures required to execute a Unix mail program. Although the interface to sendmail or qmail is standardized, using the mail function protects your script to a degree from any changes to sendmail an administrator might make. Using the mail() function means you do not have to create your own function for sending mail or duplicate code inline. The following script gives an example of using more than one mail() function in a script to notify the administrator when a form submission is made and at the same time, respond to the user by email. (Please keep in mind this is just a sketch showing what is possible, not a working example).

Talking to Sendmail

Of course, php let's you call the system mail program directly. You might want to do this in order to use a special feature only available in a certain mail program. Another reason to talk directly to sendmail is to create a From: header or other custom header in a more readable manner.

Talking to the mail program is like writing to a file. (Perl programmers will be familiar with this technique). Instead of opening a file, the (popen() function specifies a program to pipe (communicate in a standard Unix way) your message to. This makes a connection to the mail program. The puts() function is used to "print" the message headers and message body out to the mail program, connected through the file descriptor already opened with the popen() function. Because you may want to use a different mail program than sendmail, it's a good idea to store this path in variable. You can store the recipient address in a variable to make it easy to change. Here is a simple script to send a message by talking directly to the sendmail program.

The popen() function opens a pipe to the mail program. To open the pipe, you must give the function the name of the program to pipe to and set the type of communication to make, the "w" standing for "write," which tells the open function to pipe the information from php to sendmail. If it said "r" it would open the pipe in the opposite direction, sending information to php from the application. The fputs() functions then write out each line of the message to sendmail. This function requires we give it a file descriptor and the string we want to output. The file descriptor, which we obtained when the file (in Unix, devices masquerade as files) was opened tells where to send the message. The message itself is contained in the string. When we are done, the pipe is closed with the pclose() function.

Note: Specify the -t option when working directly with sendmail to reduce the chance of abuse. From the sendmail documentation: The '-t' option to 'sendmail' instructs 'sendmail' to parse the headers of the message, and use all the recipient-type headers (i.e. 'To:', 'Cc:' and 'Bcc:') to construct the list of envelope recipients. This has the advantage of simplifying the 'sendmail' command line, but makes it impossible to specify recipients other than those listed in the headers.

One reason to access the mail program directly is to set the envelope email address. If you operate a mailing hosted at another provider, you may find that when you send mail using the simple mail() function that the messages are rejected for not having a matching envelope address. This happens because the address you are sending from and the envelope address do not match. By directly accessing sendmail, we can alleviate this problem.

Sendmail Security

When calling the system mail program we must be careful of what characters we are sending to it. Becuase we are opening a Unix pipe, it is possible for malicious users to enter shell meta characters into form inputs that later are passed to sendmail. The results can be disastrous.

When creating a form handling script that eventually hands off user entered data to the mail program, you must screen user input carefully. Treat all user input as if it were hostile. Start by removing shell meta characters from any input used by sendmail, like the To: and From: inputs or even teh Subject: input of a feedback form.

One character that is important to remove is the period. Because the period is used for Unix shell commands such as to represent generic directory names.

While reading a discussion of Perl programming, an approach was suggested that has enough merit that it changed the way I look at user input. I want to share it here. The idea is to use a system of inclusion rather than exclusion when sanitizing input data. Instead of saying "give me the string that results after taking out these specific characters", I say "give me the string that results after taking out any characters that are not these specific characters". It seems safer and easier to identify what I know I want rather than attempt to identify every possible bad scenerio that could happen.

It was suggested that to process user input for saftey, use code like this:

$data =~ s/[^A-Za-z0-9_]//gs;

It removes all characters that are not letters, numbers or underscores.

You may use the Perl compatible regular expression feature (see function preg_replace()) of PHP to implement this.

Sending Mail from a Form

Of course, any of these headers or the message body can be specified using variables. This is a full treatment of the previous script, expanding it into a simple form handler. It introduces variables and divides the code into a small configuration section and the familiar sendmail script. The first section of PHP code serves as brief configuration area for setting up the values of key variables. It is helpful to store the path to your mail program in a variable so you don't have to hunt through code when it comes time to change it. The second does the work of sending the mail. In true php fashion, it intermingles HTML and PHP code to conditionally prompt the user to fill out the form or mail information collected by the form to the site administrator. Including the email address of the person submitting the form in the "From:" field makes it easy for the person receiving the mail to reply to the submitter - they just have to hit 'Reply' in their Eudora or their favorite email application. Following the internet convention, the "From:" field is formatted by placing the email address angle brackets:

"From: $frmName <$frmEmail> "

Each form input has a variable name. When the form is submitted, PHP automatically creates variables using the names given in the NAME attribute in the form for each input using the values entered by the user. These variables are available from within the PHP script. For each variable a line is added to the sendmail output printing that value in the message.

<?php

/* Configuration -----------------------------------------*/
/* Mail results to this address */
/* Set this to the email address you wish to receive mail */
/* from the form submissions at. */
$TO = "YourEmailId@YourISPDomain.SomeTopLevelDomain";
/* Specify system mail program */
/* Set this to the path to your mail program. Check with */
/* your server administrator for the proper location. */
$MP = "/usr/sbin/sendmail -t";

/*-------------------------------------------------------*/
/* Decide if we should display a new form or send the */
/* form data by email. */
/* To make this decision, the script can check for the */
/* existence of 1) the action variable defined by a */
/* hidden field; 2) a required form field that you know */
/* will always be set on submission; or you may set the */
/* action variable to a particular value that can be */
/* checked to determine the action to take. I chose */
/* to simply check for the existence of the hidden */
/* action variable (which is always set as long as we */
/* give a value in the hidden field). */
/*-------------------------------------------------------*/

if ($frmAction)
{

/*-------------------------------------------------------*/
/* A thank you message (or other response) goes here. We */
/* switch to HTML mode to make it easy to include any */
/* tags you wish without worrying about quoted */
/* attributes. */
/*-------------------------------------------------------*/

/*-------------------------------------------------------*/
/* The real work gets done here by opening a pipe to */
/* sendmail, which sends the contents of the submitted */
/* form by email to the address specified in the */
/* configuation section (which can acutally be an */
/* an included initialization file if you want to get */
/* fancy). For each variable we expect the form to */
/* to submit, we output as part of the email. */
/*-------------------------------------------------------*/

/*-------------------------------------------------------*/
/* Here the script must exit so we don't display the */
/* form again once the thank you message has been */
/* displayed and the mail sent. */
/*-------------------------------------------------------*/