I.T. Security and Linux Administration

Sep 9 2011   12:00PM GMT

Parsing E-mails via Postfix and PHP



Posted by: Eric Hansen
Tags:
E-mail
Parsing
PHP
Postfix
Virtual

While not everyone is going to have a need for this, there are a lot of good reasons to parse e-mail.  Perhaps the best is if you use a help desk/bug tracking/ticket system (Bugtraq, Clientexec, etc…), and want to allow people to send in requests and such via e-mail.  Among other things, this is the easiest way to do this with Postfix.  Since this works whether you are using virtual or real domains, you don’t have to worry about this.  There are methods to do this with MySQL as well, but I have yet to get it to work.

First thing you want to do here is edit /etc/postfix/master.cf.  What this does is give you a finer tuning of how Postfix handles e-mail (i.e.: set up SASL/SSL for SMTP).  Above the followign line:

smtp      inet  n       –       n       –       –       smtpd

You will want to add this line (replace “filter” with any name you want to, as long as its unique and doesn’t contain spaces):

filter     unix –       n       n       –       1       pipe flags=DRXhu user=vmail:vmail argv=/home/vmail/pipe.php ${sender}

Where there’s a “-” it means use the default.  ”pipe” is the command for Postfix to redirect mail somewhere else.  pipe (8) explains everything after “pipe”, or you can read it online.  User is the owner of the path to where “pipe.php” is stored.  ”${sender}” is optional; it passes the “From:” to the script, but we’ll be parsing the e-mail headers anyways, so we don’t need to worry about this.  Also, make sure the path in argv is valid.  I hvae /home/vmail/ chown’ed to vmail:vmail.

After you do the above (don’t restart postfix yet, we’ll be doing that in a little while), edit /etc/postfix/main.cf, adding the following line:

transport_maps = hash:/etc/postfix/transport

Afterwards, edit /etc/postfix/transport (create it if it don’t exist already), and add a new line for each address you want to use for parsing.  For example, if I wanted to have tickets@example.com filter e-mails for support tickets, I would simply add this line:

tickets@example.com filter

After that, you have to run this command:

postmap /etc/postfix/transport

We’re already there basically.  Now, you have to make sure that the e-mail provided in /etc/postfix/transport exists (in this case, “tickets@example.com”), or else you could run into issues, so its better to be safe than sorry anyways.  Now that we’re done with Postfix, you can restart it.  Now we need to create the piping PHP page (/home/vmail/pipe.php in this example).  First, the script:


#!/usr/bin/php -q

<?php

function preg_return($regex, $text, $delim = "/"){

$reg = $delim . $regex . $delim;

preg_match($reg, $text, $m);

return (!empty($m[1]) ? $m[1] : "");

}

function parse_header($header){

$part = array();

$lines = explode("\n", $header);

foreach($lines as $line){

$tmp = preg_split("/: /", $line);

$opt = $tmp[0];

$val = $tmp[1];

$part[$opt] = $val;

}

return $part;

}

$fd = fopen("php://stdin", "r");

$email = "";

while(!feof($fd)){

$email .= fread($fd, 1024);

}

fclose($fd);

list($data, $body) = explode("\n\n", $email, 2);

$header = parse_header($data);

$from = $header['From'];

$sub = $header['Subject'];

$body = trim($body);

// Handle e-mail

?>

You need the shabang (“#!/usr/bin/php -q”) to be the very first thing in the script, but the “-q” is optional, I always use it personally, but it basically just tells PHP to not output anything to stdin and stderr.  Also, make sure that the path for PHP is correct.

The function preg_return() is something I wrote up when I first wrote the script to parse the header portion.  Its not of use anymore, but left if you want to use it to parse the body or anything.

parse_header() simply makes each line part of an array.  How its used is shown further down, and I’ll also go into detail on it a little bit too.

The following block of code is what stumped me for a good hour:

$fd = fopen(“php://stdin”, “r”);

$email = “”;

while(!feof($fd)){

$email .= fread($fd, 1024);

}

fclose($fd);

At first I was wondering why I couldn’t get any of the e-mail data, and then after doing some research I found out that Postfix passes it to PHP via stdin (standard input).  So, you can either get all of the e-mail data (headers and body) by doing the above, or cut down to one line by doing the following:

$email = file_get_contents(“php://stdin”);

After that, since the header and body are separated by two newlines, you simply store them into two different variables, and make sure we only get 2 (in case there’s any \n\n’s in the body, we ignore them).

At this point, $data contains all of the e-mail header data, and $body contains the content itself.  To get information from the header, we pass it along into parse_header(), and retrieve the data by calling it from the header’s option.  For example, if you want the sender’s e-mail information (i.e.: bob@denver.com), we simply do the following:

$from = $header['From'];

From this point, $from will contain “bob@denver.com”.  After that, its up to you on how you want to parse the data.

While this was a long blog post, most of this was more explanation of how and why things work, than actually setting up Postfix and PHP.  If you’re curious why I didn’t restart Postfix until after we were done with it, its because we had to edit main.cf as well, and why restart the service twice?

2  Comments on this Post

 
There was an error processing your information. Please try again later.
Thanks. We'll let you know when a new response is added.
Send me notifications when other members comment.

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
  • nohandlexxx
    It's not working.
    10 pointsBadges:
    report
  • Eric Hansen
    Can you please be more specific?
    1,025 pointsBadges:
    report

Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: