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 / AuthorizedSenderDomainController.php
mailpoet / lib / Services Last commit date
Bridge 4 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
AuthorizedSenderDomainController.php
342 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Services;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Mailer\Mailer;
9 use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
10 use MailPoet\Services\Bridge\API;
11 use MailPoet\Settings\SettingsController;
12 use MailPoet\Util\License\Features\Subscribers;
13 use MailPoet\WP\Functions as WPFunctions;
14
15 class AuthorizedSenderDomainController {
16 const DOMAIN_STATUS_VERIFIED = 'verified';
17 const DOMAIN_STATUS_PARTIALLY_VERIFIED = 'partially-verified';
18 const DOMAIN_STATUS_UNVERIFIED = 'unverified';
19
20 const AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_CREATED = 'Sender domain exist';
21 const AUTHORIZED_SENDER_DOMAIN_ERROR_NOT_CREATED = 'Sender domain does not exist';
22 const AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_VERIFIED = 'Sender domain already verified';
23
24 const LOWER_LIMIT = 100;
25 const UPPER_LIMIT = 200;
26
27 const INSTALLED_AFTER_NEW_RESTRICTIONS_OPTION = 'installed_after_new_domain_restrictions';
28
29 const SENDER_DOMAINS_KEY = 'mailpoet_sender_domains';
30
31 /** @var Bridge */
32 private $bridge;
33
34 /** @var NewsletterStatisticsRepository */
35 private $newsletterStatisticsRepository;
36
37 /** @var SettingsController */
38 private $settingsController;
39
40 /** @var null|array Cached response for with authorized domains */
41 private $currentRecords = null;
42
43 /** @var null|array */
44 private $currentRawData = null;
45
46 /** @var Subscribers */
47 private $subscribers;
48
49 /** @var WPFunctions */
50 private $wp;
51
52 public function __construct(
53 Bridge $bridge,
54 NewsletterStatisticsRepository $newsletterStatisticsRepository,
55 SettingsController $settingsController,
56 Subscribers $subscribers,
57 WPFunctions $wp
58 ) {
59 $this->bridge = $bridge;
60 $this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
61 $this->settingsController = $settingsController;
62 $this->subscribers = $subscribers;
63 $this->wp = $wp;
64 }
65
66 /**
67 * Get record of Bridge::getAuthorizedSenderDomains
68 */
69 public function getDomainRecords(string $domain = ''): array {
70 $records = $this->getAllRecords();
71 if ($domain) {
72 return $records[$domain] ?? [];
73 }
74 return $records;
75 }
76
77 /**
78 * Get all Authorized Sender Domains
79 *
80 * Note: This includes both verified and unverified domains
81 */
82 public function getAllSenderDomains(): array {
83 return $this->returnAllDomains($this->getAllRecords());
84 }
85
86 /**
87 * Get all Verified Sender Domains.
88 *
89 * Note: This includes partially or fully verified domains.
90 */
91 public function getVerifiedSenderDomains(): array {
92 return $this->getFullyOrPartiallyVerifiedSenderDomains(true);
93 }
94
95 public function getVerifiedSenderDomainsIgnoringCache(): array {
96 $this->reloadCache();
97 return $this->getFullyOrPartiallyVerifiedSenderDomains(true);
98 }
99
100 /**
101 * Create new Sender Domain
102 *
103 * Throws an InvalidArgumentException if domain already exist
104 *
105 * returns an Array of DNS response or array of error
106 */
107 public function createAuthorizedSenderDomain(string $domain): array {
108 $allDomains = $this->getAllSenderDomains();
109
110 $alreadyExist = in_array($domain, $allDomains);
111
112 if ($alreadyExist) {
113 // sender domain already created. skip making new request
114 throw new \InvalidArgumentException(self::AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_CREATED);
115 }
116
117 $response = $this->bridge->createAuthorizedSenderDomain($domain);
118
119 if (isset($response['status']) && $response['status'] === API::RESPONSE_STATUS_ERROR) {
120 throw new \InvalidArgumentException($response['message']);
121 }
122
123 // Reset cached value since a new domain was added
124 $this->currentRecords = null;
125 $this->reloadCache();
126
127 return $response;
128 }
129
130 public function getRewrittenEmailAddress(string $email): string {
131 return sprintf('%s@replies.sendingservice.net', str_replace('@', '=', $email));
132 }
133
134 /**
135 * Verify Sender Domain
136 *
137 * Throws an InvalidArgumentException if domain does not exist or domain is already verified
138 *
139 * * returns [ok: bool, dns: array] if domain verification is successful
140 * * or [ok: bool, error: string, dns: array] if domain verification failed
141 * * or [error: string, status: bool] for other errors
142 */
143 public function verifyAuthorizedSenderDomain(string $domain): array {
144 $records = $this->bridge->getAuthorizedSenderDomains();
145
146 $allDomains = $this->returnAllDomains($records);
147 $alreadyExist = in_array($domain, $allDomains);
148
149 if (!$alreadyExist) {
150 // can't verify a domain that does not exist
151 throw new \InvalidArgumentException(self::AUTHORIZED_SENDER_DOMAIN_ERROR_NOT_CREATED);
152 }
153
154 $verifiedDomains = $this->getFullyVerifiedSenderDomains(true);
155 $alreadyVerified = in_array($domain, $verifiedDomains);
156
157 if ($alreadyVerified) {
158 // no need to reverify an already verified domain
159 throw new \InvalidArgumentException(self::AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_VERIFIED);
160 }
161
162 $response = $this->bridge->verifyAuthorizedSenderDomain($domain);
163
164 // API response contains status, but we need to check that dns array is not included
165 if ($response['status'] === API::RESPONSE_STATUS_ERROR && !isset($response['dns'])) {
166 throw new \InvalidArgumentException($response['message']);
167 }
168
169 $this->currentRecords = null;
170 $this->reloadCache();
171
172 return $response;
173 }
174
175 public function getSenderDomainsByStatus(array $status): array {
176 return array_filter($this->getAllRawData(), function(array $senderDomainData) use ($status) {
177 return in_array($senderDomainData['domain_status'] ?? null, $status);
178 });
179 }
180
181 /**
182 * Returns sender domains that have all required records, including DMARC.
183 */
184 public function getFullyVerifiedSenderDomains($domainsOnly = false): array {
185 $domainData = $this->getSenderDomainsByStatus([self::DOMAIN_STATUS_VERIFIED]);
186 return $domainsOnly ? $this->extractDomains($domainData) : $domainData;
187 }
188
189 /**
190 * Returns sender domains that were verified before DMARC record was required.
191 */
192 public function getPartiallyVerifiedSenderDomains($domainsOnly = false): array {
193 $domainData = $this->getSenderDomainsByStatus([self::DOMAIN_STATUS_PARTIALLY_VERIFIED]);
194 return $domainsOnly ? $this->extractDomains($domainData) : $domainData;
195 }
196
197 public function getUnverifiedSenderDomains($domainsOnly = false): array {
198 $domainData = $this->getSenderDomainsByStatus([self::DOMAIN_STATUS_UNVERIFIED]);
199 return $domainsOnly ? $this->extractDomains($domainData) : $domainData;
200 }
201
202 public function getFullyOrPartiallyVerifiedSenderDomains($domainsOnly = false): array {
203 $domainData = $this->getSenderDomainsByStatus([self::DOMAIN_STATUS_PARTIALLY_VERIFIED, self::DOMAIN_STATUS_VERIFIED]);
204 return $domainsOnly ? $this->extractDomains($domainData) : $domainData;
205 }
206
207 private function extractDomains(array $domainData): array {
208 $extractedDomains = [];
209 foreach ($domainData as $data) {
210 $extractedDomains[] = $this->domainExtractor($data);
211 }
212 return $extractedDomains;
213 }
214
215 private function domainExtractor(array $domainData): string {
216 return $domainData['domain'] ?? '';
217 }
218
219 public function getSenderDomainsGroupedByStatus(): array {
220 $groupedDomains = [];
221 foreach ($this->getAllRawData() as $senderDomainData) {
222 $status = $senderDomainData['domain_status'] ?? 'unknown';
223 if (!isset($groupedDomains[$status])) {
224 $groupedDomains[$status] = [];
225 }
226 $groupedDomains[$status][] = $senderDomainData;
227 }
228 return $groupedDomains;
229 }
230
231 /**
232 * Little helper function to return All Domains. alias to `array_keys`
233 *
234 * The domain is the key returned from the Bridge::getAuthorizedSenderDomains
235 */
236 private function returnAllDomains(array $records): array {
237 $domains = array_keys($records);
238 return $domains;
239 }
240
241 private function reloadCache() {
242 $currentRawData = $this->bridge->getRawSenderDomainData();
243 if (!$currentRawData) return; // Do not modify cache if there is no data from the API
244
245 $this->currentRawData = $currentRawData;
246 $this->wp->setTransient(self::SENDER_DOMAINS_KEY, $this->currentRawData, 60 * 60 * 24 * 7);
247 }
248
249 public function isCacheAvailable(): bool {
250 return is_array($this->wp->getTransient(self::SENDER_DOMAINS_KEY));
251 }
252
253 private function getAllRawData(): array {
254 if ($this->currentRawData === null) {
255 $currentData = $this->wp->getTransient(self::SENDER_DOMAINS_KEY);
256 if (is_array($currentData)) {
257 $this->currentRawData = $currentData;
258 } else {
259 $this->reloadCache();
260 }
261 }
262 return is_array($this->currentRawData) ? $this->currentRawData : [];
263 }
264
265 private function getAllRecords(): array {
266 if ($this->currentRecords === null) {
267 $this->currentRecords = $this->bridge->getAuthorizedSenderDomains();
268 }
269 return $this->currentRecords;
270 }
271
272 public function isNewUser(): bool {
273 $installedVersion = $this->settingsController->get('version');
274
275 // Setup wizard has not been completed
276 if ($installedVersion === null) {
277 return true;
278 }
279
280 $installedAfterNewDomainRestrictions = $this->settingsController->get(self::INSTALLED_AFTER_NEW_RESTRICTIONS_OPTION, false);
281
282 if ($installedAfterNewDomainRestrictions) {
283 return true;
284 }
285
286 return $this->newsletterStatisticsRepository->countBy([]) === 0;
287 }
288
289 public function isSmallSender(): bool {
290 return $this->subscribers->getSubscribersCount() <= self::LOWER_LIMIT;
291 }
292
293 public function isBigSender(): bool {
294 return $this->subscribers->getSubscribersCount() > self::UPPER_LIMIT;
295 }
296
297 public function isBundledSubscription(): bool {
298 return $this->settingsController->get(Bridge::SUBSCRIPTION_TYPE_SETTING_NAME) === Bridge::WPCOM_BUNDLE_SUBSCRIPTION_TYPE;
299 }
300
301 /**
302 * Bundled subscription users who are small senders
303 * can skip email/domain authorization requirements.
304 */
305 public function shouldSkipAuthorization(): bool {
306 return $this->isBundledSubscription() && $this->isSmallSender();
307 }
308
309 public function isAuthorizedDomainRequiredForNewCampaigns(): bool {
310 if ($this->shouldSkipAuthorization()) {
311 return false;
312 }
313 return $this->settingsController->get('mta.method') === Mailer::METHOD_MAILPOET && !$this->isSmallSender();
314 }
315
316 public function isAuthorizedDomainRequiredForExistingCampaigns(): bool {
317 if ($this->shouldSkipAuthorization()) {
318 return false;
319 }
320 return $this->settingsController->get('mta.method') === Mailer::METHOD_MAILPOET && $this->isBigSender();
321 }
322
323 public function getContextData(): array {
324 return [
325 'verifiedSenderDomains' => $this->getFullyVerifiedSenderDomains(true),
326 'partiallyVerifiedSenderDomains' => $this->getPartiallyVerifiedSenderDomains(true),
327 'allSenderDomains' => $this->getAllSenderDomains(),
328 'senderRestrictions' => [
329 'lowerLimit' => self::LOWER_LIMIT,
330 'alwaysRewrite' => false,
331 'skipAuthorization' => $this->shouldSkipAuthorization(),
332 ],
333 ];
334 }
335
336 public function getContextDataForAutomations(): array {
337 $data = $this->getContextData();
338 $data['senderRestrictions']['alwaysRewrite'] = true;
339 return $data;
340 }
341 }
342