2017-03-28 20:06:29 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2019-03-25 16:55:37 +03:00
|
|
|
'use strict';
|
2017-03-28 20:06:29 +03:00
|
|
|
|
2019-03-25 16:55:37 +03:00
|
|
|
const error = require('./error');
|
|
|
|
const P = require('./promise');
|
2017-03-28 20:06:29 +03:00
|
|
|
|
|
|
|
module.exports = (config, db) => {
|
2019-03-25 16:55:37 +03:00
|
|
|
const configBounces = config.smtp && config.smtp.bounces || {};
|
|
|
|
const BOUNCES_ENABLED = !! configBounces.enabled;
|
2017-06-08 01:55:29 +03:00
|
|
|
|
2019-03-25 16:55:37 +03:00
|
|
|
const BOUNCE_TYPE_HARD = 1;
|
|
|
|
const BOUNCE_TYPE_SOFT = 2;
|
|
|
|
const BOUNCE_TYPE_COMPLAINT = 3;
|
2017-03-28 20:06:29 +03:00
|
|
|
|
2019-03-25 16:55:37 +03:00
|
|
|
const freeze = Object.freeze;
|
2017-03-28 20:06:29 +03:00
|
|
|
const BOUNCE_RULES = freeze({
|
2017-06-08 01:55:29 +03:00
|
|
|
[BOUNCE_TYPE_HARD]: freeze(configBounces.hard || {}),
|
|
|
|
[BOUNCE_TYPE_SOFT]: freeze(configBounces.soft || {}),
|
|
|
|
[BOUNCE_TYPE_COMPLAINT]: freeze(configBounces.complaint || {})
|
2019-03-25 16:55:37 +03:00
|
|
|
});
|
2017-03-28 20:06:29 +03:00
|
|
|
|
2017-06-08 01:55:29 +03:00
|
|
|
const ERRORS = {
|
|
|
|
[BOUNCE_TYPE_HARD]: error.emailBouncedHard,
|
|
|
|
[BOUNCE_TYPE_SOFT]: error.emailBouncedSoft,
|
|
|
|
[BOUNCE_TYPE_COMPLAINT]: error.emailComplaint
|
2019-03-25 16:55:37 +03:00
|
|
|
};
|
2017-06-08 01:55:29 +03:00
|
|
|
|
2017-03-28 20:06:29 +03:00
|
|
|
function checkBounces(email) {
|
|
|
|
return db.emailBounces(email)
|
2019-03-25 16:55:37 +03:00
|
|
|
.then(applyRules);
|
2017-06-08 01:55:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Relies on the order of the bounces array to be sorted by date,
|
|
|
|
// descending. So, each bounce in the array must be older than the
|
|
|
|
// previous.
|
|
|
|
function applyRules(bounces) {
|
|
|
|
const tallies = {
|
|
|
|
[BOUNCE_TYPE_HARD]: {
|
|
|
|
count: 0,
|
|
|
|
latest: 0
|
|
|
|
},
|
|
|
|
[BOUNCE_TYPE_COMPLAINT]: {
|
|
|
|
count: 0,
|
|
|
|
latest: 0
|
|
|
|
},
|
|
|
|
[BOUNCE_TYPE_SOFT]: {
|
|
|
|
count: 0,
|
|
|
|
latest: 0
|
|
|
|
}
|
2019-03-25 16:55:37 +03:00
|
|
|
};
|
|
|
|
const now = Date.now();
|
2017-06-08 01:55:29 +03:00
|
|
|
|
|
|
|
bounces.forEach(bounce => {
|
2019-03-25 16:55:37 +03:00
|
|
|
const type = bounce.bounceType;
|
|
|
|
const ruleSet = BOUNCE_RULES[type];
|
2017-06-08 01:55:29 +03:00
|
|
|
if (ruleSet) {
|
2019-03-25 16:55:37 +03:00
|
|
|
const tally = tallies[type];
|
|
|
|
const tier = ruleSet[tally.count];
|
2017-06-08 01:55:29 +03:00
|
|
|
if (! tally.latest) {
|
2019-03-25 16:55:37 +03:00
|
|
|
tally.latest = bounce.createdAt;
|
2017-03-28 20:06:29 +03:00
|
|
|
}
|
2017-06-08 01:55:29 +03:00
|
|
|
if (tier && bounce.createdAt > now - tier) {
|
2019-03-25 16:55:37 +03:00
|
|
|
throw ERRORS[type](tally.latest);
|
2017-06-08 01:55:29 +03:00
|
|
|
}
|
2019-03-25 16:55:37 +03:00
|
|
|
tally.count++;
|
2017-06-08 01:55:29 +03:00
|
|
|
}
|
2019-03-25 16:55:37 +03:00
|
|
|
});
|
2017-03-28 20:06:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function disabled() {
|
2019-03-25 16:55:37 +03:00
|
|
|
return P.resolve();
|
2017-03-28 20:06:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
check: BOUNCES_ENABLED ? checkBounces : disabled
|
2019-03-25 16:55:37 +03:00
|
|
|
};
|
|
|
|
};
|