HTTP/1.1 // 38.107.191.87 // // 37764 // // CCBot/1.0 (+http://www.commoncrawl.org/bot.html)

Clicktale

Simple Email Logger Using cPanel’s Pipe Forwarder

Mar
31
2009

I needed a way to easily log all email being sent from my websites so I devised this solution where I just have to bcc the emails to a single email address and they get placed in a database.

First, add a forwarder in cPanel. Enter a super secret address to forward. I recommend a random string of 20-30 chars. Just make it something someone can't guess, otherwise your database could be filled with spam. Then select Pipe to a Program and enter the location of where you will put your script. I recommend placing it outside of the web root (public_html in my case). See the screenshot for information on how to enter it. The path to my file is:

/home/my_username/cron/email.php

Now here is the php script I use. Enter your database information at the top, and save it to where you indicated when creating your forwarder.

#!/usr/bin/php -q
<?php
// Config
$dbuser = '';
$dbpass = '';
$dbname = '';
$dbhost = 'localhost';
$email = ''; // an email address required in case of errors

// 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;
    }
}

if ($conn = @mysql_connect($dbhost,$dbuser,$dbpass)) {
  if(!@mysql_select_db($dbname,$conn))
    mail($email,'Email Logger Error',"There was an error selecting the email logger database.\n\n".mysql_error());
  $from    = mysql_real_escape_string($from);
  $to    = mysql_real_escape_string($to);
  $subject = mysql_real_escape_string($subject);
  $headers = mysql_real_escape_string($headers);
  $message = mysql_real_escape_string($message);
  $email   = mysql_real_escape_string($email);
  $result = @mysql_query("INSERT INTO email_log (`to`,`from`,`subject`,`headers`,`message`,`source`) VALUES('$to','$from','$subject','$headers','$message','$email')");
  if (mysql_affected_rows() == 0)
    mail($email,'Email Logger Error',"There was an error inserting into the email logger database.\n\n".mysql_error());
} else {
  mail($email,'Email Logger Error',"There was an error connecting the email logger database.\n\n".mysql_error());
}
?>

Here is the mysql script to create the table you'll need.

MySQL:
  1. CREATE TABLE IF NOT EXISTS `email_log` (
  2.   `id` INT(11) NOT NULL AUTO_INCREMENT,
  3.   `to` VARCHAR(100) NOT NULL,
  4.   `subject` VARCHAR(200) NOT NULL,
  5.   `FROM` VARCHAR(100) NOT NULL,
  6.   `headers` TEXT NOT NULL,
  7.   `message` TEXT NOT NULL,
  8.   `source` TEXT NOT NULL,
  9.   `logged_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  10.   PRIMARY KEY  (`id`)
  11. ) ENGINE=MyISAM;

And that's it. I use phpMyEdit behind basic HTTP Authenication to view the emails, but you could use phpMyAdmin or any other php table editor.


5 Comments »

Allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
MyAvatars 0.2

Comment by Dux Marketing
on 13 Apr 2009 at 4:08 pm #

Hi,

I’ll come across your website and found it more interesting in Web Development. I learn more, thanks for the information you share, i’ll come back often.

Regards,

Dux Marketing

 
MyAvatars 0.2

Comment by William
on 13 May 2009 at 4:28 pm # Subscribed to comments via email

How would you change this to where when someone sends you an email it gets piped to the database?

 
MyAvatars 0.2

Comment by JSteele
on 01 Sep 2009 at 9:07 pm # Subscribed to comments via email

This is great! Works like a charm! My only question, is there a way to handle an attachment in the same way? The script pulls out who the email is from, to, subject and of course the message. What about an attachment? Can it be saved to the server and referenced in the db? Once again thanks for this code, it has helped greatly!

MyAvatars 0.2

Comment by James Ehly
on 02 Sep 2009 at 7:39 am #

Well, if you save the entire body of the email to the database, you are saving the attachment. Attachments use the MIME standard, so to extract them you have to parse out that data yourself or use a class to do it. Read about the MIME standard, and this MIME parser class might help:

http://email.about.com/cs/standards/a/mime.htm
http://phpclasses.ranchoweb.com/browse/package/3169.html

MyAvatars 0.2

Comment by JSteele
on 02 Sep 2009 at 8:49 am # Subscribed to comments via email

Is this something you’d be interested in doing for $$? I’ve been reading on how to do this but can’t seem to get it to work. As I stated before I like the code you have here and how it saves multiple pieces of data from the email to the DB. It works well for what I need (except sometimes it fails to grab who the message is from, I think the email might be in a different format??) but I need the script to also save the attachment to the server and reference that file in the DB. Let me know what you think, thanks!

(Comments wont nest below this level)