Skip to content

Commit

Permalink
Send a delayed notification email
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Jan 19, 2024
1 parent cd5e1a2 commit ea1301e
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 129 deletions.
4 changes: 2 additions & 2 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ module.exports = {
disableInterfaces: ['forwarder'], // do not bounce messages from this interface
sendingZone: 'bounces',

// send a warning email about delayed delivery
// Send a warning email about delayed delivery
delayEmail: {
enabled: true,
after: 3 * 3600 * 1000
after: 3 * 3600 * 1000 // 3h
},

zoneConfig: {
Expand Down
92 changes: 92 additions & 0 deletions lib/bounces.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,98 @@ module.exports.check = (input, category) => {
};
};

module.exports.canSendBounce = delivery => {
if (delivery.skipBounce) {
log.info(
this.logName,

Check failure on line 112 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 112 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 112 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s as defined by routing',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>'
);
return false;
}

if (/^mailer-daemon@/i.test(delivery.from) || !delivery.from) {
log.info(
this.logName,

Check failure on line 124 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 124 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 124 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to envelope (MAIL FROM=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
JSON.stringify(delivery.from || '')
.replace(/"/g, '')
.trim() || '<>'
);
return false;
}

let xAutoResponseSuppress = delivery.headers.getFirst('X-Auto-Response-Suppress');
if (/\ball\b/i.test(xAutoResponseSuppress)) {
log.info(
this.logName,

Check failure on line 140 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 140 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 140 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'X-Auto-Response-Suppress',
JSON.stringify(xAutoResponseSuppress).replace(/"/g, '').trim()
);
return false;
}

let autoSubmitted = delivery.headers.getFirst('Auto-Submitted');
if (/\bauto-(generated|replied)\b/i.test(autoSubmitted)) {
log.info(
this.logName,

Check failure on line 155 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 155 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 155 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'Auto-Submitted',
JSON.stringify(autoSubmitted).replace(/"/g, '').trim()
);
return false;
}

let contentType = delivery.headers.getFirst('Content-Type');
if (/^multipart\/report\b/i.test(contentType)) {
log.info(
this.logName,

Check failure on line 170 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 170 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 170 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'Content-Type',
'multipart/report'
);
return false;
}

if (delivery.parsedEnvelope && /^mailer-daemon@/i.test(delivery.parsedEnvelope.from)) {
log.info(
this.logName,

Check failure on line 184 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 184 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (20.x, ubuntu-20.04)

Unexpected 'this'

Check failure on line 184 in lib/bounces.js

View workflow job for this annotation

GitHub Actions / test (16.x, ubuntu-20.04)

Unexpected 'this'
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'From',
JSON.stringify(delivery.parsedEnvelope.from || '<>')
.replace(/"/g, '')
.trim() || '<>'
);
return false;
}

return true;
};

function formatSMTPResponse(str) {
str = (str || '').toString().trim();
let code = str.match(/^\d{3}[\s-]+([\d.]+\s*)?/);
Expand Down
58 changes: 41 additions & 17 deletions lib/mail-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ const QueueLocker = require('./queue-locker');
const TtlCache = require('./ttl-cache');
const crypto = require('crypto');
const plugins = require('./plugins');
const Headers = require('mailsplit').Headers;
const db = require('./db');
const GridFSBucket = require('mongodb').GridFSBucket;
const ObjectId = require('mongodb').ObjectId;
const internalCounters = require('./counters');
const bounces = require('./bounces');
const MailDrop = require('./mail-drop');
const yaml = require('js-yaml');
const fs = require('fs');
const pathlib = require('path');
Expand Down Expand Up @@ -47,6 +50,7 @@ class MailQueue {
this.closing = false;
this.garbageTimer = null;
this.seqIndex = new SeqIndex();
this.maildrop = new MailDrop(this);

this.cache = new TtlCache(); // shared cache for workers
this.locks = new QueueLocker();
Expand Down Expand Up @@ -818,25 +822,45 @@ class MailQueue {
let lastCheck = now;

if (firstCheck && prevLastCheck) {
return plugins.handler.runHooks(
'queue:delayed',
[
delivery,
responseData,
{
first: firstCheck,
prev: prevLastCheck,
last: lastCheck
}
],
err => {
if (err) {
log.error('Queue', '%s.%s queue:delayed %s', delivery.id, delivery.seq, err.message);
}

return this.getMeta(delivery.id, (err, meta) => {
if (err) {
// ignore
log.error('Queue', '%s.%s GET META %s', delivery.id, delivery.seq, err.message);
return callback(null, true);
}
);

let deliveryEntry = Object.assign(item.value, meta || {});
deliveryEntry.headers = new Headers(deliveryEntry.headers);

deliveryEntry.envelope = {
from: deliveryEntry.from,
to: deliveryEntry.recipient
};

if (!bounces.canSendBounce(deliveryEntry)) {
return false;
}

return plugins.handler.runHooks(
'queue:delayed',
[
Object.assign({}, deliveryEntry, responseData),
this.maildrop,
{
first: firstCheck,
prev: prevLastCheck,
last: lastCheck
}
],
err => {
if (err) {
log.error('Queue', '%s.%s queue:delayed %s', deliveryEntry.id, deliveryEntry.seq, err.message);
}

return callback(null, true);
}
);
});
}
}

Expand Down
88 changes: 2 additions & 86 deletions lib/sender.js
Original file line number Diff line number Diff line change
Expand Up @@ -1416,92 +1416,8 @@ class Sender extends EventEmitter {
}

sendBounceMessage(delivery, bounce, smtpResponse) {
if (/^mailer-daemon@/i.test(delivery.from) || !delivery.from) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to envelope (MAIL FROM=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
JSON.stringify(delivery.from || '')
.replace(/"/g, '')
.trim() || '<>'
);
return;
}

if (delivery.skipBounce) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s as defined by routing',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>'
);
return;
}

let xAutoResponseSuppress = delivery.headers.getFirst('X-Auto-Response-Suppress');
if (/\ball\b/i.test(xAutoResponseSuppress)) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'X-Auto-Response-Suppress',
JSON.stringify(xAutoResponseSuppress).replace(/"/g, '').trim()
);
return;
}

let autoSubmitted = delivery.headers.getFirst('Auto-Submitted');
if (/\bauto-(generated|replied)\b/i.test(autoSubmitted)) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'Auto-Submitted',
JSON.stringify(autoSubmitted).replace(/"/g, '').trim()
);
return;
}

let contentType = delivery.headers.getFirst('Content-Type');
if (/^multipart\/report\b/i.test(contentType)) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'Content-Type',
'multipart/report'
);
return;
}

if (delivery.parsedEnvelope && /^mailer-daemon@/i.test(delivery.parsedEnvelope.from)) {
log.info(
this.logName,
'id=%s %s.%s SKIPBOUNCE Skip bounce to %s due to header (%s=%s)',
delivery.sessionId,
delivery.id,
delivery.seq,
delivery.from || '<>',
'From',
JSON.stringify(delivery.parsedEnvelope.from || '<>')
.replace(/"/g, '')
.trim() || '<>'
);
return;
if (!bounces.canSendBounce(delivery)) {
return false;
}

this.sendCommand(
Expand Down
Loading

0 comments on commit ea1301e

Please sign in to comment.