Sending e-mail from PHP - in Cambridge

Jon Warbrick
University of Cambridge Computing Service

Built-in to PHP is a function called 'mail' that allows PHP scripts to send mail. However it can be difficult to get this to work (especially with mail systems in Cambridge). These notes are intended to help.

I have to point out that I'm not a PHP expert. What follows comes from a few hours playing with PHP, along with a reasonably good understanding of how SMTP mail works. Suggestions for updates or amendments would be welcome.

Background

SMTP mail is more complicated than you think. There are two particular issues that you need to understand:

1) 'Envelope' vs 'Header' address

While it's being transported by SMTP, an SMTP mail message has two independent sets of sender and recipient addresses. One set of addresses are those that appear in the To:, From:, Cc:, etc. header lines. These are the addresses that mail programs display, and that human recipients of the message will normally rely on and use to construct replies, etc. However mail transport systems don't normally use these addresses, but instead rely on a set of 'Envelope' addresses for mail routing information. For most messages the same information will appear in the two sets of addresses, but this doesn't need to be the case.

This is exactly like a paper letter, which can have one set of sender and recipient addresses on the letter itself, and at least in theory a totally different set of addresses on the outside of the envelope. And it's what's on the outside of the envelope that used for delivering letters, or returning then if undeliverable.

2) When messages fail

Because SMTP mail is delivered by a 'store and forward' mechanism, there are broadly two ways in which failures can be reported to the sender. An attempt to send a message may fail immediately when the user submits it, in which case the user will be notified by error message or return code. Alternatively the transmission may fail later in the store and forward chain, in which case the only possibility is to send a message back to the sender.

The important point in all of this is that it's the Envelope Sender address (only) that will be used to report such a problem. If that address itself isn't valid then there is nothing that can be done with the failing message, other than throw it away or refer it to a human who may be able to sort out what's happening.

The problem with e-mail scripts

Because of the importance of the envelope sender address, the central University mail servers (ppsw, CUS, Hermes) will refuse to accept a message with an envelope sender address that they can tell is invalid. Unfortunately many functions used in web automation to send mail both fail to setup the envelope sender address correctly, and then fail to check that attempts to send mail have succeeded. Taken together, this can result in mail from such scripts simply vanishing.

The PHP mail() function

The mail() function is called as follows:

  bool mail ( string to, string subject, string message 
              [, string additional_headers 
              [, string additional_parameters]])

see http://www.php.net/manual/en/function.mail.php for details.

Note that mail() returns true if it manages to send a message and false if an error occurs. You should write your pages to take note of this, and probably display an apology to the user if an attempt to send fails - failure to do so may result in loss of messages.

Suitable settings of the error_reporting, display_errors, and log_errors parameters in php.ini will result in messages in the user's browser and/or in the web server error log if attempts to send mail fail. Use of these is strongly advised, at least when developing scripts that send mail.

1) Under Unix

PHP running under Unix always uses the host system's sendmail command to create mail messages. This will only work if the local mail system has been correctly configured. If not, it is likely to construct an envelope sender address from the user under which the script is running and the hostname of the computer it is running on. This will probably result in something like apache@foo.st-botolphs.cam.ac.uk which is unlikely to be an acceptable address. For users of the Exim mail transfer program, Tony Finch of the Computing Service has an example configuration which can be appropriate for a machine in the University - see http://www-uxsup.csx.cam.ac.uk/~fanf2/conf4.satellite.

A possible alternative is to tell sendmail what address to use as the envelope sender with its -f option, e.g.

  -f webmaster@st-botolphs.cam.ac.uk 

You can specify this either using the 5th (additional_parameters) argument to the mail() function, or by setting the sendmail_path parameter in your php.ini file. However this will normally cause sendmail to add a 'X-Authentication-Warning:' header to your message, noting that the envelope sender has been overridden - this can be avoided by suitable configuration of the mail system, but that's outside the scope of this document.

2) Under Windows

PHP running under Windows needs to establish a network connection to a computer running a mail server to create a message. This server could be running on the same system as the script, but for most Windows boxes their won't be one. Systems in the University can use the central mail switch, ppsw.cam,ac.uk, for this purpose. There are two parameters that control this, SMTP that sets the name of the SMTP server to use, and sendmail_from which sets the envelope sender address that will be used. These parameters can either be set in the php.ini file (in which case they will apply by default to all scripts) or by using the ini_set() function to set them on a case by case basis.

Note that under Windows the attempt to send a message will fail if the selected mail server can't be contacted. While it's unusual for ppsw.cam.ac.uk to be unavailable it can happen, and network connectivity problems could also lead to problems. For this reason, Windows PHP developers should definitely check the return status from the mail() function and take appropriate action following failure. Quite what that action should be will depend on the individual case.