PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Services / AuthorizedEmailsController.php
mailpoet / lib / Services Last commit date
Bridge 5 days ago Release 2 years ago AuthorizedEmailsController.php 2 months ago AuthorizedSenderDomainController.php 5 months ago Bridge.php 4 months ago CongratulatoryMssEmailController.php 4 months ago SubscribersCountReporter.php 3 years ago Validator.php 3 years ago index.php 3 years ago
AuthorizedEmailsController.php
286 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Services;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\NewsletterEntity;
9 use MailPoet\Mailer\Mailer;
10 use MailPoet\Mailer\MailerError;
11 use MailPoet\Mailer\MailerLog;
12 use MailPoet\Newsletter\NewslettersRepository;
13 use MailPoet\Services\Bridge\API;
14 use MailPoet\Settings\SettingsController;
15 use MailPoet\Util\Helpers;
16 use MailPoet\WP\Functions as WPFunctions;
17
18 class AuthorizedEmailsController {
19 const AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING = 'authorized_emails_addresses_check';
20
21 const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED = 'authorized';
22 const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING = 'pending';
23 const AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL = 'all';
24 const AUTHORIZED_EMAIL_ERROR_ALREADY_AUTHORIZED = 'Email address is already authorized';
25 const AUTHORIZED_EMAIL_ERROR_PENDING_CONFIRMATION = 'Email address is pending confirmation';
26
27 const AUTHORIZED_EMAILS_KEY = 'mailpoet_authorized_email_addresses';
28
29 /** @var Bridge */
30 private $bridge;
31
32 /** @var SettingsController */
33 private $settings;
34
35 /** @var NewslettersRepository */
36 private $newslettersRepository;
37
38 /** @var AuthorizedSenderDomainController */
39 private $senderDomainController;
40
41 /** @var WPFunctions */
42 private $wp;
43
44 private $automaticEmailTypes = [
45 NewsletterEntity::TYPE_WELCOME,
46 NewsletterEntity::TYPE_NOTIFICATION,
47 NewsletterEntity::TYPE_AUTOMATIC,
48 NewsletterEntity::TYPE_AUTOMATION,
49 NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL,
50 ];
51
52 public function __construct(
53 SettingsController $settingsController,
54 Bridge $bridge,
55 NewslettersRepository $newslettersRepository,
56 AuthorizedSenderDomainController $senderDomainController,
57 WPFunctions $wp
58 ) {
59 $this->settings = $settingsController;
60 $this->bridge = $bridge;
61 $this->newslettersRepository = $newslettersRepository;
62 $this->senderDomainController = $senderDomainController;
63 $this->wp = $wp;
64 }
65
66 public function setFromEmailAddress(string $address) {
67 // Bundled subscription users who are small senders skip authorization
68 if ($this->senderDomainController->shouldSkipAuthorization()) {
69 $this->settings->set('sender.address', $address);
70 $this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
71 return;
72 }
73
74 $authorizedEmails = $this->getAuthorizedEmailAddresses() ?: [];
75 $verifiedDomains = $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache();
76 $isAuthorized = $this->validateAuthorizedEmail($authorizedEmails, $address);
77
78 $emailDomainIsVerified = $this->validateEmailDomainIsVerified($verifiedDomains, $address);
79
80 if (!$emailDomainIsVerified && !$isAuthorized) {
81 throw new \InvalidArgumentException("Email address '$address' is not authorized");
82 }
83
84 // update FROM address in settings & all scheduled and active emails
85 $this->settings->set('sender.address', $address);
86 $result = $this->validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains);
87 foreach ($result['invalid_senders_in_newsletters'] ?? [] as $item) {
88 $newsletter = $this->newslettersRepository->findOneById((int)$item['newsletter_id']);
89 if ($newsletter) {
90 $newsletter->setSenderAddress($address);
91 }
92 }
93 $this->newslettersRepository->flush();
94 $this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
95 }
96
97 public function getAuthorizedEmailAddresses(string $type = 'authorized'): array {
98 $data = $this->bridge->getAuthorizedEmailAddresses();
99 if (empty($data)) {
100 // API is potentially down, fallback to cache
101 $data = $this->wp->getTransient(self::AUTHORIZED_EMAILS_KEY);
102 } else {
103 $this->wp->setTransient(self::AUTHORIZED_EMAILS_KEY, $data, WEEK_IN_SECONDS);
104 }
105
106 if ($data && $type === self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL) {
107 return $data;
108 }
109
110 return $data[$type] ?? [];
111 }
112
113 public function createAuthorizedEmailAddress(string $email): array {
114 $allEmails = $this->getAuthorizedEmailAddresses(self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_ALL);
115
116 $authorizedEmails = isset($allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED]) ? $allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_AUTHORIZED] : [];
117 $isAuthorized = $this->validateAuthorizedEmail($authorizedEmails, $email);
118
119 if ($isAuthorized) {
120 throw new \InvalidArgumentException(self::AUTHORIZED_EMAIL_ERROR_ALREADY_AUTHORIZED);
121 }
122
123 $pendingEmails = isset($allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING]) ? $allEmails[self::AUTHORIZED_EMAIL_ADDRESSES_API_TYPE_PENDING] : [];
124 $isPending = $this->validateAuthorizedEmail($pendingEmails, $email);
125
126 if ($isPending) {
127 throw new \InvalidArgumentException(self::AUTHORIZED_EMAIL_ERROR_PENDING_CONFIRMATION);
128 }
129
130 $response = $this->bridge->createAuthorizedEmailAddress($email);
131 if ($response['status'] === API::RESPONSE_STATUS_ERROR) {
132 throw new \InvalidArgumentException($response['message']);
133 }
134
135 return $response;
136 }
137
138 public function isEmailAddressAuthorized(string $email): bool {
139 $authorizedEmails = $this->getAuthorizedEmailAddresses() ?: [];
140 return $this->validateAuthorizedEmail($authorizedEmails, $email);
141 }
142
143 public function checkAuthorizedEmailAddresses() {
144 if (!$this->bridge->isMailpoetSendingServiceEnabled()) {
145 $this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
146 $this->updateMailerLog();
147 return null;
148 }
149
150 // Bundled subscription users who are small senders skip authorization
151 if ($this->senderDomainController->shouldSkipAuthorization()) {
152 $this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, null);
153 $this->updateMailerLog();
154 return null;
155 }
156
157 $authorizedEmails = $this->getAuthorizedEmailAddresses();
158 // Keep previous check result for an invalid response from API
159 if (!$authorizedEmails) {
160 return null;
161 }
162 $authorizedEmails = array_map('strtolower', $authorizedEmails);
163
164 $verifiedDomains = $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache();
165
166 $result = [];
167 $result = $this->validateAddressesInSettings($authorizedEmails, $verifiedDomains, $result);
168 $result = $this->validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains, $result);
169 $this->settings->set(self::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING, $result ?: null);
170 $this->updateMailerLog($result);
171 return $result;
172 }
173
174 public function onSettingsSave($settings): ?array {
175 $senderAddressSet = !empty($settings['sender']['address']);
176 $mailpoetSendingMethodSet = ($settings[Mailer::MAILER_CONFIG_SETTING_NAME]['method'] ?? null) === Mailer::METHOD_MAILPOET;
177 if ($senderAddressSet || $mailpoetSendingMethodSet) {
178 return $this->checkAuthorizedEmailAddresses();
179 }
180 return null;
181 }
182
183 public function onNewsletterSenderAddressUpdate(NewsletterEntity $newsletter, ?string $oldSenderAddress = null) {
184 if ($newsletter->getSenderAddress() === $oldSenderAddress) {
185 return;
186 }
187 if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD && $newsletter->getStatus() === NewsletterEntity::STATUS_SCHEDULED) {
188 $this->checkAuthorizedEmailAddresses();
189 }
190 if (in_array($newsletter->getType(), $this->automaticEmailTypes, true) && $newsletter->getStatus() === NewsletterEntity::STATUS_ACTIVE) {
191 $this->checkAuthorizedEmailAddresses();
192 }
193 }
194
195 public function isSenderAddressValid(NewsletterEntity $newsletter, string $context = 'activation'): bool {
196 if (!in_array($newsletter->getType(), NewsletterEntity::CAMPAIGN_TYPES)) {
197 return true;
198 }
199
200 $isAuthorizedDomainRequired = $context === 'activation'
201 ? $this->senderDomainController->isAuthorizedDomainRequiredForNewCampaigns()
202 : $this->senderDomainController->isAuthorizedDomainRequiredForExistingCampaigns();
203
204 if (!$isAuthorizedDomainRequired) {
205 return true;
206 }
207
208 $verifiedDomains = $context === 'activation'
209 ? $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache()
210 : $this->senderDomainController->getVerifiedSenderDomains();
211
212 // The shop is not returning data, so we allow sending and let the Sending Service block the campaign if needed.
213 if ($context === 'sending' && empty($verifiedDomains) && !$this->senderDomainController->isCacheAvailable()) {
214 return true;
215 }
216
217 return $this->validateEmailDomainIsVerified($verifiedDomains, $newsletter->getSenderAddress());
218 }
219
220 private function validateAddressesInSettings($authorizedEmails, $verifiedDomains, $result = []) {
221 $defaultSenderAddress = $this->settings->get('sender.address');
222
223 if ($this->validateEmailDomainIsVerified($verifiedDomains, $defaultSenderAddress)) {
224 // allow sending from any email address in a verified domain
225 return $result;
226 }
227
228 if (!$this->validateAuthorizedEmail($authorizedEmails, $defaultSenderAddress)) {
229 $result['invalid_sender_address'] = $defaultSenderAddress;
230 }
231
232 return $result;
233 }
234
235 private function validateAddressesInScheduledAndAutomaticEmails($authorizedEmails, $verifiedDomains, $result = []) {
236 $newsletters = $this->newslettersRepository->getScheduledStandardEmailsAndActiveAutomaticEmails($this->automaticEmailTypes);
237
238 $invalidSendersInNewsletters = [];
239 foreach ($newsletters as $newsletter) {
240 if ($this->validateAuthorizedEmail($authorizedEmails, $newsletter->getSenderAddress())) {
241 continue;
242 }
243 if ($this->validateEmailDomainIsVerified($verifiedDomains, $newsletter->getSenderAddress())) {
244 // allow sending from any email address in a verified domain
245 continue;
246 }
247 $invalidSendersInNewsletters[] = [
248 'newsletter_id' => $newsletter->getId(),
249 'subject' => $newsletter->getSubject(),
250 'sender_address' => $newsletter->getSenderAddress(),
251 ];
252 }
253
254 if (!count($invalidSendersInNewsletters)) {
255 return $result;
256 }
257
258 $result['invalid_senders_in_newsletters'] = $invalidSendersInNewsletters;
259 return $result;
260 }
261
262 /**
263 * @param array|null $error
264 */
265 private function updateMailerLog(?array $error = null) {
266 if ($error) {
267 return;
268 }
269 $mailerLogError = MailerLog::getError();
270 if ($mailerLogError && $mailerLogError['operation'] === MailerError::OPERATION_AUTHORIZATION) {
271 MailerLog::resumeSending();
272 }
273 }
274
275 private function validateAuthorizedEmail($authorizedEmails = [], $email = '') {
276 $lowercaseAuthorizedEmails = array_map('strtolower', $authorizedEmails);
277 return in_array(strtolower($email), $lowercaseAuthorizedEmails, true);
278 }
279
280 private function validateEmailDomainIsVerified(array $verifiedDomains = [], string $email = ''): bool {
281 $lowercaseVerifiedDomains = array_map('strtolower', $verifiedDomains);
282 $emailDomain = Helpers::extractEmailDomain($email);
283 return in_array($emailDomain, $lowercaseVerifiedDomains, true);
284 }
285 }
286