From 3c72e778d9d74f012142ec291fc05099dfe48f7e Mon Sep 17 00:00:00 2001 From: Tomas Bures Date: Mon, 20 May 2019 00:21:03 +0200 Subject: [PATCH] Merged PR #528 Support for detecting MTA by its response. Message IDs are reconstructed based on detected MTA. Bugfixes for AWS. AWS now seems to work. --- client/src/campaigns/triggers/CUD.js | 20 ++++----- client/src/lists/subscriptions/List.js | 17 ++++---- client/src/login/Reset.js | 19 +++++++-- client/src/send-configurations/CUD.js | 14 +++---- server/lib/campaign-sender.js | 56 +++++++++++++++++++------- server/routes/webhooks.js | 2 - 6 files changed, 83 insertions(+), 45 deletions(-) diff --git a/client/src/campaigns/triggers/CUD.js b/client/src/campaigns/triggers/CUD.js index aef1321e..37b9d3f8 100644 --- a/client/src/campaigns/triggers/CUD.js +++ b/client/src/campaigns/triggers/CUD.js @@ -93,6 +93,14 @@ export default class CUD extends Component { } submitFormValuesMutator(data) { + data.seconds = Number.parseInt(data.daysAfter) * 3600 * 24; + + if (data.entity === Entity.SUBSCRIPTION) { + data.event = data.subscriptionEvent; + } else if (data.entity === Entity.CAMPAIGN) { + data.event = data.campaignEvent; + } + return filterData(data, ['name', 'description', 'entity', 'event', 'seconds', 'enabled', 'source_campaign']); } @@ -157,22 +165,14 @@ export default class CUD extends Component { this.disableForm(); this.setFormStatusMessage('info', t('saving')); - const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => { - data.seconds = Number.parseInt(data.daysAfter) * 3600 * 24; - - if (data.entity === Entity.SUBSCRIPTION) { - data.event = data.subscriptionEvent; - } else if (data.entity === Entity.CAMPAIGN) { - data.event = data.campaignEvent; - } - }); + const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url); if (submitResult) { if (this.props.entity) { if (submitAndLeave) { this.navigateToWithFlashMessage(`/campaigns/${this.props.campaign.id}/triggers`, 'success', t('triggerUpdated')); } else { - await this.getFormValuesFromURL(`rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`, ::this.getFormValuesMutator); + await this.getFormValuesFromURL(`rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`); this.enableForm(); this.setFormStatusMessage('success', t('triggerUpdated')); } diff --git a/client/src/lists/subscriptions/List.js b/client/src/lists/subscriptions/List.js index c69f6490..1938643a 100644 --- a/client/src/lists/subscriptions/List.js +++ b/client/src/lists/subscriptions/List.js @@ -57,18 +57,21 @@ export default class List extends Component { segmentId: PropTypes.string } - updateSegmentSelection(props) { + componentDidMount() { this.populateFormValues({ - segment: props.segmentId || '' + segment: this.props.segmentId || '' }); } - componentDidMount() { - this.updateSegmentSelection(this.props); - } - componentDidUpdate() { - this.updateSegmentSelection(this.props); + const segmentId = this.props.segmentId || ''; + + if (this.getFormValue('segment') !== segmentId) { + // Populate is used here because it does not invoke onChange + this.populateFormValues({ + segment: segmentId + }); + } } render() { diff --git a/client/src/login/Reset.js b/client/src/login/Reset.js index 4c6335a5..f9fe5b6c 100644 --- a/client/src/login/Reset.js +++ b/client/src/login/Reset.js @@ -4,7 +4,16 @@ import React, {Component} from 'react'; import {withTranslation} from '../lib/i18n'; import {Title, withPageHelpers} from '../lib/page' import {Link} from 'react-router-dom' -import {Button, ButtonRow, Form, FormSendMethod, InputField, withForm, withFormErrorHandlers} from '../lib/form'; +import { + Button, + ButtonRow, + filterData, + Form, + FormSendMethod, + InputField, + withForm, + withFormErrorHandlers +} from '../lib/form'; import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling'; import passwordValidator from '../../../shared/password-validator'; import axios from '../lib/axios'; @@ -39,6 +48,10 @@ export default class Account extends Component { }); } + submitFormValuesMutator(data) { + return filterData(data, ['password']); + } + @withAsyncErrorHandler async validateResetToken() { const params = this.props.match.params; @@ -96,9 +109,7 @@ export default class Account extends Component { this.disableForm(); this.setFormStatusMessage('info', t('resettingPassword')); - const submitSuccessful = await this.validateAndSendFormValuesToURL(FormSendMethod.POST, 'rest/password-reset', data => { - delete data.password2; - }); + const submitSuccessful = await this.validateAndSendFormValuesToURL(FormSendMethod.POST, 'rest/password-reset'); if (submitSuccessful) { this.navigateToWithFlashMessage('/login', 'success', t('passwordReset-1')); diff --git a/client/src/send-configurations/CUD.js b/client/src/send-configurations/CUD.js index 992c2875..97687f13 100644 --- a/client/src/send-configurations/CUD.js +++ b/client/src/send-configurations/CUD.js @@ -78,6 +78,12 @@ export default class CUD extends Component { } submitFormValuesMutator(data) { + this.mailerTypes[data.mailer_type].beforeSave(data); + if (!data.verpEnabled) { + data.verp_hostname = null; + data.verp_disable_sender_header = false; + } + return filterData(data, ['name', 'description', 'from_email', 'from_email_overridable', 'from_name', 'from_name_overridable', 'reply_to', 'reply_to_overridable', 'subject', 'subject_overridable', 'x_mailer', 'verp_hostname', 'verp_disable_sender_header', 'mailer_type', 'mailer_settings', 'namespace']); @@ -155,13 +161,7 @@ export default class CUD extends Component { this.disableForm(); this.setFormStatusMessage('info', t('saving')); - const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => { - this.mailerTypes[data.mailer_type].beforeSave(data); - if (!data.verpEnabled) { - data.verp_hostname = null; - data.verp_disable_sender_header = false; - } - }); + const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url); if (submitResult) { if (this.props.entity) { diff --git a/server/lib/campaign-sender.js b/server/lib/campaign-sender.js index 43ab4ad5..a0d7a44c 100644 --- a/server/lib/campaign-sender.js +++ b/server/lib/campaign-sender.js @@ -1,6 +1,7 @@ 'use strict'; const config = require('config'); +const log = require('./log'); const mailers = require('./mailers'); const knex = require('./knex'); const subscriptions = require('../models/subscriptions'); @@ -20,8 +21,7 @@ const htmlToText = require('html-to-text'); const {getPublicUrl} = require('./urls'); const blacklist = require('../models/blacklist'); const libmime = require('libmime'); -const shares = require('../models/shares') - +const shares = require('../models/shares'); class CampaignSender { constructor() { @@ -83,7 +83,7 @@ class CampaignSender { const getOverridable = key => { return sendConfiguration[key]; - } + }; const campaignAddress = [campaign.cid, list.cid, subscriptionGrouped.cid].join('.'); @@ -95,7 +95,7 @@ class CampaignSender { replyTo: getOverridable('reply_to'), xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false, to: { - name: tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false), + name: list.to_name === null ? undefined : tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false), address: subscriptionGrouped.email }, sender: useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false, @@ -336,7 +336,7 @@ class CampaignSender { } else { return sendConfiguration[key] || ''; } - } + }; const mail = { from: { @@ -346,7 +346,7 @@ class CampaignSender { replyTo: getOverridable('reply_to'), xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false, to: { - name: tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false), + name: list.to_name === null ? undefined : tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false), address: subscriptionGrouped.email }, sender: this.useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false, @@ -391,24 +391,49 @@ class CampaignSender { let status; let response; - let responseId; + let responseId = null; try { const info = await mailer.sendMassMail(mail); status = SubscriptionStatus.SUBSCRIBED; - /* - ZoneMTA + log.verbose('CampaignSender', `response: ${info.response} messageId: ${info.messageId}`); + + let match; + if ((match = info.response.match(/^250 Message queued as ([0-9a-f]+)$/))) { + /* + ZoneMTA info.response: 250 Message queued as 1691ad7f7ae00080fd info.messageId: + */ + response = info.response; + responseId = match[1]; - Postal Mail Server + } else if ((match = info.messageId.match(/^<([^>@]*)@.*amazonses\.com>$/))) { + /* + AWS SES + info.response: 0102016ad2244c0a-955492f2-9194-4cd1-bef9-70a45906a5a7-000000 + info.messageId: <0102016ad2244c0a-955492f2-9194-4cd1-bef9-70a45906a5a7-000000@eu-west-1.amazonses.com> + */ + response = info.response; + responseId = match[1]; + + } else if (info.response.match(/^250 OK$/) && (match = info.messageId.match(/^<([^>]*)>$/))) { + /* + Postal Mail Server info.response: 250 OK info.messageId: (postal messageId) - */ + */ + response = info.response; + responseId = match[1]; + + } else { + /* + Fallback - Mailtrain v1 behavior + */ + response = info.response || info.messageId; + responseId = response.split(/\s+/).pop(); + } - console.log(`response: ${info.response} messageId: ${info.messageId}`); - response = info.response || info.messageId; - responseId = info.messageId.replace(/(^<|>$)/g, "") || response.split(/\s+/).pop(); await knex('campaigns').where('id', campaign.id).increment('delivered'); } catch (err) { @@ -417,6 +442,7 @@ class CampaignSender { await knex('campaigns').where('id', campaign.id).increment('delivered').increment('bounced'); } + const now = new Date(); if (campaign.type === CampaignType.REGULAR || campaign.type === CampaignType.RSS_ENTRY) { @@ -443,4 +469,4 @@ class CampaignSender { } } -module.exports = CampaignSender; +module.exports = CampaignSender; \ No newline at end of file diff --git a/server/routes/webhooks.js b/server/routes/webhooks.js index 98d35854..0304c832 100644 --- a/server/routes/webhooks.js +++ b/server/routes/webhooks.js @@ -13,8 +13,6 @@ const uploads = multer(); router.postAsync('/aws', async (req, res) => { - console.log(req.body); - if (typeof req.body === 'string') { req.body = JSON.parse(req.body); }