code (9)

Handling AWS Simple Email Service notifications

Apparently it’s not enough to gather information on your email sending, bounces and complaints. You also need to act when there’s a problem otherwise Amazon will put your account under probation and might even shut it completely if you don’t fix the problem.

And it’s pretty easy to implement per their sample code and the explanation in the aws blog

Here are the steps I use to set it up:

  • Create an SNS topic. Let’s call it SES-BOUNCES. Or create 2 SNS topics (1 for bounces, another for complaints). I will go with just one.
  • Create a service on your site to handle the bounces. I used some simple PHP code (pasted below) for that.
  • Create a subscription in your SNS topic (created first)

The service should handle 2 things:

  • the subscription (that will happen once!): call the SubscribeURL and you’re done.
  • the bounces/complaints: Remove the email addresses form your mailing list, or blacklist them, or … The main idea is to stop sending emails to those who complain. And stop trying to send emails to addresses that bounce.

Here’s the code for the PHP service, it’s pretty straight forward

if ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
	http_response_code( 405 );

require 'vendor/autoload.php';

use Aws\Sns\Message;
use Aws\Sns\MessageValidator;

function parse_bounce( $message ) {
	$recipients = array();
	foreach ( $message['bounce']['bouncedRecipients'] as $recipient ) {
		$recipients[] = $recipient['emailAddress'];
	switch ( $message['bounce']['bounceType'] ) {
		case 'Transient':
			error_log( "BouncesController - Soft bounces occurred for " . join( ', ', $recipients ) );

			return array();
		case 'Permanent':
			error_log( "BouncesController - Hard bounces occurred for " . join( ', ', $recipients ) );

			return $recipients;

function parse_complaint( $message ) {
	$recipients = array();
	foreach ( $message['complaint']['complainedRecipients'] as $recipient ) {
		$recipients[] = $recipient['emailAddress'];

	return $recipients;

function blacklist_recipients( $recipients ) {
	foreach ( $recipients as $email_address ) {
		// blacklist those emails

try {
	$sns_message = Message::fromRawPostData();

	$validator = new MessageValidator();
	$validator->validate( $sns_message );

	if ( $validator->isValid( $sns_message ) ) {
		if ( in_array( $sns_message['Type'], [ 'SubscriptionConfirmation', 'UnsubscribeConfirmation' ] ) ) {
			file_get_contents( $sns_message['SubscribeURL'] );
			error_log( 'Subscribed to ' . $sns_message['SubscribeURL'] );

		if ( $sns_message['Type'] == 'Notification' ) {
			$message           = $sns_message['Message'];
			$notification_type = $message['notificationType'];
			if ( $notification_type == 'Bounce' ) {
				blacklist_recipients( parse_bounce( $message ) );
			if ( $notification_type == 'Complaint' ) {
				blacklist_recipients( parse_complaint( $message ) );

} catch ( Exception $e ) {
	error_log( 'Error: ' . $e->getMessage() );
	http_response_code( 404 );

Don’t forget to run composer to get those dependencies:

  "require": {
    "aws/aws-sdk-php": "3.*",
    "aws/aws-php-sns-message-validator": "1.4.0"

And make sure you don’t send unsolicited emails! it’s just not nice.

Similar Posts:

Install same packages for different PHP version

This is one of the things I should have thought of a long time ago.
More times than I can count I needed to install the same (or similar) list of PHP packages for a different version (ie. installing 7.0 while keeping 5.6) So instead of manually copying over the list or guessing as to what will be installed via dependencies here’s a quick one liner. Again, it’s a “facepalm” moment here for me:

# this installs 7.1 packages from a 7.0 list
dpkg -l | grep php7 | awk '{gsub(/7\.0/,"7.1",$2); print $2}' | xargs apt install

Also on my mind: instead of a sit/stand desk, can I install a shower desk. This way I can work and take showers at the same time!

Similar Posts:

Email Tracking

Most email clients can request a return receipt. One way to set this up in Thunderbird for example is to go to “Preferences > Advanced > General > Return Receipts …” and check the “When sending messages, always request a return receipt”

The problem with this approach is that when the person you’re emailing has set his email client to reject that request (or does that manually every time), you will not get a receipt back. And honestly speaking there is no way to force that. Now there are some services out there that promise to track your mail when that’s read, and those rely on embedding a transparent gif that will call home when the message is opened. I tried a few of these services: the free ones did not deliver (or seemed too shady for my taste) and the paid ones did not look too good either. So I cooked up a quick solution that I can use when needed (like when I’m tracking my brother on his honeymoon trip *evil grin*)

The code is below, pretty self explanatory. You will need a transparent gif/png. That’s easy too ๐Ÿ˜‰

Edit: Looks like some services are not so bad. Checkout bananatag and YesWare Email Tracking

Similar Posts:

Building DNS Zone Files

The only way to get a full listing of a DNS zone is to use AXFR to request a zone transfer. But that’s usually locked and not available. So building the zone file for a domain is basically guesswork or manual labour at best.

For some background information check out these questions on SO
Here’s a script I wrote to try and automate that as much as possible. Still in beta. Suggestions welcome.

Note: I use DNSPython

Next on my list is using the RimuHosting/Zonomi API to create/import the DNS zone.

Similar Posts:

Site ScreenShot Part 2 – Planning

I was having a little conversation with my friend Ghassan from LebanonDesign and he mentioned his company was actually using something similar to SiteScreenShot to generate thumbnails for their different websites. So I thought I could scale my code to do the same. Of course, plenty of changes should be considered:

  • Snapshot generation currently takes 3-5 seconds to complete. So I need to do 2 things:
    • Tell the user that the app is actually working on generating the snapshot
    • Provide a way to retrieve the pic once the snapshot is generated
    • Make things go faster if possible
  • Getting more users means more load on the server, what should we use?
    • Replace Apache with Nginx for faster static file delivery
    • Use a queue manager like Celery/RabbitQM to distribute the load

I’m currently testing some code with django-celery and I’ll try to post more information here (so I can remember what I’m doing)

Also on my list is making the site accessible via a simple API.

I also need to research a better way to take the screenshots on a headless server.


Similar Posts:

Pidgin chat pounce plugin

I wrote the following plugin using the Pidgin Purple Perl_API. It’s pretty simple and to the point without too many bells and whistles.

We use an XMPP (jabber) chat room to communicate at work. That’s mostly for internal communication. Recently we added an external hookup to a campfire chat room via tinder. The campfire chat room is mostly for customers requesting support.

The problem is that when a customer enters the chat room, pidgin will display something like the following:

(02/23/2011 07:35:36 AM) Tinder: James Bond in Customer Service Desk: EnterMessage

There’s no way to make pidgin understand that’s a new user logging in as the ‘Tinder’ user is already logged in. Is there?

Here’s the codeI moved the code to github, including some enhancements from Adrian Deac. put the code in a file named ‘’ in your .purple/plugins/ directory

It’s been fun brushing up on my perl skills. I may write another plugin (to edit timestamps display) later as well…

Similar Posts:

Bypassing stupid security measures

A couple of weeks back, my esteemed bank BCL introduced a new “feature” (read annoyance) to their online banking site. It’s a “virtual keyboard” that sits on the login page and can be used only with a mouse. And it’s utterly stupid

Similar Posts:

Hierarchy in SQL

from this blog post

Node int NOT NULL IDENTITY(100, 1),
ParentNode int,
EmployeeID int NOT NULL,
Depth tinyint,
Lineage varchar(255) )
UPDATE Tree SET Lineage='/', Depth=0 WHERE ParentNode Is Null
   UPDATE T SET T.depth = P.Depth + 1, 
   T.Lineage = P.Lineage + Ltrim(Str(T.ParentNode,6,0)) + '/' 
   FROM Tree AS T 
   INNER JOIN Tree AS P ON (T.ParentNode=P.Node) 
   WHERE P.Depth>=0 
   AND P.Lineage Is Not Null 
   AND T.Depth Is Null
SELECT Space(T.Depth*2) + E.Name AS Name
FROM Employees E
INNER JOIN Tree T ON E.EmployeeID=T.EmployeeID
ORDER BY T.Lineage + Ltrim(Str(T.Node,6,0))
Alternatieven zij ontvangen een goede bescherming van elementen voor visit page beter postrecurrence. Door mensen die baat hebben viagra vervanger bij andere.

Similar Posts:

MySQL Incremental Backups

MySQL incremental backups can be acheived using the --log-bin option. For more information about that and for the original source of the following code, please see the mysql ref. manual.

Now to the code:
I added some logging to the script

Similar Posts: