2022-11-17 16:36:21 +01:00
|
|
|
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
2019-05-23 17:52:45 +02:00
|
|
|
|
|
|
|
namespace MailPoet\Services;
|
|
|
|
|
2020-05-13 13:35:44 +02:00
|
|
|
use MailPoet\Entities\NewsletterEntity;
|
2020-03-25 12:34:57 +01:00
|
|
|
use MailPoet\Mailer\Mailer;
|
2019-05-27 13:49:51 +02:00
|
|
|
use MailPoet\Mailer\MailerError;
|
|
|
|
use MailPoet\Mailer\MailerLog;
|
2020-03-25 12:34:57 +01:00
|
|
|
use MailPoet\Newsletter\NewslettersRepository;
|
2023-01-06 16:46:53 +01:00
|
|
|
use MailPoet\Services\Bridge\API;
|
2019-05-23 17:52:45 +02:00
|
|
|
use MailPoet\Settings\SettingsController;
|
2022-09-16 13:11:46 +01:00
|
|
|
use MailPoet\Util\Helpers;
|
2024-09-26 20:26:03 +01:00
|
|
|
use MailPoet\WP\Functions as WPFunctions;
|
2019-05-23 17:52:45 +02:00
|
|
|
|
|
|
|
class AuthorizedEmailsController {
|
|
|
|
const AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING = 'authorized_emails_addresses_check';
|
|
|
|
|
2022-07-01 14:59:02 +01:00
|
|
|
const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED = 'authorized';
|
|
|
|
const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING = 'pending';
|
|
|
|
const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL = 'all';
|
|
|
|
const AUTHORIZED_EMAIL_ERROR_ALREADY_AUTHORIZED = 'Email address is already authorized';
|
|
|
|
const AUTHORIZED_EMAIL_ERROR_PENDING_CONFIRMATION = 'Email address is pending confirmation';
|
|
|
|
|
2024-09-26 20:26:03 +01:00
|
|
|
const AUTHORIZED_EMAILS_KEY = 'mailpoet_authorized_email_addresses';
|
|
|
|
|
2019-05-23 17:52:45 +02:00
|
|
|
/** @var Bridge */
|
2019-05-27 14:14:50 +02:00
|
|
|
private $bridge;
|
2019-05-23 17:52:45 +02:00
|
|
|
|
|
|
|
/** @var SettingsController */
|
|
|
|
private $settings;
|
|
|
|
|
2020-03-25 12:34:57 +01:00
|
|
|
/** @var NewslettersRepository */
|
|
|
|
private $newslettersRepository;
|
|
|
|
|
2022-09-13 16:01:42 +01:00
|
|
|
/** @var AuthorizedSenderDomainController */
|
|
|
|
private $senderDomainController;
|
|
|
|
|
2024-09-26 20:26:03 +01:00
|
|
|
/** @var WPFunctions */
|
|
|
|
private $wp;
|
|
|
|
|
2019-05-27 14:14:50 +02:00
|
|
|
private $automaticEmailTypes = [
|
2022-07-27 17:52:03 -03:00
|
|
|
NewsletterEntity::TYPE_WELCOME,
|
|
|
|
NewsletterEntity::TYPE_NOTIFICATION,
|
|
|
|
NewsletterEntity::TYPE_AUTOMATIC,
|
2019-05-27 14:14:50 +02:00
|
|
|
];
|
|
|
|
|
2020-03-25 12:34:57 +01:00
|
|
|
public function __construct(
|
|
|
|
SettingsController $settingsController,
|
|
|
|
Bridge $bridge,
|
2022-09-13 16:01:42 +01:00
|
|
|
NewslettersRepository $newslettersRepository,
|
2024-09-26 20:26:03 +01:00
|
|
|
AuthorizedSenderDomainController $senderDomainController,
|
|
|
|
WPFunctions $wp
|
2020-03-25 12:34:57 +01:00
|
|
|
) {
|
2019-05-23 17:52:45 +02:00
|
|
|
$this->settings = $settingsController;
|
|
|
|
$this->bridge = $bridge;
|
2020-03-25 12:34:57 +01:00
|
|
|
$this->newslettersRepository = $newslettersRepository;
|
2022-09-13 16:01:42 +01:00
|
|
|
$this->senderDomainController = $senderDomainController;
|
2024-09-26 20:26:03 +01:00
|
|
|
$this->wp = $wp;
|
2020-03-25 12:34:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function setFromEmailAddress(string $address) {
|
2024-09-27 13:39:27 +01:00
|
|
|
$authorizedEmails = $this->getAuthorizedEmailAddresses() ?: [];
|
2022-09-16 06:42:38 +01:00
|
|
|
$verifiedDomains = $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache();
|
2020-03-25 12:34:57 +01:00
|
|
|
$isAuthorized = $this->validateAuthorizedEmail($authorizedEmails, $address);
|
2022-09-13 16:01:42 +01:00
|
|
|
|
|
|
|
$emailDomainIsVerified = $this->validateEmailDomainIsVerified($verifiedDomains, $address);
|
|
|
|
|
|
|
|
if (!$emailDomainIsVerified && !$isAuthorized) {
|
2020-03-25 12:34:57 +01:00
|
|
|
throw new \InvalidArgumentException("Email address '$address' is not authorized");
|
|
|
|
}
|
|
|
|
|
|
|
|
// update FROM address in settings & all scheduled and active emails
|
|
|
|
$this->settings->set('sender.address', $address);
|
2022-09-13 16:01:42 +01:00
|
|
|
$result = $this->validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains);
|
2020-03-25 12:34:57 +01:00
|
|
|
foreach ($result['invalid_senders_in_newsletters'] ?? [] as $item) {
|
|
|
|
$newsletter = $this->newslettersRepository->findOneById((int)$item['newsletter_id']);
|
|
|
|
if ($newsletter) {
|
|
|
|
$newsletter->setSenderAddress($address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->newslettersRepository->flush();
|
|
|
|
$this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2024-09-27 13:39:27 +01:00
|
|
|
public function getAuthorizedEmailAddresses(string $type = 'authorized'): array {
|
|
|
|
$data = $this->bridge->getAuthorizedEmailAddresses();
|
2024-09-26 20:26:03 +01:00
|
|
|
if (empty($data)) {
|
|
|
|
// API is potentially down, fallback to cache
|
|
|
|
$data = $this->wp->getTransient(self::AUTHORIZED_EMAILS_KEY);
|
|
|
|
} else {
|
|
|
|
$this->wp->setTransient(self::AUTHORIZED_EMAILS_KEY, $data, WEEK_IN_SECONDS);
|
|
|
|
}
|
|
|
|
|
2024-09-27 13:39:27 +01:00
|
|
|
if ($data && $type === self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL) {
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $data[$type] ?? [];
|
2022-07-01 14:59:02 +01:00
|
|
|
}
|
|
|
|
|
2022-07-12 09:38:47 +01:00
|
|
|
public function createAuthorizedEmailAddress(string $email): array {
|
2024-09-27 13:39:27 +01:00
|
|
|
$allEmails = $this->getAuthorizedEmailAddresses(self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL);
|
2022-07-01 14:59:02 +01:00
|
|
|
|
2022-07-06 19:22:04 +01:00
|
|
|
$authorizedEmails = isset($allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED]) ? $allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED] : [];
|
2022-07-01 14:59:02 +01:00
|
|
|
$isAuthorized = $this->validateAuthorizedEmail($authorizedEmails, $email);
|
|
|
|
|
|
|
|
if ($isAuthorized) {
|
|
|
|
throw new \InvalidArgumentException(self::AUTHORIZED_EMAIL_ERROR_ALREADY_AUTHORIZED);
|
|
|
|
}
|
|
|
|
|
2022-07-06 19:22:04 +01:00
|
|
|
$pendingEmails = isset($allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING]) ? $allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING] : [];
|
2022-07-01 14:59:02 +01:00
|
|
|
$isPending = $this->validateAuthorizedEmail($pendingEmails, $email);
|
|
|
|
|
|
|
|
if ($isPending) {
|
|
|
|
throw new \InvalidArgumentException(self::AUTHORIZED_EMAIL_ERROR_PENDING_CONFIRMATION);
|
|
|
|
}
|
|
|
|
|
2023-01-06 16:46:53 +01:00
|
|
|
$response = $this->bridge->createAuthorizedEmailAddress($email);
|
2023-01-10 19:50:58 +01:00
|
|
|
if ($response['status'] === API::RESPONSE_STATUS_ERROR) {
|
2023-01-06 16:46:53 +01:00
|
|
|
throw new \InvalidArgumentException($response['message']);
|
2022-07-06 15:44:29 +01:00
|
|
|
}
|
|
|
|
|
2023-01-06 16:46:53 +01:00
|
|
|
return $response;
|
2022-07-01 14:59:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function isEmailAddressAuthorized(string $email): bool {
|
2024-09-27 13:39:27 +01:00
|
|
|
$authorizedEmails = $this->getAuthorizedEmailAddresses() ?: [];
|
2022-07-01 14:59:02 +01:00
|
|
|
return $this->validateAuthorizedEmail($authorizedEmails, $email);
|
|
|
|
}
|
|
|
|
|
2019-12-26 12:56:49 +01:00
|
|
|
public function checkAuthorizedEmailAddresses() {
|
2024-09-17 18:00:26 +05:30
|
|
|
if (!$this->bridge->isMailpoetSendingServiceEnabled()) {
|
2019-05-23 17:52:45 +02:00
|
|
|
$this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
|
2019-05-27 13:49:51 +02:00
|
|
|
$this->updateMailerLog();
|
2021-10-28 20:42:06 +02:00
|
|
|
return null;
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2024-09-27 13:39:27 +01:00
|
|
|
$authorizedEmails = $this->getAuthorizedEmailAddresses();
|
2019-05-23 17:52:45 +02:00
|
|
|
// Keep previous check result for an invalid response from API
|
2022-06-08 11:58:38 +03:00
|
|
|
if (!$authorizedEmails) {
|
2021-10-28 20:42:06 +02:00
|
|
|
return null;
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
2019-05-27 12:48:31 +02:00
|
|
|
$authorizedEmails = array_map('strtolower', $authorizedEmails);
|
2019-05-23 17:52:45 +02:00
|
|
|
|
2022-09-16 06:42:38 +01:00
|
|
|
$verifiedDomains = $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache();
|
2022-09-13 16:01:42 +01:00
|
|
|
|
2019-05-27 12:48:31 +02:00
|
|
|
$result = [];
|
2022-09-13 16:01:42 +01:00
|
|
|
$result = $this->validateAddressesInSettings($authorizedEmails, $verifiedDomains, $result);
|
|
|
|
$result = $this->validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains, $result);
|
2019-05-23 17:52:45 +02:00
|
|
|
$this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, $result ?: null);
|
2019-05-27 13:49:51 +02:00
|
|
|
$this->updateMailerLog($result);
|
2021-10-28 20:42:06 +02:00
|
|
|
return $result;
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2021-10-28 20:42:06 +02:00
|
|
|
public function onSettingsSave($settings): ?array {
|
2019-05-23 17:52:45 +02:00
|
|
|
$senderAddressSet = !empty($settings['sender']['address']);
|
2020-03-25 12:34:16 +01:00
|
|
|
$mailpoetSendingMethodSet = ($settings[Mailer::MAILER_CONFIG_SETTING_NAME]['method'] ?? null) === Mailer::METHOD_MAILPOET;
|
|
|
|
if ($senderAddressSet || $mailpoetSendingMethodSet) {
|
2021-10-28 20:42:06 +02:00
|
|
|
return $this->checkAuthorizedEmailAddresses();
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
2021-10-28 20:42:06 +02:00
|
|
|
return null;
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|
|
|
|
|
2025-03-06 08:16:02 +02:00
|
|
|
public function onNewsletterSenderAddressUpdate(NewsletterEntity $newsletter, ?string $oldSenderAddress = null) {
|
2020-05-13 13:35:44 +02:00
|
|
|
if ($newsletter->getSenderAddress() === $oldSenderAddress) {
|
2019-05-27 14:14:50 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-05-13 13:35:44 +02:00
|
|
|
if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD && $newsletter->getStatus() === NewsletterEntity::STATUS_SCHEDULED) {
|
2019-05-27 14:14:50 +02:00
|
|
|
$this->checkAuthorizedEmailAddresses();
|
|
|
|
}
|
2022-07-27 17:52:03 -03:00
|
|
|
if (in_array($newsletter->getType(), $this->automaticEmailTypes, true) && $newsletter->getStatus() === NewsletterEntity::STATUS_ACTIVE) {
|
2019-05-27 14:14:50 +02:00
|
|
|
$this->checkAuthorizedEmailAddresses();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-17 16:03:58 -06:00
|
|
|
public function isSenderAddressValid(NewsletterEntity $newsletter, string $context = 'activation'): bool {
|
2024-01-13 00:32:23 -06:00
|
|
|
if (!in_array($newsletter->getType(), NewsletterEntity::CAMPAIGN_TYPES)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-19 17:13:26 -06:00
|
|
|
$isAuthorizedDomainRequired = $context === 'activation'
|
|
|
|
? $this->senderDomainController->isAuthorizedDomainRequiredForNewCampaigns()
|
|
|
|
: $this->senderDomainController->isAuthorizedDomainRequiredForExistingCampaigns();
|
2024-01-17 16:03:58 -06:00
|
|
|
|
|
|
|
if (!$isAuthorizedDomainRequired) {
|
2024-01-13 00:32:23 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-19 17:13:26 -06:00
|
|
|
$verifiedDomains = $context === 'activation'
|
|
|
|
? $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache()
|
|
|
|
: $this->senderDomainController->getVerifiedSenderDomains();
|
2024-01-18 23:18:06 -06:00
|
|
|
|
2024-02-26 17:24:16 -06:00
|
|
|
// The shop is not returning data, so we allow sending and let the Sending Service block the campaign if needed.
|
|
|
|
if ($context === 'sending' && empty($verifiedDomains) && !$this->senderDomainController->isCacheAvailable()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-13 00:32:23 -06:00
|
|
|
return $this->validateEmailDomainIsVerified($verifiedDomains, $newsletter->getSenderAddress());
|
|
|
|
}
|
|
|
|
|
2022-09-13 16:01:42 +01:00
|
|
|
private function validateAddressesInSettings($authorizedEmails, $verifiedDomains, $result = []) {
|
2019-05-23 17:52:45 +02:00
|
|
|
$defaultSenderAddress = $this->settings->get('sender.address');
|
|
|
|
|
2022-09-13 16:01:42 +01:00
|
|
|
if ($this->validateEmailDomainIsVerified($verifiedDomains, $defaultSenderAddress)) {
|
|
|
|
// allow sending from any email address in a verified domain
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2019-05-27 12:48:31 +02:00
|
|
|
if (!$this->validateAuthorizedEmail($authorizedEmails, $defaultSenderAddress)) {
|
2019-05-23 17:52:45 +02:00
|
|
|
$result['invalid_sender_address'] = $defaultSenderAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
2019-05-27 12:48:31 +02:00
|
|
|
|
2022-09-13 16:01:42 +01:00
|
|
|
private function validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains, $result = []) {
|
2022-07-27 17:52:03 -03:00
|
|
|
$newsletters = $this->newslettersRepository->getScheduledStandardEmailsAndActiveAutomaticEmails($this->automaticEmailTypes);
|
2019-05-27 12:48:31 +02:00
|
|
|
|
|
|
|
$invalidSendersInNewsletters = [];
|
|
|
|
foreach ($newsletters as $newsletter) {
|
2022-07-27 17:52:03 -03:00
|
|
|
if ($this->validateAuthorizedEmail($authorizedEmails, $newsletter->getSenderAddress())) {
|
2019-05-27 12:48:31 +02:00
|
|
|
continue;
|
|
|
|
}
|
2022-09-13 16:01:42 +01:00
|
|
|
if ($this->validateEmailDomainIsVerified($verifiedDomains, $newsletter->getSenderAddress())) {
|
|
|
|
// allow sending from any email address in a verified domain
|
|
|
|
continue;
|
|
|
|
}
|
2019-05-27 12:48:31 +02:00
|
|
|
$invalidSendersInNewsletters[] = [
|
2022-07-27 17:52:03 -03:00
|
|
|
'newsletter_id' => $newsletter->getId(),
|
|
|
|
'subject' => $newsletter->getSubject(),
|
|
|
|
'sender_address' => $newsletter->getSenderAddress(),
|
2019-05-27 12:48:31 +02:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!count($invalidSendersInNewsletters)) {
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
$result['invalid_senders_in_newsletters'] = $invalidSendersInNewsletters;
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:49:51 +02:00
|
|
|
/**
|
|
|
|
* @param array|null $error
|
|
|
|
*/
|
2025-03-06 08:16:02 +02:00
|
|
|
private function updateMailerLog(?array $error = null) {
|
2019-05-27 13:49:51 +02:00
|
|
|
if ($error) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$mailerLogError = MailerLog::getError();
|
|
|
|
if ($mailerLogError && $mailerLogError['operation'] === MailerError::OPERATION_AUTHORIZATION) {
|
|
|
|
MailerLog::resumeSending();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-06 19:22:04 +01:00
|
|
|
private function validateAuthorizedEmail($authorizedEmails = [], $email = '') {
|
|
|
|
$lowercaseAuthorizedEmails = array_map('strtolower', $authorizedEmails);
|
|
|
|
return in_array(strtolower($email), $lowercaseAuthorizedEmails, true);
|
2019-05-27 12:48:31 +02:00
|
|
|
}
|
2022-09-13 16:01:42 +01:00
|
|
|
|
2022-09-16 06:42:38 +01:00
|
|
|
private function validateEmailDomainIsVerified(array $verifiedDomains = [], string $email = ''): bool {
|
2022-09-13 16:01:42 +01:00
|
|
|
$lowercaseVerifiedDomains = array_map('strtolower', $verifiedDomains);
|
2022-09-16 13:11:46 +01:00
|
|
|
$emailDomain = Helpers::extractEmailDomain($email);
|
|
|
|
return in_array($emailDomain, $lowercaseVerifiedDomains, true);
|
2022-09-13 16:01:42 +01:00
|
|
|
}
|
2019-05-23 17:52:45 +02:00
|
|
|
}
|