- Why bother?
- Please read before going any further :)
- Requirements (tested environment)
- The idea
- The implementation
- Version française
Why bother? ¶
In order to avoid bounce messages and unsolicited registrations, most webapps send automatic activation emails upon user registration, and the account remain inactive as long as it is not activated. Better yet, you can periodically purge your data by removing accounts that have not yet been activated.
But in the supposedly small share of webapps that don't ask users to activate their accounts, you can end up with a number of bounce messages and non-accessible active accounts.
Of course, you can prevent at least wrong domain names by using the checkMX property of CEmailValidator in your model rules. But:
- it's not perfect
- it works only if PHP's checkdnsrr function is enabled on your server (so if you're on a shared host, they may not enable it)
- it doesn't check for invalid mailboxes
All that said, we can go on further.
Please read before going any further :) ¶
I wrote this wiki after looking around over the Web several times, and ending up with the solution I'm presenting here, based on the references quoted where applicable.
However, I've stumbled upon two other ways that seem way more advanced while apparently using the same scenario described here:
PHPMailer-BMH seems (is) much more advanced.
There's an implementation by Jacmoe's in his Bugitor project under protected/FetchEmailCommand.php, which uses a library called MIME E-mail message parser (mime_parser) by Manuel Lemos. As Bugitor project seems down as far as I'm editing this wiki, you can find another implementation here: PHP: Parse emails with email piping, Part 1 and Part 2.
Requirements (tested environment) ¶
- cPanel
- Access to email filtering
- Yii 1.1.10+
The idea ¶
It's heavily inspired from this article: Piping Incoming Mail with PHP
The implementation ¶
The User Table ¶
We'll use this sample user table. Adapt to your taste.
~~~
[sql]
CREATE TABLE IF NOT EXISTS User
(
id
int unsigned NOT NULL AUTO_INCREMENT,
status
tinyint unsigned NOT NULL,
login
varchar NOT NULL,
password
tinytext NOT NULL,
email
varchar NOT NULL,
firstName
varchar NOT NULL,
lastName
varchar NOT NULL,
PRIMARY KEY (id
),
UNIQUE KEY login
(login
)
);
~~~
The handleBounceMessage console command ¶
The idea is once we get the bounce email from the buffer (see next step), we search it for what we need to take further action.
In this example, I assume that the email is surely present in the bounce message in a string that matches one of two patterns:
- The first for wrong domain names
- The second for mailbox unavailable at an existing domain name
Of course, it's more straightforward to search for a specific string that is present in your original email (that has bounced). That way, you are sure of a match.
Here's our example code for the console command, which we'll save as HandleBounceMessageCommand.php and put in protected/commands
<?php
class HandleBounceMessageCommand extends CConsoleCommand
{
public function run($args)
{
$socket = fopen ("php://stdin", 'r');
$email = '';
// Read e-mail into buffer
while (!feof($socket)) {
$email .= fread($socket, 1024);
}
// Close socket
fclose($socket);
// Do a regex match on one of two patterns
// Adapt to taste
if (preg_match("/X-Failed-Recipients\:\s*(.*)/", $email, $emailMatch) == 0) {
preg_match("/RCPT TO:<(.*)>/", $email, $emailMatch);
}
// Fetch the user record matching the email address
$connection = Yii::app()->db;
$sql = 'SELECT id, login, firstName, lastName FROM User WHERE email = "' . trim($emailMatch[1]) . '"';
$command = $connection->createCommand($sql);
$user = $command->queryRow();
// Deactivate the user (by setting status to 1)
$sql = 'UPDATE User SET status = 1 WHERE id = ' . $user['id'];
$command = $connection->createCommand($sql);
$command->execute();
// Send a custom e-mail to admin
$header = "MIME-Version: 1.0\r\nContent-type: text/plain; charset=UTF-8\r\nFrom: Bounce Watchdog <bounce-watchdog@yourdomain.com>";
$header .= "\r\nX-Priority: 1 (Highest)\r\nX-MSMail-Priority: High\r\nImportance: High";
$subject = 'A user has given a wrong email address';
$body = 'Hi admin,
The user ‘' . $user['firstName'] . ' ' . $user['lastName'] . '’ has given a wrong email address in the registration form.
The matching login ‘' . $user['login'] . '’ has been automatically deactivated.
No further action is necessary.';
mail('admin@yourdomain.com', '=?UTF-8?B?'.base64_encode($subject).'?=', $body . '
For your reference, here is the bounce message transcript:
' . $email, $header);
}
}
Piping bounce emails to our console command ¶
In your cPanel GUI, go to the Mail block's User Level Filtering, select click on 'Manage Filters' for the desired account (generally the default one if you use php's mail() function, or a custom one otherwise).
Then click 'Create a New Filter', give it a name, and select 'is an error message' in the Rules parameter. Then in the Actions parameter, select 'Pipe to a program' and put the following in the next input ~~~ |/path/to/protected/yiic handleBounceMessage ~~~
I'd also recommend --at least in tests-- to add an additional action, 'Redirect to email' (and specify your email address) because you won't find the bounce email anymore in your default mailbox once you used a filter on it.
Before saying it doesn't work :) ¶
Make sure you run correctly console commands as cron jobs. Don't mute their output so you know what may be the error.
A general note is to edit your protected/yiic.php to make sure it has the correct information.
You can also insert some mail() actions for debugging into your console command.
Side note ¶
Unless you find this way of doing things ugly, you can do a lot of variations, process your contact forms for specific words and send back automatic emails, implement auto-responders, and what have you.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.