Introduction
Step one: creating your script
Step two: getting the permissions correct
Step three: getting the piping working
Step four: extraneous carriage returns
Step five: troubleshooting
Step six: your script's environment and testing
Over time I've encountered various problems when trying to pipe mail to php scripts, and from time to time people ask how this can be done. I thought I would then post a quick howto and list of things to check if you encounter errors. You should find that this covers all problems and errors you might encounter.
Step one: creating your script
Your script can, of course, do whatever it likes. However there are two things it will certainly have to do: specify what should be used to interpret it and read the contents of the email piped to it. The the email can be read from STDIN and, maybe surprisingly, an error will be bounced back to the sender if this isn't done.
First things first: the shebang line
It should be the very first line in the script and must specify the path to the php binary.
Suitable values may be:
On my system (CentOS), the first is the public version of php installed by cPanel. This is generally recommended. The second is, from what I can tell, the OS installation of php (the file modification time on the binary appears to match the OS install date). The third is my separate php5 installation. I've included this to point out that you can pick and choose which version of php if the options is indeed there.
Note the '-q' parameter. This suppresses header output as sometimes php just has to output something. If anything is output by php, the sender will receive a bounceback message containing, among other things, whatever php chose to output. We don't really want this.
Reading the email from STDIN
Below is a rather simple function to do the job for you. This is the same as the function I previously posted (http://forums.cpanel.net/showthread.php?t=39083), with a few more comments to explain what it does.
Note: if a connection to STDIN can't be established, an error message is output. Any output, as I mention above, will cause a bounceback to the sender. In this case if the email can't be read from STDIN, we'll get a whole load of errors anyway, so we might as well try and give some meaning to the errors.
Be careful when setting the size limit ($iKlimit). If this is smaller than the full size of the message, not all of the message will be read from STDIN and a bounceback message may be returned to the sender. I say 'may' as I've seen this behaviour vary from server to server.
If you're only expecting 'normal' emails, you should be safe with setting the limit as -1 so that the entire message is read in. The limit only really comes in useful if you want to stop too much of a message being read in and if you don't care too much about bouncebacks.
For example, if someone sends an email with a 100MB attachment and your script tries to read in the entire thing it will most likely fail due to the memory limit imposed on php. Set the limit to roughtly 1MB less than the php memory limit if in doubt.
That covers the essentials of what your script should contain. Next let's look at permissions.
First things first: the shebang line
It should be the very first line in the script and must specify the path to the php binary.
Suitable values may be:
Code:
#!/usr/bin/php -q
#!/usr/local/bin/php -q
#!/usr/local/php5/bin/php5 -q
Note the '-q' parameter. This suppresses header output as sometimes php just has to output something. If anything is output by php, the sender will receive a bounceback message containing, among other things, whatever php chose to output. We don't really want this.
Reading the email from STDIN
Below is a rather simple function to do the job for you. This is the same as the function I previously posted (http://forums.cpanel.net/showthread.php?t=39083), with a few more comments to explain what it does.
PHP:
function mailRead($iKlimit = "")
{
// Purpose:
// Reads piped mail from STDIN
//
// Arguements:
// $iKlimit (integer, optional): specifies after how many kilobytes reading of mail should stop
// Defaults to 1024k if no value is specified
// A value of -1 will cause reading to continue until the entire message has been read
//
// Return value:
// A string containing the entire email, headers, body and all.
// Variable perparation
// Set default limit of 1024k if no limit has been specified
if ($iKlimit == "") {
$iKlimit = 1024;
}
// Error strings
$sErrorSTDINFail = "Error - failed to read mail from STDIN!";
// Attempt to connect to STDIN
$fp = fopen("php://stdin", "r");
// Failed to connect to STDIN? (shouldn't really happen)
if (!$fp) {
echo $sErrorSTDINFail;
exit();
}
// Create empty string for storing message
$sEmail = "";
// Read message up until limit (if any)
if ($iKlimit == -1) {
while (!feof($fp)) {
$sEmail .= fread($fp, 1024);
}
} else {
while (!feof($fp) && $i_limit < $iKlimit) {
$sEmail .= fread($fp, 1024);
$i_limit++;
}
}
// Close connection to STDIN
fclose($fp);
// Return message
return $sEmail;
}
Be careful when setting the size limit ($iKlimit). If this is smaller than the full size of the message, not all of the message will be read from STDIN and a bounceback message may be returned to the sender. I say 'may' as I've seen this behaviour vary from server to server.
If you're only expecting 'normal' emails, you should be safe with setting the limit as -1 so that the entire message is read in. The limit only really comes in useful if you want to stop too much of a message being read in and if you don't care too much about bouncebacks.
For example, if someone sends an email with a 100MB attachment and your script tries to read in the entire thing it will most likely fail due to the memory limit imposed on php. Set the limit to roughtly 1MB less than the php memory limit if in doubt.
That covers the essentials of what your script should contain. Next let's look at permissions.
Step two: getting the permissions correct
When uploaded, php scripts commonly have permissions of 0644 and directories have permissions of 0755. This is not quite right for what we want to do. For mail to be piped to scripts, both the script and the directory it is in should have permissions of 0755.
Step three: getting the piping working
This is the dead easy bit as you just need to specify the correct destination for an email filter within cPanel.
Go to cPanel > Mail > E-mail Filtering > Add Filter. Enter whatever you like for the filter and then specify the destination as '|/home/username/path/to/script.php' (without the quotes).
Note: the first character is the pipe character. For me with my UK keyboard, this is typed by pressing shift and the key to the left of z.
You can pop your php script anywhere you like within your webspace and it doesn't need to be in your public_html directory. In fact, it would generally be better if it is not in your public_html directory to prevent people from directly browsing to it.
Go to cPanel > Mail > E-mail Filtering > Add Filter. Enter whatever you like for the filter and then specify the destination as '|/home/username/path/to/script.php' (without the quotes).
Note: the first character is the pipe character. For me with my UK keyboard, this is typed by pressing shift and the key to the left of z.
You can pop your php script anywhere you like within your webspace and it doesn't need to be in your public_html directory. In fact, it would generally be better if it is not in your public_html directory to prevent people from directly browsing to it.
Step four: extraneous carriage returns
Sometimes php is picky and doesn't favour carriage returns in scripts. I found that one server of mine doesn't care, whereas another does. I still can't figure out why. To be on the safe side, you shouldn't have any carriage return characters in your script.
These will generally be introduced if you are using a Windows-based editor. Unix-based editors use a single line feed (LF) character to denote a new line, whereas Windows-based editors often use a carriage return and line feed (CRLF). These extraneous CR characters aren't appreciated. A good editor will let you specify the line feed character to use - pick the unix/linux type if you can.
To be on the safe side, the dos2unix command can fix things for you. This can be run from a shell window as follows:
These will generally be introduced if you are using a Windows-based editor. Unix-based editors use a single line feed (LF) character to denote a new line, whereas Windows-based editors often use a carriage return and line feed (CRLF). These extraneous CR characters aren't appreciated. A good editor will let you specify the line feed character to use - pick the unix/linux type if you can.
To be on the safe side, the dos2unix command can fix things for you. This can be run from a shell window as follows:
Code:
dos2unix /path/to/script.php
Step five: troubleshooting
If you've written your script correctly, set the permissions on it and it's directory fine, and created your email filter correctly everything will work. Chances are it won't work your first attempt.
Checklist
If in doubt, quickly check that all of the following are correct:
1. You have the correct shebang line
2. Your script reads data from STDIN
3. Your script has permissions of 0755
4. The directory in which your script lives has permissions of 0755
5. Your filter is set up properly. Use the tester in cPanel to ensure that mail is being picked up by the filter. Double check that the path to your script is correct.
6. You have no extraneous carriage returns
If any of the above are not correct, you'll encounter errors. Even if they are all correct, you may still encounter errors. So what should you do if you come across an error?
Troubleshooting
You'll encounter two different types of error: errors with anything mentioned on the checklist or errors with the coding of the php script.
In either case, the sender will get an email back titled 'Mail delivery failed: returning message to sender'.
Scroll to the bottom of the email. If there are php-based error messages, there will be a syntactical or logical error with your code. If not, go through the above checklist again as it will be due to one of these things.
That's about it for troubleshooting. Go through the checklist to ensure that everything is set up correctly and study php-based errors in bounceback emails and correct coding errors.
Checklist
If in doubt, quickly check that all of the following are correct:
1. You have the correct shebang line
2. Your script reads data from STDIN
3. Your script has permissions of 0755
4. The directory in which your script lives has permissions of 0755
5. Your filter is set up properly. Use the tester in cPanel to ensure that mail is being picked up by the filter. Double check that the path to your script is correct.
6. You have no extraneous carriage returns
If any of the above are not correct, you'll encounter errors. Even if they are all correct, you may still encounter errors. So what should you do if you come across an error?
Troubleshooting
You'll encounter two different types of error: errors with anything mentioned on the checklist or errors with the coding of the php script.
In either case, the sender will get an email back titled 'Mail delivery failed: returning message to sender'.
Scroll to the bottom of the email. If there are php-based error messages, there will be a syntactical or logical error with your code. If not, go through the above checklist again as it will be due to one of these things.
That's about it for troubleshooting. Go through the checklist to ensure that everything is set up correctly and study php-based errors in bounceback emails and correct coding errors.
Step six: your script's environment and testing
The way things are
Your script won't be running in the same enviroment as if it were being browsed to. This will affect the superglobal arrays you might often use.
In fact the only superglobal array that contains anything useful is $_SERVER. And most of this is generally of no use. One value that is of use is $_SERVER['argv'].
For scripts executed from the command line, $_SERVER['argv'] will contain the arguements passed to the script in the form of an array. In the case of mail piping, this (at least for me) contains only one element and that is the full path to the script i.e. $_SERVER['argv'][0].
If find this useful if I want to include common library files that are used elsewhere within the same account. I normally deal with common library files by setting the include path, but that won't work here - your script is on it's own and has no idea where everything else is. But if you know the path to the script, you can navigate round to where your library files are without having to hardcode the exact path.
Testing
The process of testing your scripts is not really much different when emails are being piped to them, with the exception that you can't just output test values to the screen. Ok, you can output test values and then sift through the bounceback message to find them.
I find an easier way is to simply email test values to myself. This way your script can execute smoothly and not generate a bounceback message and you can still peek at what the case is at certain points along the way.
And I think that generally covers all you need to know about piping email to php scripts.
Your script won't be running in the same enviroment as if it were being browsed to. This will affect the superglobal arrays you might often use.
In fact the only superglobal array that contains anything useful is $_SERVER. And most of this is generally of no use. One value that is of use is $_SERVER['argv'].
For scripts executed from the command line, $_SERVER['argv'] will contain the arguements passed to the script in the form of an array. In the case of mail piping, this (at least for me) contains only one element and that is the full path to the script i.e. $_SERVER['argv'][0].
If find this useful if I want to include common library files that are used elsewhere within the same account. I normally deal with common library files by setting the include path, but that won't work here - your script is on it's own and has no idea where everything else is. But if you know the path to the script, you can navigate round to where your library files are without having to hardcode the exact path.
Testing
The process of testing your scripts is not really much different when emails are being piped to them, with the exception that you can't just output test values to the screen. Ok, you can output test values and then sift through the bounceback message to find them.
I find an easier way is to simply email test values to myself. This way your script can execute smoothly and not generate a bounceback message and you can still peek at what the case is at certain points along the way.
And I think that generally covers all you need to know about piping email to php scripts.