This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1,138 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Captcha\Validator;
if (!defined('ABSPATH')) exit;
use MailPoet\Captcha\CaptchaPhrase;
use MailPoet\Captcha\CaptchaUrlFactory;
use MailPoet\Subscribers\SubscriberIPsRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions;
class CaptchaValidator {
/** @var CaptchaUrlFactory */
private $captchaUrlFactory;
/** @var CaptchaPhrase */
private $captchaPhrase;
/** @var WPFunctions */
private $wp;
/** @var SubscriberIPsRepository */
private $subscriberIPsRepository;
/** @var SubscribersRepository */
private $subscribersRepository;
public function __construct(
CaptchaUrlFactory $urlFactory,
CaptchaPhrase $captchaPhrase,
WPFunctions $wp,
SubscriberIPsRepository $subscriberIPsRepository,
SubscribersRepository $subscribersRepository
) {
$this->captchaUrlFactory = $urlFactory;
$this->captchaPhrase = $captchaPhrase;
$this->wp = $wp;
$this->subscriberIPsRepository = $subscriberIPsRepository;
$this->subscribersRepository = $subscribersRepository;
}
public function validate(array $data): bool {
$isBuiltinCaptchaRequired = $this->isRequired(isset($data['email']) ? $data['email'] : null);
if (!$isBuiltinCaptchaRequired) {
return true;
}
// session ID must be set at this point
$sessionId = $data['captcha_session_id'] ?? null;
if (!$sessionId) {
throw new ValidationError(__('CAPTCHA verification failed.', 'mailpoet'));
}
if (empty($data['captcha'])) {
throw new ValidationError(
__('Please fill in the CAPTCHA.', 'mailpoet'),
[
'redirect_url' => $this->captchaUrlFactory->getCaptchaUrlForMPForm($sessionId),
]
);
}
$captchaHash = $this->captchaPhrase->getPhrase($sessionId);
if (empty($captchaHash)) {
throw new ValidationError(
__('Please regenerate the CAPTCHA.', 'mailpoet'),
[
'redirect_url' => $this->captchaUrlFactory->getCaptchaUrlForMPForm($sessionId),
]
);
}
if (!hash_equals(strtolower($data['captcha']), strtolower($captchaHash))) {
$this->captchaPhrase->createPhrase($sessionId);
throw new ValidationError(
__('The characters entered do not match with the previous CAPTCHA.', 'mailpoet'),
[
'refresh_captcha' => true,
]
);
}
return true;
}
public function isRequired($subscriberEmail = null) {
if ($this->isUserExemptFromCaptcha()) {
return false;
}
$subscriptionCaptchaRecipientLimit = $this->wp->applyFilters('mailpoet_subscription_captcha_recipient_limit', 0);
if ($subscriptionCaptchaRecipientLimit === 0) {
return true;
}
// Check limits per recipient if enabled
if ($subscriberEmail) {
$subscriber = $this->subscribersRepository->findOneBy(['email' => $subscriberEmail]);
if (
$subscriber && $subscriber->getConfirmationsCount() >= $subscriptionCaptchaRecipientLimit
) {
return true;
}
}
// Check limits per IP address
/** @var int|string $subscriptionCaptchaWindow */
$subscriptionCaptchaWindow = $this->wp->applyFilters('mailpoet_subscription_captcha_window', MONTH_IN_SECONDS);
$subscriberIp = Helpers::getIP();
if (empty($subscriberIp)) {
return false;
}
$subscriptionCount = $this->subscriberIPsRepository->getCountByIPAndCreatedAtAfterTimeInSeconds(
$subscriberIp,
(int)$subscriptionCaptchaWindow
);
if ($subscriptionCount > 0) {
return true;
}
return false;
}
private function isUserExemptFromCaptcha() {
if (!$this->wp->isUserLoggedIn()) {
return false;
}
$user = $this->wp->wpGetCurrentUser();
$roles = $this->wp->applyFilters('mailpoet_subscription_captcha_exclude_roles', ['administrator', 'editor']);
return !empty(array_intersect((array)$roles, $user->roles));
}
}
@@ -0,0 +1,32 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Captcha\Validator;
if (!defined('ABSPATH')) exit;
use MailPoet\Captcha\ReCaptchaValidator as Validator;
class RecaptchaValidator {
/** @var Validator */
private $validator;
public function __construct(
Validator $validator
) {
$this->validator = $validator;
}
public function validate(array $data): bool {
$token = $data['recaptchaResponseToken'] ?? '';
try {
$this->validator->validate($token);
} catch (\Exception $e) {
throw new ValidationError($e->getMessage());
}
return true;
}
}
@@ -0,0 +1,26 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
namespace MailPoet\Captcha\Validator;
if (!defined('ABSPATH')) exit;
class ValidationError extends \RuntimeException {
private $meta = [];
public function __construct(
$message = "",
array $meta = [],
$code = 0,
\Throwable $previous = null
) {
$this->meta = $meta;
$this->meta['error'] = $message;
parent::__construct($message, $code, $previous);
}
public function getMeta(): array {
return $this->meta;
}
}
@@ -0,0 +1 @@
<?php