File: /home/iddeczhh/public_html/wp-content/plugins/site-mailer/modules/mailer/classes/mail-validator.php
<?php
namespace SiteMailer\Modules\Mailer\Classes;
use SiteMailer\Modules\Suppressions\Classes\Unsubscribe_List;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* The class is responsible for email validation
*/
class Mail_Validator {
public static ?Mail_Validator $instance = null;
/**
* get_instance
* @return Mail_Validator |null
*/
public static function get_instance(): ?Mail_Validator {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Validate email object
*
* @param array $email Array of the `wp_mail()` arguments.
*
* @return array
*/
public function validate( array $email ): array {
$headers = array_key_exists( 'headers', $email ) ? $this->split_headers( $email['headers'] ) : [];
$to = $this->build_mail_list( $email['to'] );
$cc = array_key_exists( 'cc', $headers ) ? $this->build_mail_list( $headers['cc'] ) : [];
$bcc = array_key_exists( 'bcc', $headers ) ? $this->build_mail_list( $headers['bcc'] ) : [];
$unsubscribed = ! empty( $to ) ? Unsubscribe_List::get_instance()->get_unsubscribed( $to, $cc, $bcc ) : [];
$email['to'] = array_diff( $to, $unsubscribed );
$headers['cc'] = implode( ', ', array_diff( $cc, $unsubscribed ) );
$headers['bcc'] = implode( ', ', array_diff( $bcc, $unsubscribed ) );
$merged_headers = [];
foreach ( $headers as $key => $value ) {
if ( $value ) {
$merged_headers[] = $key . ': ' . $value;
}
}
$email['headers'] = $merged_headers;
return [
'email' => $email,
'to' => $to,
'cc' => $cc,
'bcc' => $bcc,
'unsubscribed' => $unsubscribed,
];
}
/**
* Validate initial email object for build log statuses list
*
* @param array $email Array of the `wp_mail()` arguments.
* @param array $unsubscribed Array of unsubscribed recipients.
*
* @return array $email Array of the `wp_mail()` arguments.
*/
public function validate_for_log_status( array $email, array $unsubscribed ): array {
$headers = array_key_exists( 'headers', $email ) ? $this->split_headers( $email['headers'] ) : [];
$to = $this->build_email_status_list( $email['to'], 'to', $unsubscribed );
$cc = array_key_exists( 'cc', $headers ) ? $this->build_email_status_list( $headers['cc'], 'cc', $unsubscribed ) : [];
$bcc = array_key_exists( 'bcc', $headers ) ? $this->build_email_status_list( $headers['bcc'], 'bcc', $unsubscribed ) : [];
return array_merge( $bcc, $cc, $to );
}
/**
* Check if headers is an object
*
* @param mixed $value
*
* @return bool
*/
public function is_object( $value ): bool {
return is_array( $value ) && ! empty( $value ) && array_keys( $value ) !== range( 0, count( $value ) - 1 );
}
/**
* Get header value
*
* @param array $headers
*
* @return array
*/
public function get_properties_value( array $headers ): array {
$object_headers = [];
foreach ( $headers as $key => $value ) {
$item = explode( ': ', $value );
if ( isset( $item[1] ) ) {
$object_headers[ strtolower( $key ) ] = $item[1];
} elseif ( strtolower( $item[0] ) !== strtolower( $key ) ) {
$object_headers[ strtolower( $key ) ] = $item[0];
}
}
return $object_headers;
}
/**
* Split headers from string to key-value array
*
* @param string|array $headers
*
* @return array
*/
public function split_headers( $headers ): array {
if ( $this->is_object( $headers ) ) {
return $this->get_properties_value( $headers );
}
$object_headers = [];
$separate_lines = is_array( $headers ) ? $headers : preg_split( '/\r?\n|\r|\n/', $headers );
if ( $separate_lines ) {
foreach ( $separate_lines as $header ) {
$item = explode( ': ', $header );
if ( ! empty( $item[0] ) && ! empty( $item[1] ) ) {
$object_headers[ strtolower( $item[0] ) ] = $item[1];
}
}
}
return $object_headers;
}
/**
* Build mail list from comma-separated string or array
*
* @param string|array|null $list
*
* @return array
*/
public function build_mail_list( $list = null ): array {
$list_values = is_object( $list ) ? get_object_vars( $list ) : $list;
$split_list = is_array( $list_values ) ? $list_values : explode( ',', $list_values );
$mail_list = [];
if ( $split_list ) {
foreach ( $split_list as $item ) {
$pattern = '/^[a-zA-Z0-9 ._\-]+ <(.+)>$/';
$trimmed = trim( $item );
// Check for 'Some Email <test@test.com>' format
$formated = preg_match( $pattern, $trimmed, $matched ) && filter_var( $matched[1], FILTER_VALIDATE_EMAIL );
if ( $formated || filter_var( $trimmed, FILTER_VALIDATE_EMAIL ) ) {
$mail_list[] = $formated ? $matched[1] : $trimmed;
}
}
}
return $mail_list;
}
/**
* Build mail list from comma-separated string or array
*
* @param string|array $list
* @param string $type
* @param array $unsubscribed *
*
* @return array
*/
public function build_email_status_list( $list, string $type, array $unsubscribed = [] ): array {
$split_list = is_array( $list ) ? $list : explode( ',', $list );
$mail_list = [];
if ( $split_list ) {
foreach ( $split_list as $item ) {
$pattern = '/^[a-zA-Z0-9 ._\-]+ <(.+)>$/';
$trimmed = trim( $item );
// Check for 'Some Email <test@test.com>' format
$formated = preg_match( $pattern, $trimmed, $matched ) && filter_var( $matched[1], FILTER_VALIDATE_EMAIL );
if ( ! ( $formated || filter_var( $trimmed, FILTER_VALIDATE_EMAIL ) ) ) {
$status = Mail_Handler::LOG_STATUSES['not_valid'];
} elseif ( in_array( $trimmed, $unsubscribed, true ) ) {
$status = Mail_Handler::LOG_STATUSES['unsubscribed'];
} else {
$status = false;
}
$mail_list[ $trimmed ] = [
'status' => $status,
'type' => $type,
];
}
}
return $mail_list;
}
}