File "API.php"
Full Path: /home/warrior1/public_html/languages/wp-content/plugins/mailpoet/lib/Services/Bridge/API.php
File size: 12.46 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace MailPoet\Services\Bridge;
if (!defined('ABSPATH')) exit;
use MailPoet\Logging\LoggerFactory;
use MailPoet\WP\Functions as WPFunctions;
use WP_Error;
class API {
const SENDING_STATUS_OK = 'ok';
const SENDING_STATUS_CONNECTION_ERROR = 'connection_error';
const SENDING_STATUS_SEND_ERROR = 'send_error';
const REQUEST_TIMEOUT = 10; // seconds
const RESPONSE_CODE_KEY_INVALID = 401;
const RESPONSE_CODE_STATS_SAVED = 204;
const RESPONSE_CODE_CREATED = 201;
const RESPONSE_CODE_INTERNAL_SERVER_ERROR = 500;
const RESPONSE_CODE_BAD_GATEWAY = 502;
const RESPONSE_CODE_TEMPORARY_UNAVAILABLE = 503;
const RESPONSE_CODE_GATEWAY_TIMEOUT = 504;
const RESPONSE_CODE_NOT_ARRAY = 422;
const RESPONSE_CODE_PAYLOAD_TOO_BIG = 413;
const RESPONSE_CODE_PAYLOAD_ERROR = 400;
const RESPONSE_CODE_CAN_NOT_SEND = 403;
private $apiKey;
private $wp;
/** @var LoggerFactory */
private $loggerFactory;
/** @var mixed|null It is an instance of \CurlHandle in PHP8 and aboove but a resource in PHP7 */
private $curlHandle = null;
public $urlMe = 'https://bridge.mailpoet.com/api/v0/me';
public $urlPremium = 'https://bridge.mailpoet.com/api/v0/premium';
public $urlMessages = 'https://bridge.mailpoet.com/api/v0/messages';
public $urlBounces = 'https://bridge.mailpoet.com/api/v0/bounces/search';
public $urlStats = 'https://bridge.mailpoet.com/api/v0/stats';
public $urlAuthorizedEmailAddresses = 'https://bridge.mailpoet.com/api/v1/authorized_email_address';
public $urlAuthorizedSenderDomains = 'https://bridge.mailpoet.com/api/v1/sender_domain';
public $urlAuthorizedSenderDomainVerification = 'https://bridge.mailpoet.com/api/v1/sender_domain_verify';
public function __construct(
$apiKey,
$wp = null
) {
$this->setKey($apiKey);
if (is_null($wp)) {
$this->wp = new WPFunctions();
} else {
$this->wp = $wp;
}
$this->loggerFactory = LoggerFactory::getInstance();
}
public function checkMSSKey() {
$result = $this->request(
$this->urlMe,
['site' => WPFunctions::get()->homeUrl()]
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
switch ($code) {
case 200:
$body = json_decode($this->wp->wpRemoteRetrieveBody($result), true);
break;
default:
$this->logKeyCheckError((int)$code, 'mss');
$body = null;
break;
}
return ['code' => $code, 'data' => $body];
}
public function checkPremiumKey() {
$result = $this->request(
$this->urlPremium,
['site' => WPFunctions::get()->homeUrl()]
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
switch ($code) {
case 200:
$body = $this->wp->wpRemoteRetrieveBody($result);
if ($body) {
$body = json_decode($body, true);
}
break;
default:
$this->logKeyCheckError((int)$code, 'premium');
$body = null;
break;
}
return ['code' => $code, 'data' => $body];
}
public function logCurlInformation($headers, $info) {
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_MSS)->info(
'requests-curl.after_request',
['headers' => $headers, 'curl_info' => $info]
);
}
public function setCurlHandle($handle) {
$this->curlHandle = $handle;
}
public function sendMessages($messageBody) {
$this->curlHandle = null;
add_action('requests-curl.before_request', [$this, 'setCurlHandle'], 10, 2);
add_action('requests-curl.after_request', [$this, 'logCurlInformation'], 10, 2);
$result = $this->request(
$this->urlMessages,
$messageBody
);
remove_action('requests-curl.after_request', [$this, 'logCurlInformation']);
remove_action('requests-curl.before_request', [$this, 'setCurlHandle']);
if (is_wp_error($result)) {
$this->logCurlError($result);
return [
'status' => self::SENDING_STATUS_CONNECTION_ERROR,
'message' => $result->get_error_message(),
];
}
$responseCode = $this->wp->wpRemoteRetrieveResponseCode($result);
if ($responseCode !== 201) {
$response = ($this->wp->wpRemoteRetrieveBody($result)) ?
$this->wp->wpRemoteRetrieveBody($result) :
$this->wp->wpRemoteRetrieveResponseMessage($result);
return [
'status' => self::SENDING_STATUS_SEND_ERROR,
'message' => $response,
'code' => $responseCode,
];
}
return ['status' => self::SENDING_STATUS_OK];
}
public function checkBounces(array $emails) {
$result = $this->request(
$this->urlBounces,
$emails
);
if ($this->wp->wpRemoteRetrieveResponseCode($result) === 200) {
return json_decode($this->wp->wpRemoteRetrieveBody($result), true);
}
return false;
}
public function updateSubscriberCount($count): bool {
$result = $this->request(
$this->urlStats,
['subscriber_count' => (int)$count],
'PUT'
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
$isSuccess = $code === self::RESPONSE_CODE_STATS_SAVED;
if (!$isSuccess) {
$logData = [
'code' => $code,
'error' => is_wp_error($result) ? $result->get_error_message() : null,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_BRIDGE)->error('Stats API call failed.', $logData);
}
return $isSuccess;
}
public function getAuthorizedEmailAddresses(): ?array {
$result = $this->request(
$this->urlAuthorizedEmailAddresses,
null,
'GET'
);
if ($this->wp->wpRemoteRetrieveResponseCode($result) !== 200) {
return null;
}
$data = json_decode($this->wp->wpRemoteRetrieveBody($result), true);
return is_array($data) ? $data : null;
}
/**
* Create Authorized Email Address
*
* returns ['status' => true] if done or an array of error messages ['error' => $errorBody, 'status' => false]
* @param string $emailAddress
* @return array
*/
public function createAuthorizedEmailAddress(string $emailAddress): array {
$body = ['email' => $emailAddress];
$result = $this->request(
$this->urlAuthorizedEmailAddresses,
$body
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
$isSuccess = $code === self::RESPONSE_CODE_CREATED;
if (!$isSuccess) {
$errorBody = $this->wp->wpRemoteRetrieveBody($result);
$logData = [
'code' => $code,
'error' => is_wp_error($result) ? $result->get_error_message() : $errorBody,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_BRIDGE)->error('CreateAuthorizedEmailAddress API call failed.', $logData);
$errorResponseData = json_decode($errorBody, true);
// translators: %d is the error code.
$fallbackError = sprintf(__('An error has happened while performing a request, the server has responded with response code %d', 'mailpoet'), $code);
$errorData = is_array($errorResponseData) && isset($errorResponseData['error']) ? $errorResponseData['error'] : $fallbackError;
return ['error' => $errorData, 'status' => false];
}
return ['status' => $isSuccess];
}
/**
* Get a list of sender domains
* Fetched from API
* @see https://github.com/mailpoet/services-bridge#sender-domains
*/
public function getAuthorizedSenderDomains(): ?array {
$result = $this->request(
$this->urlAuthorizedSenderDomains,
null,
'GET'
);
if ($this->wp->wpRemoteRetrieveResponseCode($result) !== 200) {
return null;
}
$rawData = $this->wp->wpRemoteRetrieveBody($result);
$data = json_decode($rawData, true);
if (!is_array($data)) {
$this->logInvalidDataFormat('getAuthorizedSenderDomains', $rawData);
return null;
}
return $data;
}
/**
* Create Sender domain record
* Done via API
* Returns same response se sender_domain_verify @see https://github.com/mailpoet/services-bridge#verify-a-sender-domain
*/
public function createAuthorizedSenderDomain(string $domain): array {
$body = ['domain' => strtolower($domain)];
$result = $this->request(
$this->urlAuthorizedSenderDomains,
$body
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
$rawResponseBody = $this->wp->wpRemoteRetrieveBody($result);
$responseBody = json_decode($rawResponseBody, true);
$isSuccess = $code === self::RESPONSE_CODE_CREATED;
if (!$isSuccess) {
$logData = [
'code' => $code,
'error' => is_wp_error($result) ? $result->get_error_message() : $rawResponseBody,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_BRIDGE)->error('createAuthorizedSenderDomain API call failed.', $logData);
// translators: %d will be replaced by an error code
$fallbackError = sprintf(__('An error has happened while performing a request, the server has responded with response code %d', 'mailpoet'), $code);
$errorData = is_array($responseBody) && isset($responseBody['error']) ? $responseBody['error'] : $fallbackError;
return ['error' => $errorData, 'status' => false];
}
if (!is_array($responseBody)) {
$this->logInvalidDataFormat('createAuthorizedSenderDomain', $rawResponseBody);
return [];
}
return $responseBody;
}
/**
* Verify Sender Domain records
* returns an Array of DNS response or an array of error
* @see https://github.com/mailpoet/services-bridge#verify-a-sender-domain
*/
public function verifyAuthorizedSenderDomain(string $domain): array {
$url = $this->urlAuthorizedSenderDomainVerification . '/' . urlencode(strtolower($domain));
$result = $this->request(
$url,
null
);
$code = $this->wp->wpRemoteRetrieveResponseCode($result);
$rawResponseBody = $this->wp->wpRemoteRetrieveBody($result);
$responseBody = json_decode($rawResponseBody, true);
$isSuccess = $code === 200;
if (!$isSuccess) {
if ($code === 400) {
// we need to return the body as it is
return is_array($responseBody) ? $responseBody : [];
}
$logData = [
'code' => $code,
'error' => is_wp_error($result) ? $result->get_error_message() : $rawResponseBody,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_BRIDGE)->error('verifyAuthorizedSenderDomain API call failed.', $logData);
// translators: %d will be replaced by an error code
$fallbackError = sprintf(__('An error has happened while performing a request, the server has responded with response code %d', 'mailpoet'), $code);
$errorData = is_array($responseBody) && isset($responseBody['error']) ? $responseBody['error'] : $fallbackError;
return ['error' => $errorData, 'status' => false];
}
if (!is_array($responseBody)) {
$this->logInvalidDataFormat('verifyAuthorizedSenderDomain', $rawResponseBody);
return [];
}
return $responseBody;
}
public function setKey($apiKey) {
$this->apiKey = $apiKey;
}
public function getKey() {
return $this->apiKey;
}
private function auth() {
return 'Basic ' . base64_encode('api:' . $this->apiKey);
}
private function request($url, $body, $method = 'POST') {
$params = [
'timeout' => $this->wp->applyFilters('mailpoet_bridge_api_request_timeout', self::REQUEST_TIMEOUT),
'httpversion' => '1.0',
'method' => $method,
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => $this->auth(),
],
'body' => $body !== null ? json_encode($body) : null,
];
return $this->wp->wpRemotePost($url, $params);
}
private function logCurlError(WP_Error $error) {
$logData = [
'curl_errno' => $this->curlHandle ? curl_errno($this->curlHandle) : 'n/a',
'curl_error' => $this->curlHandle ? curl_error($this->curlHandle) : $error->get_error_message(),
'curl_info' => $this->curlHandle ? curl_getinfo($this->curlHandle) : 'n/a',
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_MSS)->error('requests-curl.failed', $logData);
}
private function logKeyCheckError(int $code, string $keyType): void {
$logData = [
'http_code' => $code,
'home_url' => $this->wp->homeUrl(),
'key_type' => $keyType,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_MSS)->error('key-validation.failed', $logData);
}
private function logInvalidDataFormat(string $method, ?string $response = null): void {
$logData = [
'code' => json_last_error(),
'response' => $response,
];
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_BRIDGE)->error($method . ' API response was not in expected format.', $logData);
}
}