From 0876b8029e859dc0975cf00a6768a2627c905799 Mon Sep 17 00:00:00 2001 From: killua-eu Date: Mon, 14 Aug 2017 09:47:08 +0200 Subject: [PATCH 01/15] Update README.md --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index a25979ba..670a7da5 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,50 @@ If you are using the bundled ZoneMTA then you should make sure you are using a p With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect 10/10 score out from [MailTester](https://www.mail-tester.com/) when sending a campaign message to a MailTester test address. I did not have VERP turned on, so the sender address matched return path address. +#### Getting your head around DKIM, DMARK, SPF and PTR + +DKIM, DMARK, SPF and PTR are DNS records which spam filters use to figure out if e-mails were really sent by you (and not by a spammer who tries to conceal his identity to be able to continue send bulks of e-mails people never subscribed for). Assuming that you use zone-mta and your e-mails are to originate from a Mailtrain installation at `mailtrain.example.com` and optionally from `mail.example.net`, to practically set all these records up you will need to: + +1. generate genrate a private and public DKIM key + +```sh +mkdir /opt/dkim-keys +chmod 700 /opt/dkim-keys +pushd /opt/dkim-keys +openssl genrsa -out mailtrain.example.com.key 2048 # private key mailtrain.example.com.key +openssl rsa -in mailtrain.example.com.key -out mailtrain.example.com.pub -pubout -outform PEM # public key mailtrain.example.com.pub +``` + +2. add 3 new txt records for the mailtrain.example.com that will most likely similar to the example below: + +``` +default._domainkey.mailtrain.example.com TXT "k=rsa; p=[public key in one line];" +mailtrain.example.com TXT "v=spf1 mx a a:mail.example.net -all" +_dmarc.mailtrain.example.com TXT "v=DMARC1; p=reject" +``` + +(refer to a google search for a DKIM generator, SPF generator and DMARC genreator to get you up to speed). Configure your Mailtrain settings accoring to this: + +**DKIM domain:** mailtrain.example.com +**DKIM selector:** default +**DKIM Private Key:** [copy and paste the private key in /opt/dkim-keys/mailtrain.example.com.key] + +The above steps will have the following effect: + +- all messages sent by Mailtrain / Zone-mta will be signed by the DKIM Private Key (the signature becomes a part of the e-mail) +- when a spamfilter encounters this signature, it will look for the ****._domainkey.**** TXT record, and use the public key stored there to verify that the signature is valid +- additionally, the spamfilter will look for a TXT SPF record and will look a if the e-mail was sent from the IP address of mailtrain.example.com or mail.example.net. If the sender IP or domain is different, it will discard the e-mail as spam. +- furthermore, the spamfilter looks for the DMARC record, which tells it what to do with mails that aren't signed with DKIM or which don't have a valid signature. The example above will tell the spamfilter to reject such a mail as well. + +3. You are now almost set. To further confirm that you have full control over your network, the last step is to set up a PTR record, which will give the right answer for a reverse DNS lookup (answer to "what domain name is bound to IP address xxx.xxx.xxx.xxx). If you run your own DNS, you probably know it will look similar to this: + +``` +10.27/1.110.220.in-addr.arpa. 1800 PTR mailtrain.example.com. +``` + +If you run Mailtrain on a VPS, you will have to find the PTR configuration somewhere in your administration interface or ask your provider to help you. + + ### Simple Install (Docker) #### Requirements: From c91d1d02969c0b1d1e1c202d28b982860b5a0679 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Mon, 28 Aug 2017 15:36:15 +0300 Subject: [PATCH 02/15] Create README.md --- docs/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..209212f3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +Here should be the docs From da6aa7f6b50803ba2cb3949bb580b9969a28f102 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Mon, 28 Aug 2017 15:37:26 +0300 Subject: [PATCH 03/15] Create CNAME --- docs/CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/CNAME diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..fa5baf40 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +docs.mailtrain.org From dd637a1f2b9a8b5e957297b61e03c3e01874592d Mon Sep 17 00:00:00 2001 From: theyough Date: Tue, 5 Sep 2017 23:22:39 +0100 Subject: [PATCH 04/15] Fix when postfix server does not send 'queue as' properly --- services/postfix-bounce-server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index 3bc578b9..729b0d7c 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -48,7 +48,9 @@ let server = net.createServer(socket => { if ( queued ) { queued = queued[1]; queued_as = queued.match(/ queued as (\w+)/); - queued_as = queued_as[1]; + if (queued_as) { + queued_as = queued_as[1]; + } } } From 4235a9b9840b8a032ccaad910da54053d7bf2696 Mon Sep 17 00:00:00 2001 From: theyough Date: Wed, 6 Sep 2017 00:33:29 +0100 Subject: [PATCH 05/15] Fix when postfix server does not send 'queue as' properly --- services/postfix-bounce-server.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index 729b0d7c..cc903b48 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -50,6 +50,8 @@ let server = net.createServer(socket => { queued_as = queued.match(/ queued as (\w+)/); if (queued_as) { queued_as = queued_as[1]; + } else { + queued_as = '' } } } From 332666c851f95009c37cdbf4e2cfb60893370826 Mon Sep 17 00:00:00 2001 From: theyough Date: Wed, 6 Sep 2017 00:45:27 +0100 Subject: [PATCH 06/15] Fix when postfix server does not send 'queue as' properly --- services/postfix-bounce-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index cc903b48..cafbe7a2 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -60,7 +60,7 @@ let server = net.createServer(socket => { if (err || !message) { return checkNextLine(); } - if ( queued_as ) { + if ( queued_as || status === 'sent' ) { 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) => { From b663de138e5df259b6b1643be104c80faefba343 Mon Sep 17 00:00:00 2001 From: theyough Date: Wed, 6 Sep 2017 10:14:25 +0100 Subject: [PATCH 07/15] Fix Travis: Missing semicolon --- services/postfix-bounce-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/postfix-bounce-server.js b/services/postfix-bounce-server.js index cafbe7a2..ceb6ebdc 100644 --- a/services/postfix-bounce-server.js +++ b/services/postfix-bounce-server.js @@ -51,7 +51,7 @@ let server = net.createServer(socket => { if (queued_as) { queued_as = queued_as[1]; } else { - queued_as = '' + queued_as = ''; } } } From d9fe2e4eda767fb846f6a3e46a7863e883e27b7c Mon Sep 17 00:00:00 2001 From: witzig Date: Fri, 15 Sep 2017 22:17:54 +0200 Subject: [PATCH 08/15] Added note about mysql timezone option. #311 #134 --- config/default.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/default.toml b/config/default.toml index cd9446d4..44f91166 100644 --- a/config/default.toml +++ b/config/default.toml @@ -85,6 +85,7 @@ database="mailtrain" # MAMP users should also turn on "Allow network access to MySQL" otherwise MySQL might not be accessible port=3306 charset="utf8mb4" +# The timezone configured on the MySQL server. This can be 'local', 'Z', or an offset in the form +HH:MM or -HH:MM timezone="local" [redis] From 92fe9121f12515ab606d4139b1521c9575e65e75 Mon Sep 17 00:00:00 2001 From: witzig Date: Tue, 19 Sep 2017 11:50:03 +0200 Subject: [PATCH 09/15] Fixed incompatibility with the ZoneMTA DKIM Plugin https://github.com/zone-eu/zone-mta/issues/93 --- routes/webhooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/webhooks.js b/routes/webhooks.js index 4fa27015..957c3cb7 100644 --- a/routes/webhooks.js +++ b/routes/webhooks.js @@ -319,11 +319,11 @@ router.post('/zone-mta/sender-config', (req, res) => { res.json({ dkim: { - keys: { + keys: [{ domainName: configItems.dkimDomain || domain, keySelector: configItems.dkimSelector, privateKey: configItems.dkimPrivateKey - } + }] } }); }); From f66416ad651beb0c29936fae88b28b999391e8ee Mon Sep 17 00:00:00 2001 From: Passakorn Suppakityothin Date: Wed, 20 Sep 2017 11:59:23 +0700 Subject: [PATCH 10/15] add package-lock.json file to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9ab19ae8..2437b541 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules npm-debug.log +package-lock.json .DS_Store config/development.* config/production.* @@ -30,4 +31,4 @@ public/grapejs/templates/* config/production.toml workers/reports/config/production.toml -docker-compose.override.yml \ No newline at end of file +docker-compose.override.yml From 544e221278145e8f6f2a340dc58d88218db72e62 Mon Sep 17 00:00:00 2001 From: witzig Date: Fri, 22 Sep 2017 11:39:01 +0200 Subject: [PATCH 11/15] Honour tracking settings from parent RSS-Campaign, fixes #319 --- services/feedcheck.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/feedcheck.js b/services/feedcheck.js index b2871d38..3469fc06 100644 --- a/services/feedcheck.js +++ b/services/feedcheck.js @@ -20,7 +20,7 @@ function feedLoop() { return setTimeout(feedLoop, feed_timeout); } - let query = 'SELECT `id`, `source_url`, `from`, `address`, `subject`, `list`, `segment`, `html` FROM `campaigns` WHERE `type`=2 AND `status`=6 AND (`last_check` IS NULL OR `last_check`< NOW() - INTERVAL 10 MINUTE) LIMIT 1'; + let query = 'SELECT `id`, `source_url`, `from`, `address`, `subject`, `list`, `segment`, `html`, `open_tracking_disabled`, `click_tracking_disabled` FROM `campaigns` WHERE `type`=2 AND `status`=6 AND (`last_check` IS NULL OR `last_check`< NOW() - INTERVAL 10 MINUTE) LIMIT 1'; connection.query(query, (err, rows) => { connection.release(); @@ -148,7 +148,9 @@ function checkEntries(parent, entries, callback) { address: parent.address, subject: entry.title || parent.subject, list: parent.segment ? parent.list + ':' + parent.segment : parent.list, - html + html, + openTrackingDisabled: parent.openTrackingDisabled, + clickTrackingDisabled: parent.clickTrackingDisabled }; campaigns.create(campaign, { From 8e1bc1df7abcbe6fa39646ed858972c9b2c7d065 Mon Sep 17 00:00:00 2001 From: witzig Date: Fri, 22 Sep 2017 14:26:47 +0200 Subject: [PATCH 12/15] Fixes #298 - Import CSV didn't show hidden custom fields --- routes/lists.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/lists.js b/routes/lists.js index 4c28bf32..25f010ba 100644 --- a/routes/lists.js +++ b/routes/lists.js @@ -551,7 +551,7 @@ router.get('/subscription/:id/import/:importId', passport.csrfProtection, (req, data.list = list; data.csrfToken = req.csrfToken(); - data.customFields = fields.getRow(fieldList, data); + data.customFields = fields.getRow(fieldList, data, false, true); res.render('lists/subscription/import-preview', data); }); From f96b9de510fa3019db507395027a081430882757 Mon Sep 17 00:00:00 2001 From: witzig Date: Fri, 22 Sep 2017 15:28:26 +0200 Subject: [PATCH 13/15] Bumped some deps Note: isemail 3.x, jsdom 11.x, and mjml 3.3.4 contain breaking changes --- package.json | 86 ++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 069bf284..8ae998ad 100644 --- a/package.json +++ b/package.json @@ -31,90 +31,90 @@ "node": ">=5.0.0" }, "devDependencies": { - "babel-eslint": "^7.2.3", - "chai": "^4.0.2", + "babel-eslint": "^8.0.0", + "chai": "^4.1.2", "eslint-config-nodemailer": "^1.2.0", "grunt": "^1.0.1", "grunt-cli": "^1.2.0", "grunt-contrib-nodeunit": "^1.0.0", - "grunt-eslint": "^20.0.0", + "grunt-eslint": "^20.1.0", "jsxgettext-andris": "^0.9.0-patch.1", - "mocha": "^3.3.0", - "phantomjs-prebuilt": "^2.1.14", - "selenium-webdriver": "^3.4.0", + "mocha": "^3.5.3", + "phantomjs-prebuilt": "^2.1.15", + "selenium-webdriver": "^3.5.0", "url-pattern": "^1.0.3" }, "optionalDependencies": { "posix": "^4.1.1" }, "dependencies": { - "async": "^2.3.0", - "aws-sdk": "^2.37.0", + "async": "^2.5.0", + "aws-sdk": "^2.121.0", "bcrypt-nodejs": "0.0.3", - "body-parser": "^1.17.1", + "body-parser": "^1.18.1", "bounce-handler": "^7.3.2-fork.2", - "compression": "^1.6.2", - "config": "^1.25.1", + "compression": "^1.7.0", + "config": "^1.26.2", "connect-flash": "^0.1.1", - "connect-redis": "^3.2.0", + "connect-redis": "^3.3.0", "cookie-parser": "^1.4.3", - "cors": "^2.8.3", + "cors": "^2.8.4", "csurf": "^1.9.0", "csv-generate": "^1.0.0", - "csv-parse": "^1.2.0", + "csv-parse": "^1.2.2", "device": "^0.3.8", - "dompurify": "^0.9.0", + "dompurify": "^1.0.2", "escape-html": "^1.0.3", - "express": "^4.15.2", - "express-session": "^1.15.2", + "express": "^4.15.4", + "express-session": "^1.15.5", "faker": "^4.1.0", - "feedparser": "^2.1.0", - "fs-extra": "^3.0.1", + "feedparser": "^2.2.1", + "fs-extra": "^4.0.2", "geoip-ultralight": "^0.1.5", - "gettext-parser": "^1.2.2", + "gettext-parser": "^1.3.0", "gm": "^1.23.0", - "handlebars": "^4.0.6", + "handlebars": "^4.0.10", "hbs": "^4.0.1", "he": "^1.1.1", - "html-to-text": "^3.2.0", + "html-to-text": "^3.3.0", "humanize": "0.0.9", "is-url": "^1.2.2", "isemail": "^2.2.1", "jquery-file-upload-middleware": "^0.1.8", "jsdom": "^9.12.0", "json-stringify-date": "^0.1.4", - "juice": "^4.0.2", + "juice": "^4.1.1", "libmime": "^3.1.0", "mailparser": "^2.0.5", "marked": "^0.3.6", - "memory-cache": "^0.1.6", + "memory-cache": "^0.2.0", "mjml": "3.3.3", "mkdirp": "^0.5.1", - "moment-timezone": "^0.5.12", - "morgan": "^1.8.1", + "moment-timezone": "^0.5.13", + "morgan": "^1.8.2", "multer": "^1.3.0", "multiparty": "^4.1.3", - "mysql": "^2.13.0", - "node-gettext": "^2.0.0-rc.1", - "node-mocks-http": "^1.6.1", - "nodemailer": "^4.0.1", - "nodemailer-openpgp": "^1.0.2", - "npmlog": "^4.0.2", - "object-hash": "^1.1.7", - "openpgp": "^2.5.3", - "passport": "^0.3.2", + "mysql": "^2.14.1", + "node-gettext": "^2.0.0", + "node-mocks-http": "^1.6.4", + "nodemailer": "^4.1.0", + "nodemailer-openpgp": "^1.1.0", + "npmlog": "^4.1.2", + "object-hash": "^1.1.8", + "openpgp": "^2.5.11", + "passport": "^0.4.0", "passport-local": "^1.0.0", "premailer-api": "^1.0.4", - "redfour": "^1.0.0", - "redis": "^2.7.1", - "request": "^2.81.0", + "redfour": "^1.0.2", + "redis": "^2.8.0", + "request": "^2.82.0", "request-promise": "^4.2.1", - "serve-favicon": "^2.4.2", + "serve-favicon": "^2.4.4", "shortid": "^2.2.8", - "slugify": "^1.1.0", - "smtp-server": "^3.0.1", - "striptags": "^3.0.1", - "toml": "^2.3.2", + "slugify": "^1.2.1", + "smtp-server": "^3.1.0", + "striptags": "^3.1.0", + "toml": "^2.3.3", "try-require": "^1.2.1" } } From e669b5684e63b814af5dedaca81bd9156b23b2cd Mon Sep 17 00:00:00 2001 From: witzig Date: Fri, 22 Sep 2017 16:20:14 +0200 Subject: [PATCH 14/15] Fixed MJML Dependency (temporary) --- package.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/package.json b/package.json index 8ae998ad..317d628e 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,34 @@ "marked": "^0.3.6", "memory-cache": "^0.2.0", "mjml": "3.3.3", + "mjml-accordion": "3.3.3", + "mjml-button": "3.3.3", + "mjml-carousel": "3.3.3", + "mjml-cli": "3.3.3", + "mjml-column": "3.3.3", + "mjml-container": "3.3.3", + "mjml-core": "3.3.3", + "mjml-divider": "3.3.3", + "mjml-group": "3.3.3", + "mjml-head-attributes": "3.3.0", + "mjml-head-font": "3.3.0", + "mjml-head-preview": "3.3.3", + "mjml-head-style": "3.3.0", + "mjml-head-title": "3.3.0", + "mjml-hero": "3.3.3", + "mjml-html": "3.3.3", + "mjml-image": "3.3.3", + "mjml-invoice": "3.3.3", + "mjml-list": "3.3.3", + "mjml-location": "3.3.3", + "mjml-navbar": "3.3.3", + "mjml-raw": "3.3.3", + "mjml-section": "3.3.3", + "mjml-social": "3.3.3", + "mjml-spacer": "3.3.3", + "mjml-table": "3.3.3", + "mjml-text": "3.3.3", + "mjml-wrapper": "3.3.3", "mkdirp": "^0.5.1", "moment-timezone": "^0.5.13", "morgan": "^1.8.2", From c7c972d1d5e3492da40505fdaf1be5b23a593ed6 Mon Sep 17 00:00:00 2001 From: witzig Date: Tue, 26 Sep 2017 14:10:16 +0200 Subject: [PATCH 15/15] Bump deps - mjml comment bug has been fixed in 3.3.5 - csv-generate is no longer required since v1.19.1 - escape-string-regexp added to dependencies - bluebird and lodash added to dev-dependencies --- package.json | 48 +++++++++++------------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 317d628e..e427313b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "babel-eslint": "^8.0.0", + "bluebird": "^3.5.0", "chai": "^4.1.2", "eslint-config-nodemailer": "^1.2.0", "grunt": "^1.0.1", @@ -39,6 +40,7 @@ "grunt-contrib-nodeunit": "^1.0.0", "grunt-eslint": "^20.1.0", "jsxgettext-andris": "^0.9.0-patch.1", + "lodash": "^4.17.4", "mocha": "^3.5.3", "phantomjs-prebuilt": "^2.1.15", "selenium-webdriver": "^3.5.0", @@ -49,9 +51,9 @@ }, "dependencies": { "async": "^2.5.0", - "aws-sdk": "^2.121.0", + "aws-sdk": "^2.122.0", "bcrypt-nodejs": "0.0.3", - "body-parser": "^1.18.1", + "body-parser": "^1.18.2", "bounce-handler": "^7.3.2-fork.2", "compression": "^1.7.0", "config": "^1.26.2", @@ -60,12 +62,12 @@ "cookie-parser": "^1.4.3", "cors": "^2.8.4", "csurf": "^1.9.0", - "csv-generate": "^1.0.0", - "csv-parse": "^1.2.2", + "csv-parse": "^1.2.3", "device": "^0.3.8", "dompurify": "^1.0.2", "escape-html": "^1.0.3", - "express": "^4.15.4", + "escape-string-regexp": "^1.0.5", + "express": "^4.15.5", "express-session": "^1.15.5", "faker": "^4.1.0", "feedparser": "^2.2.1", @@ -88,35 +90,7 @@ "mailparser": "^2.0.5", "marked": "^0.3.6", "memory-cache": "^0.2.0", - "mjml": "3.3.3", - "mjml-accordion": "3.3.3", - "mjml-button": "3.3.3", - "mjml-carousel": "3.3.3", - "mjml-cli": "3.3.3", - "mjml-column": "3.3.3", - "mjml-container": "3.3.3", - "mjml-core": "3.3.3", - "mjml-divider": "3.3.3", - "mjml-group": "3.3.3", - "mjml-head-attributes": "3.3.0", - "mjml-head-font": "3.3.0", - "mjml-head-preview": "3.3.3", - "mjml-head-style": "3.3.0", - "mjml-head-title": "3.3.0", - "mjml-hero": "3.3.3", - "mjml-html": "3.3.3", - "mjml-image": "3.3.3", - "mjml-invoice": "3.3.3", - "mjml-list": "3.3.3", - "mjml-location": "3.3.3", - "mjml-navbar": "3.3.3", - "mjml-raw": "3.3.3", - "mjml-section": "3.3.3", - "mjml-social": "3.3.3", - "mjml-spacer": "3.3.3", - "mjml-table": "3.3.3", - "mjml-text": "3.3.3", - "mjml-wrapper": "3.3.3", + "mjml": "3.3.5", "mkdirp": "^0.5.1", "moment-timezone": "^0.5.13", "morgan": "^1.8.2", @@ -124,8 +98,8 @@ "multiparty": "^4.1.3", "mysql": "^2.14.1", "node-gettext": "^2.0.0", - "node-mocks-http": "^1.6.4", - "nodemailer": "^4.1.0", + "node-mocks-http": "^1.6.5", + "nodemailer": "^4.1.1", "nodemailer-openpgp": "^1.1.0", "npmlog": "^4.1.2", "object-hash": "^1.1.8", @@ -136,7 +110,7 @@ "redfour": "^1.0.2", "redis": "^2.8.0", "request": "^2.82.0", - "request-promise": "^4.2.1", + "request-promise": "^4.2.2", "serve-favicon": "^2.4.4", "shortid": "^2.2.8", "slugify": "^1.2.1",