I use this simple method, and thought to share it with everyone, since I found no complete tutorial on the Internet for this task:
1. Define a way to identify your users.
I use my primary key, userid, from my database, e.g. User 12345, but it can also be a textual string, just encode any special characters.
2. Encode the user identity like this:
user-12345-bounce@yoursite.com
This technique is called Variable Envelope Return Path (VERP)
3. Include the Return-Path: header when sending emails
When you send emails from PHP from now on, you should include the Return-Path.
Example: Assume the variables $to_name, $to_email, $user_id
contains the user you are planning to send an email to:
"John Smith", "johns@gmail.com", 12345 respectively.
CODE
$return_path = "user-".((int)$user_id)."-bounce@yoursite.com";
$headers = "MIME-Version: 1.0\n";
$headers .= "From: YourSite <support@yoursite.com>\n";
$headers .= "Reply-To: YourSite <support@yoursite.com>\n";
$headers .= "X-Priority: 3\n";
$headers .= "X-MSMail-Priority: Normal\n";
$to_address = $to_name . " <" . $to_email . ">";
$result=mail($to_address, $subject, $message, $headers, "-f".$return_path );
$headers = "MIME-Version: 1.0\n";
$headers .= "From: YourSite <support@yoursite.com>\n";
$headers .= "Reply-To: YourSite <support@yoursite.com>\n";
$headers .= "X-Priority: 3\n";
$headers .= "X-MSMail-Priority: Normal\n";
$to_address = $to_name . " <" . $to_email . ">";
$result=mail($to_address, $subject, $message, $headers, "-f".$return_path );
What this brings is that you get these headers on your outgoing emails to this user:
Return-Path: <user-12345-bounce@yoursite.com>
To: John Smith <johns@gmail.com>
From: YourSite <support@yoursite.com>
Note the last argument of mail(). You need to switch SafeMode Off for that argument to be accepted. That's usually ok as you probably run this as a cronjob, so you can still keep SafeMode On for your PHP web pages if you want, and SafeMode is removed in PHP6 anyway.
4. Now invalid email addresses will bounce
If it now turns out that the email address johns@gmail.com is no longer valid, the email will bounce back to:
user-12345-bounce@yoursite.com
5. Set up a script that can process that bounce.
Create a script file at /home/yoursite/misc/bounceparse.php
CODE
#!/usr/local/bin/php -q
<?php
// Reading in the email
$fd = fopen("php://stdin", "r");
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// Parsing the email
$lines = explode("\n", $email);
$stillheaders=true;
for ($i=0; $i < count($lines); $i++) {
if ($stillheaders) {
// 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
break;
// Optionally you can read out the message also, instead of the break:
//$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$stillheaders = false;
}
}
list($part1,$dum1) = explode("-bounce@yoursite.com", trim($to) );
list($dum2,$user) = explode("user-", $part1);
//
// $user now contains the user id "12345" in the example
//
// Here you put in your custom code
// like open up your database connection and
// mark the user as invalid email address,
// so you don's send to him again.
return true;
?>
<?php
// Reading in the email
$fd = fopen("php://stdin", "r");
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// Parsing the email
$lines = explode("\n", $email);
$stillheaders=true;
for ($i=0; $i < count($lines); $i++) {
if ($stillheaders) {
// 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
break;
// Optionally you can read out the message also, instead of the break:
//$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$stillheaders = false;
}
}
list($part1,$dum1) = explode("-bounce@yoursite.com", trim($to) );
list($dum2,$user) = explode("user-", $part1);
//
// $user now contains the user id "12345" in the example
//
// Here you put in your custom code
// like open up your database connection and
// mark the user as invalid email address,
// so you don's send to him again.
return true;
?>
6. Set the permissions
The script must be executable and owned by the user who is running your site (the one you log into Cpanel with)
cd /home/yoursite/misc/
chmod a+x bounceparse.php
chown youruser:youruser bounceparse.php
7. Add a Filter
Now log into Cpanel, Go to Mail, click E-Mail Filtering and Add Filter.
Select Filter: TO CONTAINS bounce@yoursite.com
Destination: |/home/yoursite/misc/bounceparse.php
(Note the Pipe | in the beginning.)
8. Enable default address
If you set the default address to :fail: the email will never reach the Filter stage, unfortunately.
So you need to have a default address.
Go into Cpanel / Mail / Default Address and set your default address. (Actually I pipe it to null.)
9. Check that it works!
Check that it works by sending an email to one valid gmail user and one non-existing and check what happens in /var/log/exim_mainlog
=> means email sent by you to someone, e.g. you would find johns@gmail.com
That if that fails, you will get one of two behaviours, which will both be processed by your bounceparse.php script:
<= means email delivered to you, e.g. a returned bounce message to user-12345-bounce@yoursite.com
** means email that couldn't be sent non-existent mailbox discovered already at the SMTP stage
Improvement possibility 1
The only thing I'm not 100% satisfied with, is that I would have liked to :fail: instead of having a catch-all default address. Because with :fail: you reject spam already at the SMTP stage before reading the email to your server. Maybe someone can figure out how to :fail: all emails to the domain, except those with a valid address and those which have the *bounce@yoursite.com structure , I'm sure it's possible to tweak /etc/exim.conf to do that, but I leave that as future improvement for someone to post.
Improvement possibility 2
With this implementation, all bounces are treated as bad email address, and are removed from the email list, better safe than sorry. If you want to be more elaborate, you can of course use the $headers and $message fields to parse the bounce and possibly refine different issues, but in practice, my experience is that a bounce like this is enough reason to take him off the list, to not risk offending the receiptent sysadmin. The big 4 have become much more stingy.
Enjoy safe emailing! And of course add SPF records.
Different parts of this tutorial is compiled from various good sources and friends. All credits to those who deserve it!
Best Regards, Anders
http://www.phoneimage.com/