Support for detecting MTA by its response. Message IDs are reconstructed based on detected MTA.
Bugfixes for AWS. AWS now seems to work.
This commit is contained in:
Tomas Bures 2019-05-20 00:21:03 +02:00
parent bbbe671d59
commit 3c72e778d9
6 changed files with 83 additions and 45 deletions

View file

@ -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'));
}

View file

@ -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() {

View file

@ -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'));

View file

@ -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) {

View file

@ -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: <e65c9386-e899-7d01-b21e-ec03c3a9d9b4@sathyasai.org>
*/
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: <xxxxxxxxx@xxx.xx> (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;

View file

@ -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);
}