Introduction to writing haraka plugins

  • Introduction to Haraka or how to write a haraka plugin
    [[http://haraka.github.io/][Haraka]] is an email server, similar to postfix.
    It is very modular and written in nodejs. That makes it ideal if you want
    to do more than just sending or receiving email.

The main idea behind haraka is to have a plugin queue, where each plugin is
executed according to the smtp session. You have plugins that are executed
immediately when another smtp server connects, for instance, to do a check
on the other server’s IP address. Or you have plugins that are executed once
the recipient is known or only when the whole email has been transferred. To
give an example, a basic smtp session goes like this:

S: 220 aspmx1.migadu.com ESMTP Haraka 2.7.3 ready
C: EHLO relay.example.org
S: 250 Hello relay.example.org, Haraka is at your service
S: 250-PIPELINING
S: 250-8BITMIME
S: 250 SIZE 26214400
C: MAIL FROM:<peter@test.com>
S: 250 Ok
C: RCPT TO:<ron@example.com>
S: 250 recipient <ron@example.com> OK
C: RCPT TO:<maria@example.com>
S: 250 recipient <maria@example.com> OK
C: DATA state=1
S: 354 go ahead, make my day
C: From: "Ron Example" <ron@example.org>
C: To: "Maria Example" <maria@example.com>
C: Cc: someoneelse@example.com
C: Date: Thu, 14 Apr 2016 07:30:39 +0200\r
C: Subject: Hello World
C: X-Mailer: Thunderbird
C:
C: Hello Ron and Maria.
C: We are happy to have a working email system
C: Cheers,
C: Ron
C: .
S: 250 Ok: queued as 12345
C: QUIT
S: 221 Bye

As you can see there are different parts in an smtp session. First, the server greet each other
and define the protocol (eg. SMTP or ESMTP). Then the mail from is sent, followed by the recipients.
Finally, the data, that is the email content is sent.

Now how do we write a haraka plugin? When you installed haraka you typically have two folders:
plugins and config. In plugins you have the code of your own plugins, in config the configuration
(how surprising).

The simplest plugin is probably the following:

exports.register = function() {
    var plugin = this;

    plugin.register_hook('queue', 'debugQueue);
};

exports.debugQueue = function(next, connection, params) {
    var plugin = this;
    plugin.loginfo("Email queued");
    next();
};

In the register function you define at what moment in time the plugin shall be executed (here queue)
and the name of the function to call (here debugQueue).
The function debugQueue then simply prints out the info «Email queue» whenever an email is queued for
delivery. In order to actually get the plugin working, you still need to add the plugin to the
config/plugin file. Save the content above into the file plugins/debug.js and add in config/plugins
at the Queue section: debug. The plugin should then look similiar to:

...
# QUEUE
# queues: discard  qmail-queue  quarantine  smtp_forward  smtp_proxy
# Queue mail via smtp - see config/smtp_forward.ini for where your mail goes
# queue/smtp_forward
debug
...

All the hooks you can plug into are documented here [[http://haraka.github.io/manual/Plugins.html][Plugins]].

Difference between transaction and connection

When two stmp server connect, there is the distinction between connection and transaction.
Connection is the
connection before actually the email (eg the protocoll selection). You get the connection information,
for instance, in the function(next, connection, params).
The transaction is then the «real» email, that is starting at the FROM
command until the QUIT command. To get access to a single
email transaction, you do connection.transaction. To get an idea, do the following: Change the plugin
listed above slightly to

var util = require('util');
exports.register = function() {
    var plugin = this;

    plugin.register_hook('queue', 'debugQueue);
};

exports.debugQueue = function(next, connection, params) {
    var plugin = this;
    var txn = connection.transaction;
    plugin.loginfo(util.inspect(connection));
    plugin.loginfo(util.inspect(txn));
    plugin.loginfo(util.inspect(txn.mail_from));
    plugin.loginfo(util.inspect(txn.rcpt_to));
    plugin.loginfo(util.inspect(txn.header.headers_decoded));
    next();
};

You will see a lot of information being displayed now, which gives you an idea of all the fields
that you can access. To get more info, read the [[http://haraka.github.io/manual.html][haraka manual]].