Intercepting Email with PHP

Here I will show you how to intercept an incoming email through piping. I have also written a guide on How to Access Email in a Mailbox through IMAP

One of the most frequent questions I’m asked, or I see asked on forums is “how do I send out an email using PHP?” The answer to that is fairly simple and well documented. It more or less involves the use of a single function mail().

Something a bit more complicated and, I think, more interesting is how you can intercept an incoming email with a pretty small chunk of PHP code. The answer to how to do this, though not so difficult to find out, is a far less trivial thing.

The Scenario (optional if you don’t feel like reading)

Say we are building a contact form for a web application. This pretty basic contact form takes whatever input  the user gives, to keep things simple we’ll just say its the users email address and a message. This message is then sent to a database where it can be read from an administrative control panel of some sort. This is pretty simple and for the most part effective. Personally I have a problem with this type of setup. If users are not logged in when using the form there is no guaranteed way to respond. The email address they provide may or may not be valid, or they could just be spamming some nonsense because they are anonymous in the Internet and no one can stop them. A simple solution: Force users to log in. But Wait! what if users are having trouble creating an account or logging in and this is the reason why thy need to contact you? OK, to solve this problem we’ll just set up an email address that users can contact us with in addition to the form. This solves the problem of reliable contact information and minimizes the spam problem (the email address can still be spammed but a lot of it can be filtered). So once this is all setup will have a contact form that sends info that can be read by all of our admins in our admin panel… and the occasional email to some email account that all of our admins will need the password to to check regularly.

This may be acceptable to some… but not to us! We know of a better way to consolidate all of our communications with our users while at the same time having our desired setup. OK maybe you don’t or else why would you read this. Here’s how we do it:

The Code(this is where you should start reading if you like to get to the point)

Some of you may be thinking that PHP is so magical that there is a simple function that is going to go out and fetch the email we want from where ever it is on the server. Well you’re wrong (not about the magical part). What we need to do is setup a script that can capture the email when the server recieves it. This is actually pretty simple, all we need to do is extract the email data from standard input (php://stdin).

$file = fopen('php://stdin', 'r');

Once we establish a file pointer to our input stream the rest is the same as reading in any other file. Here’s the complete code:

< ?php
$data = '';
$file = fopen('php://stdin', 'r');
while(!feof($file))
{
     $data .= fgets($file, 4096);
}
fclose($file);
?>

$data now stores the raw content of the email, headers and body. At this point we just need to decide what to do with the email, I’ll leave that for you to decide. So other than that our script is complete correct? False. We have not yet addressed one crucial step. The server needs to be told how to execute this script. You may be wondering, why would we have to tell the server what to do with a .php file… its .php! The main difference between a user requesting a .php file via the internet and what we are doing here is what is handling the request. When a user goes to index.php on your website, apache knows what to do with the file because that is how it has been configured. The server on the otherhand will not necissarily assume that the file should be parsed by php. So how do we tell it to you say? Hashbang. A hashbang tells the server how to handle a file, in our case we will use the following hashbang #!/usr/bin/php -q. Note that this may or may not work for you depending on the path to php on your server. The one I’m using is the default, so if you’re not sure use that one, otherwise contact your hosting provider. Ok so here is our completed, hashbanged code:

#!/usr/bin/php -q
< ?php
$data = '';
$file = fopen('php://stdin', 'r');
while(!feof($file))
{
     $data .= fgets($file, 4096);
}
fclose($file);
?>

Note that the hashbang is outside of the php tags. This is absolutely necissary for the script to work. Save this code and take note of the path to it.

The Setup

Now that we’ve written our script we need to tell the server what to do with incoming email. This setup is going to vary depending on your server configuration, I’ll tell you how to do it on a linux server with sendmail and cpanel but for anything else you’re on your own (or you can post back explaining how and I’ll add it to the tutorial). What we need to do is setup a forwarder. If you have cPanel this is fairly straightforward. In your mail section find the forwarders option and click it. From there click add new forwarder. At this point you’ll have a few different options: you can forward to another address, discard and send an error message, do nothing, or pipe to a program. We want to pipe to a program. Just type in the address that you want piped, select the pipe option and then set the path to the path to the file you just saved. If you have access to your home directory, you can forego using cpanel and use a .forward file. All you have to do is create a file called .forward and add the following line:

email@address.com,”|/path/to/script.php”

Of course replace the email address with the address you want forwarded and /path/to/script.php with the actual path to the script. You can also omit the email address portion to have all mail forwarded to the script. I believe this process is the same using Exim or Qmail.

Well that’s it for the most part. Feel free to post back with any problems you have or additions you’d like to make. I’ll write about taking a raw email and converting it to a more friendly format, in this case an associative array, in the near future.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 

5 thoughts on “Intercepting Email with PHP

  1. Is it possible to have only the subject and content of the email caught by the php script? Also, how are attachments handled?

  2. Pingback: Do Not Reply | Tinsology

  3. Regarding file_get_contents vs fopen/fgets
    If you only intend to read the contents of a file, file_get_contents is preferable, however they should be functionally identical.

    Regarding your other question, addresses that an email is Bcc’ed to are intentionally left out of the headers. The only way to determine the desired recipient in this case is to look at the email address the message was sent to, and not the content of the message. In order to do this you will need to modify each of your forwarders to call your script with the email address as a parameter, like so:

    email@address.com,”|/path/to/script.php email@address.com

    You can then use the $argv array to read the parameter. One problem with this method is it requires that each email address be piped individually, so if you are having all of your email piped you will need to do this some other way. Someone a bit more familiar with shell scripting than myself might be able to help you with this.

  4. wow i wish i had seen this before i started a project some time ago. one comment, i’ve been using file_get_contents() to open the email so i don’t have to specify how long the maximum line has to be. is there any disadvantage to using file_get_contents(“php://stdin”) over $file = fopen(‘php://stdin’, ‘r’); while(!feof($file)){$data .= fgets($file, 4096);} ?

    also, since this seems to be a field of expertise for you i was wondering if you know of any way to identify who the email is intended for other than reading the raw email file? my problem is that when someone bcc’s the email and it gets passed to a default email script i do not know of a way to identify the recipient through php since the bcc is not included in the header. thanks!