diff --git a/.travis.yml b/.travis.yml index 0a827b44..dc5ea4b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: trusty sudo: required language: node_js node_js: - - 7 + - 8 services: - mysql before_install: diff --git a/README.md b/README.md index 60726293..a25979ba 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect * Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain` * **Note**: depending on how you have configured your system and Docker you may need to prepend the commands below with `sudo`. -* Copy the file `docker-compose.override.yml.tmpl` to `docker-compose.override.yml.tmpl` and modify it if you need to. +* Copy the file `docker-compose.override.yml.tmpl` to `docker-compose.override.yml` and modify it if you need to. * Bring up the stack with: `docker-compose up -d`, by default it will use the included `docker-compose.yml` file and override some configurations taken from the `docker-compose.override.yml` file. * If you want to use only / copy the `docker-compose.yml` file (for example, if you were deploying with Rancher), you may need to first run `docker-compose build` to make sure your system has a Docker image `mailtrain:latest`. * Open [http://localhost:3000/](http://localhost:3000/) (change the host name `localhost` to the name of the host where you are deploying the system). diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js index 0fb4323d..8d7bc899 100644 --- a/lib/models/campaigns.js +++ b/lib/models/campaigns.js @@ -951,7 +951,7 @@ module.exports.findMailByResponse = (responseId, callback) => { return callback(err); } - connection.query('SELECT id FROM campaigns', [], (err, campaignList) => { + connection.query('SELECT id FROM campaigns ORDER BY id DESC', [], (err, campaignList) => { if (err) { connection.release(); return callback(err); @@ -1086,6 +1086,24 @@ module.exports.updateMessage = (message, status, updateSubscription, callback) = }); }; +module.exports.updateMessageResponse = (message, response, response_id, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + let query = 'UPDATE `campaign__' + message.campaign + '` SET `response`=?, `response_id`=? WHERE id=? LIMIT 1'; + connection.query(query, [response, response_id, message.id], err => { + connection.release(); + if (err) { + return callback(err); + } + return callback(null, true); + }); + + }); +}; + function createCampaignTables(id, callback) { let query = 'CREATE TABLE `campaign__' + id + '` LIKE campaign'; db.getConnection((err, connection) => { diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index 60aba027..3bc578b9 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -28,26 +28,58 @@ let server = net.createServer(socket => { return readNextChunk(); } let line = lines[pos++]; - let match = /\bstatus=bounced\b/.test(line) && line.match(/\bpostfix\/\w+\[\d+\]:\s*([^:]+)/); + let match = /\bstatus=(bounced|sent)\b/.test(line) && line.match(/\bpostfix\/\w+\[\d+\]:\s*([^:]+).*?status=(\w+)/); if (match) { let queueId = match[1]; + let queued = ''; + let queued_as = ''; if (seenIds.has(queueId)) { return checkNextLine(); } seenIds.add(queueId); + // Losacno: Check for local requeue + let status = match[2]; + log.verbose('POSTFIXBOUNCE', 'Checking message %s for local requeue (status: %s)', queueId, status); + if ( status === 'sent' ) { + // Save new queueId to update message's previous queueId (thanks @mfechner ) + queued = / relay=/.test(line) && line.match(/status=sent \((.*)\)/); + if ( queued ) { + queued = queued[1]; + queued_as = queued.match(/ queued as (\w+)/); + queued_as = queued_as[1]; + } + } + campaigns.findMailByResponse(queueId, (err, message) => { if (err || !message) { return checkNextLine(); } - campaigns.updateMessage(message, 'bounced', true, (err, updated) => { - if (err) { - log.error('POSTFIXBOUNCE', 'Failed updating message: %s', err && err.stack); - } else if (updated) { - log.verbose('POSTFIXBOUNCE', 'Marked message %s as bounced', queueId); - } - }); + if ( queued_as ) { + log.verbose('POSTFIXBOUNCE', 'Message %s locally requeued as %s', queueId, queued_as); + // Update message's previous queueId (thanks @mfechner ) + campaigns.updateMessageResponse(message, queued, queued_as, (err, updated) => { + if (err) { + log.error('POSTFIXBOUNCE', 'Failed updating message: %s', err && err.stack); + } else if (updated) { + log.verbose('POSTFIXBOUNCE', 'Successfully changed message queueId to %s', queued_as); + } + }); + + } else { + campaigns.updateMessage(message, 'bounced', true, (err, updated) => { + if (err) { + log.error('POSTFIXBOUNCE', 'Failed updating message: %s', err && err.stack); + } else if (updated) { + log.verbose('POSTFIXBOUNCE', 'Marked message %s as bounced', queueId); + } + }); + } + + // No need to keep in memory... free it ( thanks @witzig ) + seenIds.delete(queueId); + return checkNextLine(); }); return; diff --git a/services/sender.js b/services/sender.js index 040a8a13..a30b38ee 100644 --- a/services/sender.js +++ b/services/sender.js @@ -523,7 +523,7 @@ let sendLoop = () => { } } - let status = err ? 2 : 1; + let status = err ? 3 : 1; let response = err && (err.response || err.message) || info.response || info.messageId; let responseId = response.split(/\s+/).pop(); @@ -533,7 +533,7 @@ let sendLoop = () => { return; } - let query = 'UPDATE `campaigns` SET `delivered`=`delivered`+1 ' + (status === 2 ? ', `bounced`=`bounced`+1 ' : '') + ' WHERE id=? LIMIT 1'; + let query = 'UPDATE `campaigns` SET `delivered`=`delivered`+1 ' + (status === 3 ? ', `bounced`=`bounced`+1 ' : '') + ' WHERE id=? LIMIT 1'; connection.query(query, [message.campaignId], err => { if (err) {