PHP script to handle incoming mails using pipes
I recently came across this blogging service called "Posterous". The feature that caught my attention was this: You can send mails to a particular email address and it would get automatically posted onto the blog as a post. I had heard about something similar in Wordpress couple of years ago.I started doing my research on how this could be done. In this post, I try to document the steps that were involved and any challenges I faced doing this.
What can this be used for?
- Such a script could handle the auto subscribe or unsubscribe to or from a mailing list
- To write and manage a full mailing list application
- To send commands to your site or scripts using emails
- To post on a blog as I did
- ...... (your imagination is the limit)
Step 1:
Add an email forwarder in your cPanel. Under the email section, you will find "forwarders". Click on this and you will be able to add email forwarders. When you add a forwarder, there will be an option to "pipe to a program". Choose this and add the full path to your php script that will be handling the mails. This program should have permissions of 755 to be executable. I wasted some significant time because I forgot this.


Step 2:
Now, you can start writing your program to handle the mails. It will look like this: (this script is taken from this post.)
#!/usr/bin/php -q
<?php
ob_start();
// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// handle email
$lines = explode("n", $email);
// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
if ($splittingheaders) {
// this is a header
$headers .= $lines[$i]."n";
// look out for special headers
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
$to = $matches[1];
}
} else {
// not a header, but message
$message .= $lines[$i]."n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
ob_end_clean();
?>
When you have completed Step 1 and Step 2, the script will run when the mail arrives and reads and parses the mail. <?php
ob_start();
// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// handle email
$lines = explode("n", $email);
// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
if ($splittingheaders) {
// this is a header
$headers .= $lines[$i]."n";
// look out for special headers
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
$to = $matches[1];
}
} else {
// not a header, but message
$message .= $lines[$i]."n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
ob_end_clean();
?>
You can now use the variables, $to, $headers, $subject and $message and process as you require. Maybe use php mail to send them back an email or store it on a database. For the purposes of Pritlog hosted site, I do some validations and store this in the database.
Step 3: (Only required if you need better message parsing)
One challenge I had was that the above script was not parsing the message well, especially when the message had multi-parts (text, html etc). I found this php class called "Mime email message parser".What I did is to write the full message (not parsed one) from the above step into a file and let the mime message parser class do the correct parsing. During my tests from several different email id's, this class parsed the mail correctly.
Here is the code for this.
$file = 'tempmail.txt';
$current = $email;
file_put_contents($file, $current);
require_once('mimeparser/rfc822_addresses.php');
require_once('mimeparser/mime_parser.php');
$message_file=((IsSet($_SERVER['argv']) && count($_SERVER['argv'])>1) ?
$_SERVER['argv'][1] : $file);
$mime=new mime_parser_class;
$mime->mbox = 1;
$mime->decode_bodies = 1;
$mime->ignore_syntax_errors = 1;
$parameters=array(
'File'=>$message_file,
'SkipBody'=>0,
);
if(!$mime->Decode($parameters, $decoded))
echo 'MIME message decoding error: '.$mime->error.' at position
'.$mime->error_position."rn";
else
{
for($message = 0; $message < count($decoded); $message++)
{
if($mime->Analyze($decoded[$message], $results)) {
$message = $results["Data"];
}
else
echo 'MIME message analyse error: '.$mime->error."rn";
}
}
$current = $email;
file_put_contents($file, $current);
require_once('mimeparser/rfc822_addresses.php');
require_once('mimeparser/mime_parser.php');
$message_file=((IsSet($_SERVER['argv']) && count($_SERVER['argv'])>1) ?
$_SERVER['argv'][1] : $file);
$mime=new mime_parser_class;
$mime->mbox = 1;
$mime->decode_bodies = 1;
$mime->ignore_syntax_errors = 1;
$parameters=array(
'File'=>$message_file,
'SkipBody'=>0,
);
if(!$mime->Decode($parameters, $decoded))
echo 'MIME message decoding error: '.$mime->error.' at position
'.$mime->error_position."rn";
else
{
for($message = 0; $message < count($decoded); $message++)
{
if($mime->Analyze($decoded[$message], $results)) {
$message = $results["Data"];
}
else
echo 'MIME message analyse error: '.$mime->error."rn";
}
}
Things to remember:
- The #!/usr/bin/php -q at the beginning of your script is very important. The "-q" supresses output. My initial attempts were failing and took a long time for me to figure this one out. If it had any sort of outputs or warnings, then the mail would bounce back. When I did this, it stopped bouncing back.
- The CHMOD of the script must be 755 (executable). Again, the mails kept bouncing back when the permissions on the script where not 755.
The outcome of this experiment is that now Pritlog.com users can send mails to post on their blogs. Hurray!!
Please feel free to post any comments, suggestions or questions.
Tags: programming,pritlog,web - Visits: 457 - Comments: 1
Plugin system for Pritlog
A plugin system enables external users or members of the community to add functionality to the software without even touching a single line from the main software program.Pritlog needed a plugin system and that too a very efficient and lightweight one. I found the plugin system called "Simple Hooks Plugin" from Phpclass website. The link is http://phpclasses.betablue.net/browse/package/4497.html.
I will mention some details of how it is implemented.
Firstly, the main program needs to initialize the plugins. It can be done like this:
//include Simple Hooks Plugin Class
$SHP = new SHP();
//set hook to which plugin developers can assign functions $SHP->developer_set_hook('hook-test');
$SHP = new SHP();
//set hook to which plugin developers can assign functions $SHP->developer_set_hook('hook-test');
Second, the main program needs to define what is called hooks. These are basically points in the main program where the developer wants external plugins to attach and execute. This code can look like this:
<html>
<head>
<title>Testing the plugins</title>
</head>
<body>
<?php
//Initialize the plugin
include "SHP.class.php";
$SHP = new SHP();
$SHP->developer_set_hook('hook-test');
echo "<h1>Simple Hooks Plugin</h1>";
$SHP->execute_hooks('hook-test');
?>
</body>
</html>
<head>
<title>Testing the plugins</title>
</head>
<body>
<?php
//Initialize the plugin
include "SHP.class.php";
$SHP = new SHP();
$SHP->developer_set_hook('hook-test');
echo "<h1>Simple Hooks Plugin</h1>";
$SHP->execute_hooks('hook-test');
?>
</body>
</html>
In the above piece of code, the developer has provided a hook called hook-test where external plugins can attach and execute. Now let us look at a simple plugin.
//set plugin id as file name of plugin
$plugin_id = basename(__FILE__);
//some plugin data
$data['name'] = "Name of the plugin";
$data['author'] = "Authors Name";
$data['desc'] = "Brief description of the plugin";
$data['url'] = "Plugin/Author website";
//register the plugin
register_plugin($plugin_id, $data);
//plugin function
function test_function() {
echo 'echoed from plugin';
}
//add hook, where to execute a function
add_hook($plugin_id, 'hook-test','test_function');
echo "Plugin 1 LOADED!"; //code to execute when loading plugin
$plugin_id = basename(__FILE__);
//some plugin data
$data['name'] = "Name of the plugin";
$data['author'] = "Authors Name";
$data['desc'] = "Brief description of the plugin";
$data['url'] = "Plugin/Author website";
//register the plugin
register_plugin($plugin_id, $data);
//plugin function
function test_function() {
echo 'echoed from plugin';
}
//add hook, where to execute a function
add_hook($plugin_id, 'hook-test','test_function');
echo "Plugin 1 LOADED!"; //code to execute when loading plugin
When the main program runs, this plugin is picked up and the final output from the program would be:
Plugin 1 LOADED!
<h1>Simple Hooks Plugin</h1>
echoed from plugin
See how the code from the plugin got executed in the main program even without touching a single line of code from the main program.
In Pritlog, I modified the plugin system so that the user can enable or disable a plugin at will.
This was a very interesting and exciting experiment for me.
Tags: pritlog,web - Visits: 220 - No Comments