From b372131b3e1ebd5454e970ca187fb8df461c61e3 Mon Sep 17 00:00:00 2001 From: Oleksandr Mykhailenko Date: Wed, 24 Aug 2022 16:31:50 +0300 Subject: [PATCH] Plugin refactoring. Small fixes. Code style --- .gitignore | 3 +- includes/admin.php | 116 ++++---- includes/wp-mail-api.php | 622 +++++++++++++++++++-------------------- mailgun.php | 2 +- readme.md | 5 +- readme.txt | 5 +- 6 files changed, 374 insertions(+), 379 deletions(-) diff --git a/.gitignore b/.gitignore index 7d8cc4e..8ddae45 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ svn/ .idea/ -docker-compose.yaml \ No newline at end of file +docker-compose.yaml +.git diff --git a/includes/admin.php b/includes/admin.php index aa90016..76df5bc 100755 --- a/includes/admin.php +++ b/includes/admin.php @@ -26,6 +26,13 @@ class MailgunAdmin extends Mailgun */ private $defaults; + /** + * @var array + */ + protected $options = []; + + protected $hook_suffix; + /** * Setup backend functionality in WordPress. * @@ -63,9 +70,9 @@ public function __construct() public function init() { $sitename = strtolower($_SERVER['SERVER_NAME']); - if (substr($sitename, 0, 4) == 'www.'): + if (substr($sitename, 0, 4) === 'www.') { $sitename = substr($sitename, 4); - endif; + } $region = (defined('MAILGUN_REGION') && MAILGUN_REGION) ? MAILGUN_REGION : $this->get_option('region'); $regionDefault = $region ?: 'us'; @@ -85,10 +92,10 @@ public function init() 'override-from' => '0', 'tag' => $sitename, ); - if (!$this->options): + if (!$this->options) { $this->options = $this->defaults; add_option('mailgun', $this->options); - endif; + } } /** @@ -100,7 +107,7 @@ public function init() */ public function admin_menu() { - if (current_user_can('manage_options')): + if (current_user_can('manage_options')) { $this->hook_suffix = add_options_page(__('Mailgun', 'mailgun'), __('Mailgun', 'mailgun'), 'manage_options', 'mailgun', array(&$this, 'options_page')); add_options_page(__('Mailgun Lists', 'mailgun'), __('Mailgun Lists', 'mailgun'), 'manage_options', @@ -108,7 +115,7 @@ public function admin_menu() add_action("admin_print_scripts-{$this->hook_suffix}", array(&$this, 'admin_js')); add_filter("plugin_action_links_{$this->plugin_basename}", array(&$this, 'filter_plugin_actions')); add_action("admin_footer-{$this->hook_suffix}", array(&$this, 'admin_footer_js')); - endif; + } } /** @@ -198,10 +205,10 @@ public function admin_footer_js() */ public function options_page() { - if (!@include 'options-page.php'): + if (!@include 'options-page.php') { printf(__('

The options page for the Mailgun plugin cannot be displayed. The file %s is missing. Please reinstall the plugin.

', - 'mailgun'), dirname(__FILE__) . '/options-page.php'); - endif; + 'mailgun'), __DIR__ . '/options-page.php'); + } } /** @@ -213,10 +220,10 @@ public function options_page() */ public function lists_page() { - if (!@include 'lists-page.php'): + if (!@include 'lists-page.php') { printf(__('

The lists page for the Mailgun plugin cannot be displayed. The file %s is missing. Please reinstall the plugin.

', - 'mailgun'), dirname(__FILE__) . '/lists-page.php'); - endif; + 'mailgun'), __DIR__ . '/lists-page.php'); + } } /** @@ -260,7 +267,7 @@ public function register_settings() * * @since 0.1 */ - public function validation($options) + public function validation(array $options) { $apiKey = trim($options['apiKey']); $username = trim($options['username']); @@ -384,6 +391,7 @@ public function filter_plugin_actions($links) * * @return string * + * @throws JsonException * @since 0.1 */ public function ajax_send_test() @@ -406,26 +414,25 @@ public function ajax_send_test() $secure = (defined('MAILGUN_SECURE') && MAILGUN_SECURE) ? MAILGUN_SECURE : $this->get_option('secure'); $sectype = (defined('MAILGUN_SECTYPE') && MAILGUN_SECTYPE) ? MAILGUN_SECTYPE : $this->get_option('sectype'); - if ((bool)!$getRegion): + if (!$getRegion) { mg_api_last_error(__("Region has not been selected", "mailgun")); - else: - if ($getRegion === 'us'): + } else { + if ($getRegion === 'us') { $region = __("U.S./North America", "mailgun"); - endif; - - if ($getRegion === "eu"): + } + if ($getRegion === "eu") { $region = __("Europe", "mailgun"); - endif; - endif; + } + } - if ((bool)$useAPI): + if ($useAPI) { $method = __('HTTP API', 'mailgun'); - else: - $method = ((bool)$secure) ? __('Secure SMTP', 'mailgun') : __('Insecure SMTP', 'mailgun'); - if ((bool)$secure): - $method = $method . sprintf(__(' via %s', 'mailgun'), $sectype); - endif; - endif; + } else { + $method = ($secure) ? __('Secure SMTP', 'mailgun') : __('Insecure SMTP', 'mailgun'); + if ($secure) { + $method .= sprintf(__(' via %s', 'mailgun'), $sectype); + } + } $admin_email = get_option('admin_email'); $result = wp_mail( @@ -433,54 +440,51 @@ public function ajax_send_test() __('Mailgun WordPress Plugin Test', 'mailgun'), sprintf(__("This is a test email generated by the Mailgun WordPress plugin.\n\nIf you have received this message, the requested test has succeeded.\n\nThe sending region is set to %s.\n\nThe method used to send this email was: %s.", 'mailgun'), $region, $method), - array('Content-Type: text/plain') + ['Content-Type: text/plain'] ); - if ((bool)$useAPI): - if (!function_exists('mg_api_last_error')): - if (!include __DIR__ . '/wp-mail-api.php'): + if ($useAPI) { + if (!function_exists('mg_api_last_error')) { + if (!include __DIR__ . '/wp-mail-api.php') { $this->deactivate_and_die(__DIR__ . '/wp-mail-api.php'); - endif; - endif; - + } + } $error_msg = mg_api_last_error(); - else: - if (!function_exists('mg_smtp_last_error')): - if (!include __DIR__ . '/wp-mail-smtp.php'): + } else { + if (!function_exists('mg_smtp_last_error')) { + if (!include __DIR__ . '/wp-mail-smtp.php') { $this->deactivate_and_die(__DIR__ . '/wp-mail-smtp.php'); - endif; - endif; - + } + } $error_msg = mg_smtp_last_error(); - endif; + } // Admin Email is used as 'to' parameter, but in case of 'Test Configuration' this message is not clear for the user, so replaced with more appropriate one - if (false !== strpos($error_msg, "'to'") && false !== strpos($error_msg, 'is not a valid')): + if (false !== strpos($error_msg, "'to'") && false !== strpos($error_msg, 'is not a valid')) { $error_msg = sprintf( "Administration Email Address (%s) is not valid and can't be used for test, you can change it at General Setting page", $admin_email ); - endif; + } - if ($result): + if ($result) { die( - json_encode(array( - 'message' => __('Success', 'mailgun'), - 'method' => $method, - 'error' => __('Success', 'mailgun'), - ), JSON_THROW_ON_ERROR) + json_encode(array( + 'message' => __('Success', 'mailgun'), + 'method' => $method, + 'error' => __('Success', 'mailgun'), + ), JSON_THROW_ON_ERROR) ); - else: - // Error message will always be returned in case of failure, if not - connection wasn't successful - $error_msg = $error_msg ? $error_msg : "Can't connect to Mailgun"; + } - die( + // Error message will always be returned in case of failure, if not - connection wasn't successful + $error_msg = $error_msg ? $error_msg : "Can't connect to Mailgun"; + die( json_encode(array( 'message' => __('Failure', 'mailgun'), 'method' => $method, 'error' => $error_msg, ), JSON_THROW_ON_ERROR) - ); - endif; + ); } } diff --git a/includes/wp-mail-api.php b/includes/wp-mail-api.php index fa88f3a..ac35413 100644 --- a/includes/wp-mail-api.php +++ b/includes/wp-mail-api.php @@ -20,19 +20,19 @@ */ // Include MG filter functions -if (!include dirname(__FILE__).'/mg-filter.php') { - Mailgun::deactivate_and_die(dirname(__FILE__).'/mg-filter.php'); +if (!include __DIR__ . '/mg-filter.php') { + (new Mailgun)->deactivate_and_die(__DIR__ . '/mg-filter.php'); } /** * mg_api_last_error is a compound getter/setter for the last error that was * encountered during a Mailgun API call. * - * @param string $error OPTIONAL + * @param string $error OPTIONAL * - * @return string Last error that occurred. + * @return string Last error that occurred. * - * @since 1.5.0 + * @since 1.5.0 */ function mg_api_last_error($error = null) { @@ -40,12 +40,12 @@ function mg_api_last_error($error = null) if (null === $error) { return $last_error; - } else { - $tmp = $last_error; - $last_error = $error; - - return $tmp; } + + $tmp = $last_error; + $last_error = $error; + + return $tmp; } /* @@ -79,14 +79,14 @@ function mg_mutate_to_rcpt_vars_cb($to_addrs) // TODO: Also add folding to prevent hitting the 998 char limit on headers. return array( - 'to' => '%recipient%', + 'to' => '%recipient%', 'rcpt_vars' => json_encode($rcpt_vars), ); } } return array( - 'to' => $to_addrs, + 'to' => $to_addrs, 'rcpt_vars' => null, ); } @@ -98,365 +98,351 @@ function mg_mutate_to_rcpt_vars_cb($to_addrs) * Based off of the core wp_mail function, but with modifications required to * send email using the Mailgun HTTP API * - * @param string|array $to Array or comma-separated list of email addresses to send message. - * @param string $subject Email subject - * @param string $message Message contents - * @param string|array $headers Optional. Additional headers. - * @param string|array $attachments Optional. Files to attach. + * @param string|array $to Array or comma-separated list of email addresses to send message. + * @param string $subject Email subject + * @param string $message Message contents + * @param string|array $headers Optional. Additional headers. + * @param string|array $attachments Optional. Files to attach. * - * @return bool Whether the email contents were sent successfully. + * @return bool Whether the email contents were sent successfully. * * @global PHPMailer\PHPMailer\PHPMailer $phpmailer * - * @since 0.1 + * @since 0.1 */ if (!function_exists('wp_mail')) { -function wp_mail($to, $subject, $message, $headers = '', $attachments = array()) -{ - // Compact the input, apply the filters, and extract them back out - extract(apply_filters('wp_mail', compact('to', 'subject', 'message', 'headers', 'attachments'))); - - $mailgun = get_option('mailgun'); - $region = (defined('MAILGUN_REGION') && MAILGUN_REGION) ? MAILGUN_REGION : $mailgun['region']; - $apiKey = (defined('MAILGUN_APIKEY') && MAILGUN_APIKEY) ? MAILGUN_APIKEY : $mailgun['apiKey']; - $domain = (defined('MAILGUN_DOMAIN') && MAILGUN_DOMAIN) ? MAILGUN_DOMAIN : $mailgun['domain']; - - if (empty($apiKey) || empty($domain)) { - return false; - } + function wp_mail($to, $subject, $message, $headers = '', $attachments = array()) + { + // Compact the input, apply the filters, and extract them back out + extract(apply_filters('wp_mail', compact('to', 'subject', 'message', 'headers', 'attachments'))); + + $mailgun = get_option('mailgun'); + $region = (defined('MAILGUN_REGION') && MAILGUN_REGION) ? MAILGUN_REGION : $mailgun['region']; + $apiKey = (defined('MAILGUN_APIKEY') && MAILGUN_APIKEY) ? MAILGUN_APIKEY : $mailgun['apiKey']; + $domain = (defined('MAILGUN_DOMAIN') && MAILGUN_DOMAIN) ? MAILGUN_DOMAIN : $mailgun['domain']; + + if (empty($apiKey) || empty($domain)) { + return false; + } - // If a region is not set via defines or through the options page, default to US region. - if (!((bool) $region)) { - error_log('[Mailgun] No region configuration was found! Defaulting to US region.'); - $region = 'us'; - } + // If a region is not set via defines or through the options page, default to US region. + if (!((bool)$region)) { + error_log('[Mailgun] No region configuration was found! Defaulting to US region.'); + $region = 'us'; + } - if (!is_array($attachments)) { - $attachments = explode("\n", str_replace("\r\n", "\n", $attachments)); - } + if (!is_array($attachments)) { + $attachments = explode("\n", str_replace("\r\n", "\n", $attachments)); + } - // Headers - if (empty($headers)) { - $headers = array(); - } else { - if (!is_array($headers)) { - // Explode the headers out, so this function can take both - // string headers and an array of headers. - $tempheaders = explode("\n", str_replace("\r\n", "\n", $headers)); + // Headers + if (empty($headers)) { + $headers = []; } else { - $tempheaders = $headers; - } - $headers = array(); - $cc = array(); - $bcc = array(); - - // If it's actually got contents - if (!empty($tempheaders)) { - // Iterate through the raw headers - foreach ((array) $tempheaders as $header) { - if (strpos($header, ':') === false) { - if (false !== stripos($header, 'boundary=')) { - $parts = preg_split('/boundary=/i', trim($header)); - $boundary = trim(str_replace(array("'", '"'), '', $parts[1])); - } - continue; - } - // Explode them out - list($name, $content) = explode(':', trim($header), 2); - - // Cleanup crew - $name = trim($name); - $content = trim($content); - - switch (strtolower($name)) { - // Mainly for legacy -- process a From: header if it's there - case 'from': - if (strpos($content, '<') !== false) { - // So... making my life hard again? - $from_name = substr($content, 0, strpos($content, '<') - 1); - $from_name = str_replace('"', '', $from_name); - $from_name = trim($from_name); - - $from_email = substr($content, strpos($content, '<') + 1); - $from_email = str_replace('>', '', $from_email); - $from_email = trim($from_email); - } else { - $from_email = trim($content); - } - break; - case 'content-type': - if (strpos($content, ';') !== false) { - list($type, $charset) = explode(';', $content); - $content_type = trim($type); - if (false !== stripos($charset, 'charset=')) { - $charset = trim(str_replace(array('charset=', '"'), '', $charset)); - } elseif (false !== stripos($charset, 'boundary=')) { - $boundary = trim(str_replace(array('BOUNDARY=', 'boundary=', '"'), '', $charset)); - $charset = ''; + if (!is_array($headers)) { + // Explode the headers out, so this function can take both + // string headers and an array of headers. + $tempheaders = explode("\n", str_replace("\r\n", "\n", $headers)); + } else { + $tempheaders = $headers; + } + $headers = []; + $cc = []; + $bcc = []; + + // If it's actually got contents + if (!empty($tempheaders)) { + // Iterate through the raw headers + foreach ((array)$tempheaders as $header) { + if (strpos($header, ':') === false) { + if (false !== stripos($header, 'boundary=')) { + $parts = preg_split('/boundary=/i', trim($header)); + $boundary = trim(str_replace(["'", '"'], '', $parts[1])); } - } else { - $content_type = trim($content); + continue; + } + // Explode them out + list($name, $content) = explode(':', trim($header), 2); + + // Cleanup crew + $name = trim($name); + $content = trim($content); + + switch (strtolower($name)) { + // Mainly for legacy -- process a From: header if it's there + case 'from': + if (strpos($content, '<') !== false) { + // So... making my life hard again? + $from_name = substr($content, 0, strpos($content, '<') - 1); + $from_name = str_replace('"', '', $from_name); + $from_name = trim($from_name); + + $from_email = substr($content, strpos($content, '<') + 1); + $from_email = str_replace('>', '', $from_email); + $from_email = trim($from_email); + } else { + $from_email = trim($content); + } + break; + case 'content-type': + if (strpos($content, ';') !== false) { + list($type, $charset) = explode(';', $content); + $content_type = trim($type); + if (false !== stripos($charset, 'charset=')) { + $charset = trim(str_replace(array('charset=', '"'), '', $charset)); + } elseif (false !== stripos($charset, 'boundary=')) { + $boundary = trim(str_replace(array('BOUNDARY=', 'boundary=', '"'), '', $charset)); + $charset = ''; + } + } else { + $content_type = trim($content); + } + break; + case 'cc': + $cc = array_merge((array)$cc, explode(',', $content)); + break; + case 'bcc': + $bcc = array_merge((array)$bcc, explode(',', $content)); + break; + default: + // Add it to our grand headers array + $headers[trim($name)] = trim($content); + break; } - break; - case 'cc': - $cc = array_merge((array) $cc, explode(',', $content)); - break; - case 'bcc': - $bcc = array_merge((array) $bcc, explode(',', $content)); - break; - default: - // Add it to our grand headers array - $headers[trim($name)] = trim($content); - break; } } } - } - if (!isset($from_name)) { - $from_name = null; - } - - if (!isset($from_email)) { - $from_email = null; - } + if (!isset($from_name)) { + $from_name = null; + } - $from_name = mg_detect_from_name($from_name); - $from_email = mg_detect_from_address($from_email); + if (!isset($from_email)) { + $from_email = null; + } - $body = array( - 'from' => "{$from_name} <{$from_email}>", - 'to' => $to, - 'subject' => $subject, - ); + $from_name = mg_detect_from_name($from_name); + $from_email = mg_detect_from_address($from_email); - $rcpt_data = apply_filters('mg_mutate_to_rcpt_vars', $to); - if (!is_null($rcpt_data['rcpt_vars'])) { - $body['recipient-variables'] = $rcpt_data['rcpt_vars']; - } + $body = array( + 'from' => "{$from_name} <{$from_email}>", + 'to' => $to, + 'subject' => $subject, + ); - $body['o:tag'] = array(); - $body['o:tracking-clicks'] = !empty($mailgun['track-clicks']) ? $mailgun['track-clicks'] : 'no'; - $body['o:tracking-opens'] = empty($mailgun['track-opens']) ? 'no' : 'yes'; + $rcpt_data = apply_filters('mg_mutate_to_rcpt_vars', $to); + if (!is_null($rcpt_data['rcpt_vars'])) { + $body['recipient-variables'] = $rcpt_data['rcpt_vars']; + } - // this is the wordpress site tag - if (isset($mailgun['tag'])) { - $tags = explode(',', str_replace(' ', '', $mailgun['tag'])); - $body['o:tag'] = $tags; - } + $body['o:tag'] = array(); + $body['o:tracking-clicks'] = !empty($mailgun['track-clicks']) ? $mailgun['track-clicks'] : 'no'; + $body['o:tracking-opens'] = empty($mailgun['track-opens']) ? 'no' : 'yes'; - // campaign-id now refers to a list of tags which will be appended to the site tag - if (!empty($mailgun['campaign-id'])) { - $tags = explode(',', str_replace(' ', '', $mailgun['campaign-id'])); - if (empty($body['o:tag'])) { + // this is the wordpress site tag + if (isset($mailgun['tag'])) { + $tags = explode(',', str_replace(' ', '', $mailgun['tag'])); $body['o:tag'] = $tags; - } elseif (is_array($body['o:tag'])) { - $body['o:tag'] = array_merge($body['o:tag'], $tags); - } else { - $body['o:tag'] .= ','.$tags; } - } - /** - * Filter tags. - * - * @param array $tags Mailgun tags. - * @param string $to To address. - * @param string $subject Subject line. - * @param string $message Message content. - * @param array $headers Headers array. - * @param array $attachments Attachments array. - * @param string $region Mailgun region. - * @param string $domain Mailgun domain. - * - * @return array Mailgun tags. - */ - $body['o:tag'] = apply_filters( 'mailgun_tags', $body['o:tag'], $to, $subject, $message, $headers, $attachments, $region, $domain ); - - if (!empty($cc) && is_array($cc)) { - $body['cc'] = implode(', ', $cc); - } + // campaign-id now refers to a list of tags which will be appended to the site tag + if (!empty($mailgun['campaign-id'])) { + $tags = explode(',', str_replace(' ', '', $mailgun['campaign-id'])); + if (empty($body['o:tag'])) { + $body['o:tag'] = $tags; + } elseif (is_array($body['o:tag'])) { + $body['o:tag'] = array_merge($body['o:tag'], $tags); + } else { + $body['o:tag'] .= ',' . $tags; + } + } - if (!empty($bcc) && is_array($bcc)) { - $body['bcc'] = implode(', ', $bcc); - } + /** + * Filter tags. + * + * @param array $tags Mailgun tags. + * @param string $to To address. + * @param string $subject Subject line. + * @param string $message Message content. + * @param array $headers Headers array. + * @param array $attachments Attachments array. + * @param string $region Mailgun region. + * @param string $domain Mailgun domain. + * + * @return array Mailgun tags. + */ + $body['o:tag'] = apply_filters('mailgun_tags', $body['o:tag'], $to, $subject, $message, $headers, $attachments, $region, $domain); - // If we are not given a Content-Type in the supplied headers, - // write the message body to a file and try to determine the mimetype - // using get_mime_content_type. - if (!isset($content_type)) { - $tmppath = tempnam(get_temp_dir(), 'mg'); - $tmp = fopen($tmppath, 'w+'); + if (!empty($cc) && is_array($cc)) { + $body['cc'] = implode(', ', $cc); + } - fwrite($tmp, $message); - fclose($tmp); + if (!empty($bcc) && is_array($bcc)) { + $body['bcc'] = implode(', ', $bcc); + } - $content_type = get_mime_content_type($tmppath, 'text/plain'); + // If we are not given a Content-Type in the supplied headers, + // write the message body to a file and try to determine the mimetype + // using get_mime_content_type. + if (!isset($content_type)) { + $tmppath = tempnam(get_temp_dir(), 'mg'); + $tmp = fopen($tmppath, 'w+'); - unlink($tmppath); - } + fwrite($tmp, $message); + fclose($tmp); - // Allow external content type filter to function normally - if (has_filter('wp_mail_content_type')) { - $content_type = apply_filters( - 'wp_mail_content_type', - $content_type - ); - } + $content_type = get_mime_content_type($tmppath, 'text/plain'); - if ('text/plain' === $content_type) { - $body['text'] = $message; - } else if ('text/html' === $content_type) { - $body['html'] = $message; - } else { - // Unknown Content-Type?? - error_log('[mailgun] Got unknown Content-Type: ' . $content_type); - $body['text'] = $message; - $body['html'] = $message; - } + unlink($tmppath); + } - // Some plugins, such as WooCommerce (@see WC_Email::handle_multipart()), to handle multipart/alternative with html - // and plaintext messages hooks into phpmailer_init action to override AltBody property directly in $phpmailer, - // so we should allow them to do this, and then get overridden plain text body from $phpmailer. - // Partly, this logic is taken from original wp_mail function. - if (false !== stripos($content_type, 'multipart')) { - global $phpmailer; - - // (Re)create it, if it's gone missing. - if (!($phpmailer instanceof PHPMailer\PHPMailer\PHPMailer)) { - require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php'; - require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php'; - require_once ABSPATH . WPINC . '/PHPMailer/Exception.php'; - $phpmailer = new PHPMailer\PHPMailer\PHPMailer(true); - - $phpmailer::$validator = static function ($email) { - return (bool)is_email($email); - }; + // Allow external content type filter to function normally + if (has_filter('wp_mail_content_type')) { + $content_type = apply_filters( + 'wp_mail_content_type', + $content_type + ); } - /** - * Fires after PHPMailer is initialized. - * - * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference). - */ - do_action_ref_array('phpmailer_init', array(&$phpmailer)); + if ('text/plain' === $content_type) { + $body['text'] = $message; + } else if ('text/html' === $content_type) { + $body['html'] = $message; + } else { + // Unknown Content-Type?? + error_log('[mailgun] Got unknown Content-Type: ' . $content_type); + $body['text'] = $message; + $body['html'] = $message; + } + + // Some plugins, such as WooCommerce (@see WC_Email::handle_multipart()), to handle multipart/alternative with html + // and plaintext messages hooks into phpmailer_init action to override AltBody property directly in $phpmailer, + // so we should allow them to do this, and then get overridden plain text body from $phpmailer. + // Partly, this logic is taken from original wp_mail function. + if (false !== stripos($content_type, 'multipart')) { + global $phpmailer; + + // (Re)create it, if it's gone missing. + if (!($phpmailer instanceof PHPMailer\PHPMailer\PHPMailer)) { + require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php'; + require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php'; + require_once ABSPATH . WPINC . '/PHPMailer/Exception.php'; + $phpmailer = new PHPMailer\PHPMailer\PHPMailer(true); + + $phpmailer::$validator = static function ($email) { + return (bool)is_email($email); + }; + } - $plainTextMessage = $phpmailer->AltBody; + /** + * Fires after PHPMailer is initialized. + * + * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference). + */ + do_action_ref_array('phpmailer_init', array(&$phpmailer)); - if ($plainTextMessage) { - $body['text'] = $plainTextMessage; + $plainTextMessage = $phpmailer->AltBody; + + if ($plainTextMessage) { + $body['text'] = $plainTextMessage; + } } - } - // If we don't have a charset from the input headers - if (!isset($charset)) { - $charset = get_bloginfo('charset'); - } + // If we don't have a charset from the input headers + if (!isset($charset)) { + $charset = get_bloginfo('charset'); + } - // Set the content-type and charset - $charset = apply_filters('wp_mail_charset', $charset); - if (isset($headers['Content-Type'])) { - if (!strstr($headers['Content-Type'], 'charset')) { - $headers['Content-Type'] = rtrim($headers['Content-Type'], '; ')."; charset={$charset}"; + // Set the content-type and charset + $charset = apply_filters('wp_mail_charset', $charset); + if (isset($headers['Content-Type'])) { + if (!strstr($headers['Content-Type'], 'charset')) { + $headers['Content-Type'] = rtrim($headers['Content-Type'], '; ') . "; charset={$charset}"; + } } - } - // Set custom headers - if (!empty($headers)) { - foreach ((array) $headers as $name => $content) { - $body["h:{$name}"] = $content; + // Set custom headers + if (!empty($headers)) { + foreach ((array)$headers as $name => $content) { + $body["h:{$name}"] = $content; + } } - // TODO: Can we handle this? - //if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) - // $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) ); - } + /* + * Deconstruct post array and create POST payload. + * This entire routine is because wp_remote_post does + * not support files directly. + */ - /* - * Deconstruct post array and create POST payload. - * This entire routine is because wp_remote_post does - * not support files directly. - */ + $payload = ''; - $payload = ''; + // First, generate a boundary for the multipart message. + $boundary = sha1(uniqid('', true)); - // First, generate a boundary for the multipart message. - $boundary = sha1(uniqid('', true)); + // Allow other plugins to apply body changes before creating the payload. + $body = apply_filters('mg_mutate_message_body', $body); + if (($body_payload = mg_build_payload_from_body($body, $boundary)) != null) { + $payload .= $body_payload; + } - // Allow other plugins to apply body changes before creating the payload. - $body = apply_filters('mg_mutate_message_body', $body); - if ( ($body_payload = mg_build_payload_from_body($body, $boundary)) != null ) { - $payload .= $body_payload; - } + // Allow other plugins to apply attachment changes before writing to the payload. + $attachments = apply_filters('mg_mutate_attachments', $attachments); + if (($attachment_payload = mg_build_attachments_payload($attachments, $boundary)) != null) { + $payload .= $attachment_payload; + } - // TODO: Special handling for multipart/alternative mail - // if ('multipart/alternative' === $content_type) { - // // Build payload from mime - // // error_log(sprintf('building message payload from multipart/alternative')); - // // error_log($body['message']); - // // error_log('Attachments:'); - // // foreach ($attachments as $attachment) { - // // error_log($attachment); - // // } - // } - - // Allow other plugins to apply attachment changes before writing to the payload. - $attachments = apply_filters('mg_mutate_attachments', $attachments); - if ( ($attachment_payload = mg_build_attachments_payload($attachments, $boundary)) != null ) { - $payload .= $attachment_payload; - } + $payload .= '--' . $boundary . '--'; - $payload .= '--'.$boundary.'--'; + $data = array( + 'body' => $payload, + 'headers' => array( + 'Authorization' => 'Basic ' . base64_encode("api:{$apiKey}"), + 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, + ), + ); - $data = array( - 'body' => $payload, - 'headers' => array( - 'Authorization' => 'Basic '.base64_encode("api:{$apiKey}"), - 'Content-Type' => 'multipart/form-data; boundary='.$boundary, - ), - ); + $endpoint = mg_api_get_region($region); + $endpoint = ($endpoint) ? $endpoint : 'https://api.mailgun.net/v3/'; + $url = $endpoint . "{$domain}/messages"; - $endpoint = mg_api_get_region($region); - $endpoint = ($endpoint) ? $endpoint : 'https://api.mailgun.net/v3/'; - $url = $endpoint."{$domain}/messages"; + // TODO: Mailgun only supports 1000 recipients per request, since we are + // overriding this function, let's add looping here to handle that + $response = wp_remote_post($url, $data); + if (is_wp_error($response)) { + // Store WP error in last error. + mg_api_last_error($response->get_error_message()); - // TODO: Mailgun only supports 1000 recipients per request, since we are - // overriding this function, let's add looping here to handle that - $response = wp_remote_post($url, $data); - if (is_wp_error($response)) { - // Store WP error in last error. - mg_api_last_error($response->get_error_message()); + return false; + } - return false; - } + $response_code = wp_remote_retrieve_response_code($response); + $response_body = json_decode(wp_remote_retrieve_body($response)); - $response_code = wp_remote_retrieve_response_code($response); - $response_body = json_decode(wp_remote_retrieve_body($response)); + // Mailgun API should *always* return a `message` field, even when + // $response_code != 200, so a lack of `message` indicates something + // is broken. + if ((int)$response_code != 200 || !isset($response_body->message)) { + // Store response code and HTTP response message in last error. + $response_message = wp_remote_retrieve_response_message($response); + $errmsg = "$response_code - $response_message"; + mg_api_last_error($errmsg); - // Mailgun API should *always* return a `message` field, even when - // $response_code != 200, so a lack of `message` indicates something - // is broken. - if ((int) $response_code != 200 || !isset($response_body->message)) { - // Store response code and HTTP response message in last error. - $response_message = wp_remote_retrieve_response_message($response); - $errmsg = "$response_code - $response_message"; - mg_api_last_error($errmsg); + return false; + } - return false; - } + // Not sure there is any additional checking that needs to be done here, but why not? + if ($response_body->message != 'Queued. Thank you.') { + mg_api_last_error($response_body->message); - // Not sure there is any additional checking that needs to be done here, but why not? - if ($response_body->message != 'Queued. Thank you.') { - mg_api_last_error($response_body->message); + return false; + } - return false; + return true; } - - return true; -} } -function mg_build_payload_from_body($body, $boundary) { +function mg_build_payload_from_body($body, $boundary) +{ $payload = ''; // Iterate through pre-built params and build payload: @@ -464,16 +450,16 @@ function mg_build_payload_from_body($body, $boundary) { if (is_array($value)) { $parent_key = $key; foreach ($value as $key => $value) { - $payload .= '--'.$boundary; + $payload .= '--' . $boundary; $payload .= "\r\n"; - $payload .= 'Content-Disposition: form-data; name="'.$parent_key."\"\r\n\r\n"; + $payload .= 'Content-Disposition: form-data; name="' . $parent_key . "\"\r\n\r\n"; $payload .= $value; $payload .= "\r\n"; } } else { - $payload .= '--'.$boundary; + $payload .= '--' . $boundary; $payload .= "\r\n"; - $payload .= 'Content-Disposition: form-data; name="'.$key.'"'."\r\n\r\n"; + $payload .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n"; $payload .= $value; $payload .= "\r\n"; } @@ -482,10 +468,8 @@ function mg_build_payload_from_body($body, $boundary) { return $payload; } -function mg_build_payload_from_mime($body, $boundary) { -} - -function mg_build_attachments_payload($attachments, $boundary) { +function mg_build_attachments_payload($attachments, $boundary) +{ $payload = ''; // If we have attachments, add them to the payload. @@ -493,9 +477,9 @@ function mg_build_attachments_payload($attachments, $boundary) { $i = 0; foreach ($attachments as $attachment) { if (!empty($attachment)) { - $payload .= '--'.$boundary; + $payload .= '--' . $boundary; $payload .= "\r\n"; - $payload .= 'Content-Disposition: form-data; name="attachment['.$i.']"; filename="'.basename($attachment).'"'."\r\n\r\n"; + $payload .= 'Content-Disposition: form-data; name="attachment[' . $i . ']"; filename="' . basename($attachment) . '"' . "\r\n\r\n"; $payload .= file_get_contents($attachment); $payload .= "\r\n"; $i++; diff --git a/mailgun.php b/mailgun.php index 7c498c0..f33caa0 100755 --- a/mailgun.php +++ b/mailgun.php @@ -4,7 +4,7 @@ * Plugin Name: Mailgun * Plugin URI: http://wordpress.org/extend/plugins/mailgun/ * Description: Mailgun integration for WordPress - * Version: 1.8.1 + * Version: 1.8.2 * Author: Mailgun * Author URI: http://www.mailgun.com/ * License: GPLv2 or later diff --git a/readme.md b/readme.md index 7325fea..90df28f 100755 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ Contributors: mailgun, sivel, lookahead.io, m35dev Tags: mailgun, smtp, http, api, mail, email Requires at least: 3.3 Tested up to: 6.0.1 -Stable tag: 1.8.1 +Stable tag: 1.8.2 License: GPLv2 or later @@ -131,6 +131,9 @@ MAILGUN_FROM_ADDRESS Type: string == Changelog == += 1.8.2 (2022-08-24): = +- Plugin refactoring. Small fixes + = 1.8.1 (2022-08-19): = - backward compatibility with php7.0 diff --git a/readme.txt b/readme.txt index b2d740f..adfdd4e 100755 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Contributors: mailgun, sivel, lookahead.io, m35dev Tags: mailgun, smtp, http, api, mail, email Requires at least: 3.3 Tested up to: 6.0.1 -Stable tag: 1.8.1 +Stable tag: 1.8.2 License: GPLv2 or later @@ -128,6 +128,9 @@ MAILGUN_FROM_ADDRESS Type: string == Changelog == += 1.8.2 (2022-08-24): = +- Plugin refactoring. Small fixes + = 1.8.1 (2022-08-19): = - backward compatibility with php7.0