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: 1094 - Comments: 1
PritMartkit: Social bookmarking button
I have been searching for a good bookmarking button to use for Pritlog. I found a couple of good ones, but all of them have external website dependancies and this was slowing down Pritlog. Hence, after doing some research, I wrote one. I am currently working on getting this to be easy to use on any site so that it can be released as a javascript file. I will be posting more when something is ready to be released.The different experiments I did on this front are on this webpage:
http://hardkap.net/pritmarkit
I am not releasing anything yet as I found several inconsistencies when working with different browsers. Hence, I cannot assure if this solution will work as is with different browsers/ css frameworks.
Tags: programming - Visits: 340 - Comments: 2
Considering Sqlite for Pritlog
After my experiments with flat files and php for Pritlog, when I came to find out that Sqlite would be much more performance effective compared to flat files, I am starting to think if I should move to Sqlite for Pritlog.As an initial test, I tried using a table with 5000 records. Did all common operations like read, update, search. These operations completed in an instant. Absolutely no performance issues. Users would not even realize that there are 5000 records being accessed. I am impressed.
Sqlite databases are simple files created on the path you specify. Hence, I would think, we can move these around and get the same functionality. Mobility is one thing I would like Pritlog to always have.
I have posted on the Pritlog forum to see the responses from about moving to Sqlite.
Tags: programming - Visits: 359 - No Comments