From 9018ab8c5f5b7dd249a744d13d60b204a3af31c7 Mon Sep 17 00:00:00 2001 From: wxa Date: Wed, 10 Jul 2019 13:14:00 +0300 Subject: [PATCH] Add blacklist to Mailer [MAILPOET-2176] --- lib/Mailer/Methods/AmazonSES.php | 6 ++++ lib/Mailer/Methods/BlacklistTrait.php | 33 +++++++++++++++++++ .../Methods/ErrorMappers/AmazonSESMapper.php | 3 ++ .../BlacklistErrorMapperTrait.php | 14 ++++++++ .../Methods/ErrorMappers/MailPoetMapper.php | 4 +++ .../Methods/ErrorMappers/PHPMailMapper.php | 3 ++ .../Methods/ErrorMappers/SMTPMapper.php | 3 ++ .../Methods/ErrorMappers/SendGridMapper.php | 3 ++ lib/Mailer/Methods/MailPoet.php | 14 ++++++++ lib/Mailer/Methods/PHPMail.php | 6 ++++ lib/Mailer/Methods/SMTP.php | 6 ++++ lib/Mailer/Methods/SendGrid.php | 6 ++++ lib/Subscription/Blacklist.php | 24 ++++++++++++++ 13 files changed, 125 insertions(+) create mode 100644 lib/Mailer/Methods/BlacklistTrait.php create mode 100644 lib/Mailer/Methods/ErrorMappers/BlacklistErrorMapperTrait.php create mode 100644 lib/Subscription/Blacklist.php diff --git a/lib/Mailer/Methods/AmazonSES.php b/lib/Mailer/Methods/AmazonSES.php index 1c7ce29a0c..0010395ce3 100644 --- a/lib/Mailer/Methods/AmazonSES.php +++ b/lib/Mailer/Methods/AmazonSES.php @@ -8,6 +8,8 @@ use MailPoet\WP\Functions as WPFunctions; if (!defined('ABSPATH')) exit; class AmazonSES { + use BlacklistTrait; + public $aws_access_key; public $aws_secret_key; public $aws_region; @@ -67,6 +69,10 @@ class AmazonSES { } function send($newsletter, $subscriber, $extra_params = []) { + if ($this->isBlacklisted($subscriber)) { + $error = $this->error_mapper->getBlacklistError($subscriber); + return Mailer::formatMailerErrorResult($error); + } try { $result = $this->wp->wpRemotePost( $this->url, diff --git a/lib/Mailer/Methods/BlacklistTrait.php b/lib/Mailer/Methods/BlacklistTrait.php new file mode 100644 index 0000000000..68d8aae061 --- /dev/null +++ b/lib/Mailer/Methods/BlacklistTrait.php @@ -0,0 +1,33 @@ +getSubscriberEmailForBlacklistCheck($subscriber); + return $this->getBlacklist()->isBlacklisted($email); + } + + function getSubscriberEmailForBlacklistCheck($subscriber) { + preg_match('!(?P.*?)\s<(?P.*?)>!', $subscriber, $subscriber_data); + if (!isset($subscriber_data['email'])) { + return $subscriber; + } + return $subscriber_data['email']; + } + + function getBlacklist() { + if (!$this->blacklist instanceof Blacklist) { + $this->blacklist = new Blacklist(); + } + return $this->blacklist; + } + + function setBlacklist(Blacklist $blacklist) { + $this->blacklist = $blacklist; + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php b/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php index 7e5f35ac88..265f7a6e67 100644 --- a/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php +++ b/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php @@ -7,8 +7,11 @@ use MailPoet\Mailer\SubscriberError; use MailPoet\WP\Functions as WPFunctions; class AmazonSESMapper { + use BlacklistErrorMapperTrait; use ConnectionErrorMapperTrait; + const METHOD = Mailer::METHOD_AMAZONSES; + function getErrorFromException(\Exception $e, $subscriber) { $level = MailerError::LEVEL_HARD; if ($e instanceof \Swift_RfcComplianceException) { diff --git a/lib/Mailer/Methods/ErrorMappers/BlacklistErrorMapperTrait.php b/lib/Mailer/Methods/ErrorMappers/BlacklistErrorMapperTrait.php new file mode 100644 index 0000000000..de3feae348 --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/BlacklistErrorMapperTrait.php @@ -0,0 +1,14 @@ +__('%s has returned an unknown error.', 'mailpoet'), self::METHOD); + $subscriber_errors = [new SubscriberError($subscriber, null)]; + return new MailerError(MailerError::OPERATION_SEND, MailerError::LEVEL_SOFT, $message, null, $subscriber_errors); + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/MailPoetMapper.php b/lib/Mailer/Methods/ErrorMappers/MailPoetMapper.php index 74bd8885b5..d5e1538cb0 100644 --- a/lib/Mailer/Methods/ErrorMappers/MailPoetMapper.php +++ b/lib/Mailer/Methods/ErrorMappers/MailPoetMapper.php @@ -2,6 +2,7 @@ namespace MailPoet\Mailer\Methods\ErrorMappers; use MailPoet\Mailer\MailerError; +use MailPoet\Mailer\Mailer; use MailPoet\Mailer\SubscriberError; use MailPoet\Services\Bridge\API; use InvalidArgumentException; @@ -14,8 +15,11 @@ if (!defined('ABSPATH')) exit; class MailPoetMapper { + use BlacklistErrorMapperTrait; use ConnectionErrorMapperTrait; + const METHOD = Mailer::METHOD_MAILPOET; + const TEMPORARY_UNAVAILABLE_RETRY_INTERVAL = 300; // seconds function getInvalidApiKeyError() { diff --git a/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php b/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php index 02af5c2786..d7225c4986 100644 --- a/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php +++ b/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php @@ -7,8 +7,11 @@ use MailPoet\Mailer\SubscriberError; use MailPoet\WP\Functions as WPFunctions; class PHPMailMapper { + use BlacklistErrorMapperTrait; use ConnectionErrorMapperTrait; + const METHOD = Mailer::METHOD_PHPMAIL; + function getErrorFromException(\Exception $e, $subscriber) { $level = MailerError::LEVEL_HARD; if (strpos($e->getMessage(), 'Invalid address') === 0) { diff --git a/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php b/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php index 6cdaf81f06..7f29c371e2 100644 --- a/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php +++ b/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php @@ -7,8 +7,11 @@ use MailPoet\Mailer\SubscriberError; use MailPoet\WP\Functions as WPFunctions; class SMTPMapper { + use BlacklistErrorMapperTrait; use ConnectionErrorMapperTrait; + const METHOD = Mailer::METHOD_SMTP; + /** * @see https://swiftmailer.symfony.com/docs/sending.html * @return MailerError diff --git a/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php b/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php index da5e2461a1..46fad2b804 100644 --- a/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php +++ b/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php @@ -7,8 +7,11 @@ use MailPoet\Mailer\SubscriberError; use MailPoet\WP\Functions as WPFunctions; class SendGridMapper { + use BlacklistErrorMapperTrait; use ConnectionErrorMapperTrait; + const METHOD = Mailer::METHOD_SENDGRID; + function getErrorFromResponse($response, $subscriber) { $response = (!empty($response['errors'][0])) ? $response['errors'][0] : diff --git a/lib/Mailer/Methods/MailPoet.php b/lib/Mailer/Methods/MailPoet.php index 64feebf7c1..ffb8b9a2d6 100644 --- a/lib/Mailer/Methods/MailPoet.php +++ b/lib/Mailer/Methods/MailPoet.php @@ -12,6 +12,8 @@ use MailPoet\Services\Bridge\API; if (!defined('ABSPATH')) exit; class MailPoet { + use BlacklistTrait; + public $api; public $sender; public $reply_to; @@ -37,6 +39,14 @@ class MailPoet { return Mailer::formatMailerErrorResult($this->error_mapper->getInvalidApiKeyError()); } + $subscribers_for_blacklist_check = is_array($subscriber) ? $subscriber : [$subscriber]; + foreach ($subscribers_for_blacklist_check as $sub) { + if ($this->isBlacklisted($sub)) { + $error = $this->error_mapper->getBlacklistError($sub); + return Mailer::formatMailerErrorResult($error); + } + } + $message_body = $this->getBody($newsletter, $subscriber, $extra_params); $result = $this->api->sendMessages($message_body); @@ -125,4 +135,8 @@ class MailPoet { } return $body; } + + function checkBlacklist(array $subscribers) { + + } } diff --git a/lib/Mailer/Methods/PHPMail.php b/lib/Mailer/Methods/PHPMail.php index d2c72b0c5a..39cfff057e 100644 --- a/lib/Mailer/Methods/PHPMail.php +++ b/lib/Mailer/Methods/PHPMail.php @@ -10,6 +10,8 @@ if (!defined('ABSPATH')) exit; require_once ABSPATH . WPINC . '/class-phpmailer.php'; class PHPMail { + use BlacklistTrait; + public $sender; public $reply_to; public $return_path; @@ -29,6 +31,10 @@ class PHPMail { } function send($newsletter, $subscriber, $extra_params = []) { + if ($this->isBlacklisted($subscriber)) { + $error = $this->error_mapper->getBlacklistError($subscriber); + return Mailer::formatMailerErrorResult($error); + } try { $mailer = $this->configureMailerWithMessage($newsletter, $subscriber, $extra_params); $result = $mailer->send(); diff --git a/lib/Mailer/Methods/SMTP.php b/lib/Mailer/Methods/SMTP.php index 880e9c71c2..9c385659ad 100644 --- a/lib/Mailer/Methods/SMTP.php +++ b/lib/Mailer/Methods/SMTP.php @@ -8,6 +8,8 @@ use MailPoet\WP\Functions as WPFunctions; if (!defined('ABSPATH')) exit; class SMTP { + use BlacklistTrait; + public $host; public $port; public $authentication; @@ -48,6 +50,10 @@ class SMTP { } function send($newsletter, $subscriber, $extra_params = []) { + if ($this->isBlacklisted($subscriber)) { + $error = $this->error_mapper->getBlacklistError($subscriber); + return Mailer::formatMailerErrorResult($error); + } try { $message = $this->createMessage($newsletter, $subscriber, $extra_params); $result = $this->mailer->send($message); diff --git a/lib/Mailer/Methods/SendGrid.php b/lib/Mailer/Methods/SendGrid.php index ec2245aedf..3d9464c4bf 100644 --- a/lib/Mailer/Methods/SendGrid.php +++ b/lib/Mailer/Methods/SendGrid.php @@ -9,6 +9,8 @@ use MailPoet\WP\Functions as WPFunctions; if (!defined('ABSPATH')) exit; class SendGrid { + use BlacklistTrait; + public $url = 'https://api.sendgrid.com/api/mail.send.json'; public $api_key; public $sender; @@ -28,6 +30,10 @@ class SendGrid { } function send($newsletter, $subscriber, $extra_params = []) { + if ($this->isBlacklisted($subscriber)) { + $error = $this->error_mapper->getBlacklistError($subscriber); + return Mailer::formatMailerErrorResult($error); + } $result = $this->wp->wpRemotePost( $this->url, $this->request($newsletter, $subscriber, $extra_params) diff --git a/lib/Subscription/Blacklist.php b/lib/Subscription/Blacklist.php new file mode 100644 index 0000000000..96f09dece0 --- /dev/null +++ b/lib/Subscription/Blacklist.php @@ -0,0 +1,24 @@ + 1, + ]; + + public function isBlacklisted($email) { + $hashed_email = $this->hash($email); + return !empty($this->blacklist[$hashed_email]); + } + + public function hash($email) { + return hash('sha256', $email . self::SALT); + } + + public function addEmail($email) { + $hashed_email = $this->hash($email); + $this->blacklist[$hashed_email] = 1; + } +}