Skip to content

Instantly share code, notes, and snippets.

@jmendeth jmendeth/maildir.ini
Last active May 17, 2019

Embed
What would you like to do?
Haraka plugin to store mail in a fixed maildir
# This file must be placed in "config" directory of your Haraka server.
# Maildir directory. Can be relative to Haraka server directory, or absolute.
# Available tokens:
# %d -> domain
# %n -> username
path=/var/mails/%d/%n
# Permission of new directories.
dir_mode=0750
# Permission of new files.
file_mode=0650
# Rspamd score when a mail is considered spam and should be moved
# to spam folder
# rspamd_spam_threshold=15.0
rspamd_move_to_spam=true
rspamd_spam_threshold=8.0
/**
* @overview: Stores email into a single maildir folder, optionally obeying rspamd-score.
* Needs Node v10
*/
const os = require('os');
const fs = require('fs');
const path = require('path');
const util = require('util');
const stream = require('stream');
const link = util.promisify(fs.link);
const unlink = util.promisify(fs.unlink);
const pipeline = util.promisify(stream.pipeline);
const mkdir = util.promisify(fs.mkdir);
exports.register = function() {
this.load_maildir_ini();
};
/**
* Saves email when the smtp server enqueues it.
*/
exports.hook_queue = function(next, connection) {
const plugin = this;
const cfg = plugin.cfg.main;
const txn = connection.transaction;
const options = {};
// If message is SPAM, send to Junk folder
const rspamd_header = txn.header.headers_decoded['x-rspamd-score'];
if (rspamd_header && cfg.rspamd_move_to_spam) {
const spam_score = rspamd_header[0];
if (spam_score > cfg.rspamd_spam_threshold) {
plugin.loginfo("Email is spam, move to spam folder. Score: " + util.inspect(spam_score));
options.folder = ".Junk";
}
}
plugin.deposit(options, connection).then(() => next(OK), err => {
plugin.logerror(`Failed to deliver mail: ${(err && err.stack) || err}`);
next(DENYSOFT);
});
};
exports.deposit = async function(params, connection) {
const plugin = this;
const cfg = plugin.cfg.main;
const dirMode = parseInt(cfg.dir_mode, 8);
const fileMode = parseInt(cfg.file_mode, 8);
let maildir = cfg.path;
//const [name, domain] = params.user.split('@');
//maildir = maildir.replace('%d', domain);
//maildir = maildir.replace('%n', name);
const folder = params.folder ? path.join(maildir, params.folder) : maildir;
const fileName = getFileName(connection);
// Create subdirectories if needed
const f = {};
await Promise.all(['tmp', 'cur', 'new'].map(async dir => {
const subfolder = path.join(folder, dir);
f[dir] = path.join(subfolder, fileName);
await mkdir(subfolder, { mode: dirMode, recursive: true })
.catch(err => err.code === 'EEXIST' || Promise.reject(err))
}));
// Write the file
const fileStream = fs.createWriteStream(f['tmp'], { flags: 'w', mode: fileMode });
await pipeline(connection.transaction.message_stream, fileStream);
await link(f['tmp'], f['new']);
await unlink(f['tmp']);
};
exports.load_maildir_ini = function() {
const plugin = this;
plugin.cfg = plugin.config.get('maildir.ini', 'ini', () => {
plugin.load_maildir_ini();
});
};
/**
* Unique name of the file inside the maildir.
* For filename uniqueness, connection uuid is used.
* Thanks: http://cr.yp.to/proto/maildir.html
* @return {string}
*/
const getFileName = (connection) =>
`${Date.now()}.${connection.uuid}.${os.hostname()}`;

maildir

Stores the email into a file in a maildir structure. See here for more info about the maildir format. This plugin is loosely based on the one in https://github.com/swerter/haraka-plugins, but stores mails in a single, fixed maildir.

Configuration

This plugin uses the configuration maildir.ini in INI format.

  • path = /var/emails (REQUIRED)

    Set the destination directory to store the email

  • dir_mode = 0750 (REQUIRED)

    Set the permission of the new directory

  • rspamd_move_to_spam = true (REQUIRED)

    Whether to move a email marked as spam to the .Spam folder instead of into the inbox.

  • rspamd_spam_threshold = 8.0 (REQUIRED)

    Above which threshold the email should be moved to the spam folder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.