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: 219 - No Comments
Create subdomains in PHP
When I started receiving more and more requests for installing Pritlog on my server for different folks, I wanted to think of a better solution than just installing a separate Pritlog copy to each individual and put them in separate sub directories. This can cause any modification or updations to all these different installations to be a very time consuming task.This is when I started thinking about the possibility of modifying Pritlog in such a way that a single installation could handle multiple blogs and these blogs can be in separate subdomains - similar to how it works in Blogspot and few other sites.
First task was to create sub domains dynamically using PHP. Usually, sub domains can be created from the control panel provided by your hosting provider. But, this is a manual task. I did some searching through the net and came across some articles that talked about using a XML API provided by cPanel (hosting control panel) that could be used. My delicious link below has the bookmarks I made during my research.
http://delicious.com/prithish/subdomain
But, none of the API solutions worked for me. I was trying this on my hosting account with Hostmonster. Finally, the below solution worked for me.
The original discussion link:
http://www.webmasterworld.com/forum88/8746.htm
The steps that actually helped me.
Step 1: First you need to set
your main domain to act as a catch-all subdomain. its like putting
ServerAlias * so that each subdomain which is requested to the main domain reaches the same place. I mean if your main domain root is
/home/admin/abc.com/htdocs/
then your catch-all setup shall send all sub-domain requests to that same directory
/home/admin/abc.com/htdocs/
Initially, I could not figure out how to do it in Hostmonster. Finally, I found the solution somewhere that said, we could add a subdomain called '*' and make it direct to the root of the domain. This way, my domain became a catch all domain.
Step 2: You'd add this code to very top of your index page right after ""
$domain = $_SERVER['HTTP_HOST'];
$domain_parts = explode('.',$domain);
if (count($domain_parts) == 3 && $domain_parts[0]!= "www") {
// make sure a subdomain is called
$user = $domain_parts[0];
$loc = "http://domain.tld/whateverpage.ext?varname=$user";
@header("Location:$loc");
}
$domain_parts = explode('.',$domain);
if (count($domain_parts) == 3 && $domain_parts[0]!= "www") {
// make sure a subdomain is called
$user = $domain_parts[0];
$loc = "http://domain.tld/whateverpage.ext?varname=$user";
@header("Location:$loc");
}
What this step is basically saying is when the domain is a catch all domain, all requests to any subdomains will come to the index files of the root domain. You can add this code to parse the data in the requested url and figure out what subdomain was requested and use "header" function from within your index script to handle the redirection.
I am now using this to provide hosted Pritlog. Here are some links to check this out.
http://mathew.pritlog.com/
http://tania.pritlog.com/
http://blakley.pritlog.com/
If anything on this post is not clear, please feel free to post a comment requesting clarification.
Tags: web - Visits: 1203 - Comments: 3
Template engine for Pritlog
In developing the latest release of Pritlog, one major ingredient I wanted to add was a template engine. I searched far and wide on the web and came up with a big list of probably solutions. Keep in mind that I was more interested in very simple and light weight solutions and not in any full fledged template engines. This requirement is because I want Pritlog to be a lightweight blogging solution.If you want to see the complete list of templating solutions I bookmarked, here they are:
http://delicious.com/prithish/templateengine
The below two fascinated me:
1 - Using PHP as your template engine: The author explains how the extract command in PHP can be used to create an easy solution for templating with pure PHP.2 - The one line template engine: The author gives the solution mentioned below. I ended up using this for Pritlog and was pretty satisfied with this approach. Pritlog is still under 350 KB uncompressed with templating and plugin functionality.
I apologize if the code does not look very clean. I will see if I can get a good code highlighter gadget to use on my Pritlog. The engine itself:
Format of template.tpl file:
If you're not comfortable with pulling variables out of the global scope, define them as
Tags: web - Visits: 202 - No Comments
Website hosting with Godaddy
First fact - I don't get any money or sponsorship for this blog and hence this is an honest account from experience.Its amazing how when looking for hosting deals, you come across lots of websites claiming that they give web hosting reviews. But looking through many such sites, I found that they all talked about the same companies over and over again and usually Godaddy and Yahoo hosting rated very poor on these review sites.
Also, its shocking to know that many major hosting companies are infact part of the same group. We might think they are competitors, but they are just a big group working with each other. This is one reason we can see good reviews about all these sites always together in the same place. I am not going to mention any names. Please do your research.
3IX Hosting:
I was with a hosting company called 3ix for a year before switching to Godaddy. I must say that 3iX did an excellent job for the price. I paid around $33 for hosting for a year for 3 websites and the sites have been down for only a couple of days in a whole year. Because my requirements were low, I really did not get affected by this. All other times, it has been very stable and fast. I could get the live chat support anytime instantly. If someone is looking for extremely cheap hosting with excellent support, then 3ix would be a great choice.Godaddy:
I switched to Godaddy seeking more stability. I have been with them for over a year now and have been very happy. The sites have rarely went down and I could do all the things I needed using the hosting. The support has been great and friendly.There are minor glitches like
- htaccess takes around 1 hour to get activated
- Its not cpanel and its their own control panel software
- There are no logging apps like awstats
- Very limited MySQL Databases
- Setting new things like new ftp user, new database, new domains can take couple of hours
Positives:
- Friendly and good support
- Fast - I have never had speed or bandwidth problems
- Stable - The server has been very stable and my sites have hardly gone down
Tags: web - Visits: 223 - No Comments