diff --git a/CHANGELOG.md b/CHANGELOG.md index 6239786..0e8b713 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ Changelog ========= +1.9.5 (2023-06-20) +- Fix bug with sending emails + 1.9.4 (2023-06-10) - Fixed bug `Fatal error on lists page when another plugin redeclare wp_mail()` diff --git a/includes/admin.php b/includes/admin.php index 6338606..77134d4 100755 --- a/includes/admin.php +++ b/includes/admin.php @@ -427,23 +427,13 @@ public function ajax_send_test() ), JSON_THROW_ON_ERROR) ); } - if (function_exists('mg_wp_mail')) { - $result = mg_wp_mail( - $admin_email, - __('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), - ['Content-Type: text/plain'] - ); - } else { - $result = wp_mail( - $admin_email, - __('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), - ['Content-Type: text/plain'] - ); - } + $result = wp_mail( + $admin_email, + __('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), + ['Content-Type: text/plain'] + ); diff --git a/includes/wp-mail-api.php b/includes/wp-mail-api.php index 5c78402..500d5d6 100644 --- a/includes/wp-mail-api.php +++ b/includes/wp-mail-api.php @@ -459,330 +459,6 @@ function wp_mail($to, $subject, $message, $headers = '', $attachments = []) return false; } - return true; - } -} else { - add_filter('wp_mail', 'mg_wp_mail', 10, 5); - - function mg_wp_mail($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 (!($region)) { - error_log('[Mailgun] No region configuration was found! Defaulting to US region.'); - $region = 'us'; - } - - // Headers - if (empty($headers)) { - $headers = []; - } 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)); - } 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])); - } - continue; - } - // Explode them out - [$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) { - $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) { - [$type, $charset] = explode(';', $content); - $content_type = trim($type); - if (false !== stripos($charset, 'charset=')) { - $charset = trim(str_replace(['charset=', '"'], '', $charset)); - } elseif (false !== stripos($charset, 'boundary=')) { - $boundary = trim(str_replace(['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; - } - } - } - } - - if (!isset($from_name)) { - $from_name = null; - } - - if (!isset($from_email)) { - $from_email = null; - } - - $from_name = mg_detect_from_name($from_name); - $from_email = mg_detect_from_address($from_email); - $fromString = "{$from_name} <{$from_email}>"; - - $body = [ - 'from' => $fromString, - 'h:Sender' => $from_email, - 'to' => $to, - 'subject' => $subject, - ]; - - - $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['o:tag'] = []; - $body['o:tracking-clicks'] = !empty($mailgun['track-clicks']) ? $mailgun['track-clicks'] : 'no'; - $body['o:tracking-opens'] = empty($mailgun['track-opens']) ? 'no' : 'yes'; - - // this is the wordpress site tag - if (isset($mailgun['tag'])) { - $tags = explode(',', str_replace(' ', '', $mailgun['tag'])); - $body['o:tag'] = $tags; - } - - // 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'] .= ',' . implode(',', $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); - } - - if (!empty($bcc) && is_array($bcc)) { - $body['bcc'] = implode(', ', $bcc); - } - - // 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+'); - - fwrite($tmp, $message); - fclose($tmp); - - $content_type = get_mime_content_type($tmppath, 'text/plain'); - - unlink($tmppath); - } - - // 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 - ); - } - - if ('text/plain' === $content_type) { - $body['text'] = $message; - } else if ('text/html' === $content_type) { - $body['html'] = $message; - } else { - $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); - }; - } - - /** - * Fires after PHPMailer is initialized. - * - * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference). - */ - do_action_ref_array('phpmailer_init', [&$phpmailer]); - - $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'); - } - - // 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; - } - } - - /* - * Deconstruct post array and create POST payload. - * This entire routine is because wp_remote_post does - * not support files directly. - */ - - $payload = ''; - - // 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 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 . '--'; - - $data = [ - 'body' => $payload, - 'headers' => [ - '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"; - - // 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; - } - - $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); - - 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); - - return false; - } - return true; } } diff --git a/mailgun.php b/mailgun.php index 3918507..c81b426 100755 --- a/mailgun.php +++ b/mailgun.php @@ -3,7 +3,7 @@ * Plugin Name: Mailgun * Plugin URI: http://wordpress.org/extend/plugins/mailgun/ * Description: Mailgun integration for WordPress - * Version: 1.9.4 + * Version: 1.9.5 * Tested up to: 6.1 * Author: Mailgun * Author URI: http://www.mailgun.com/ @@ -81,7 +81,7 @@ public function __construct() // When using SMTP, we also need to inject a `wp_mail` filter to make "from" settings // work properly. Fixed issues with 1.5.7+ if ($this->get_option('useAPI') || (defined('MAILGUN_USEAPI') && MAILGUN_USEAPI)) { - if (!function_exists('wp_mail') || !function_exists('mg_wp_mail')) { + if (!function_exists('wp_mail')) { if (!include_once(__DIR__ . '/includes/wp-mail-api.php')) { $this->deactivate_and_die(__DIR__ . '/includes/wp-mail-api.php'); } diff --git a/readme.md b/readme.md index c47890e..4ba4512 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.1.1 -Stable tag: 1.9.4 +Stable tag: 1.9.5 Requires PHP: 5.6 License: GPLv2 or later @@ -131,6 +131,9 @@ MAILGUN_FROM_ADDRESS Type: string == Changelog == += 1.9.5 (2023-06-20): = +- Fix bug with sending emails + = 1.9.4 (2023-06-10): = - Fixed bug `Fatal error on lists page when another plugin redeclare wp_mail()` diff --git a/readme.txt b/readme.txt index fd332a2..c3ce2bb 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: 4.4 Tested up to: 6.1.1 -Stable tag: 1.9.4 +Stable tag: 1.9.5 Requires PHP: 5.6 License: GPLv2 or later @@ -129,10 +129,13 @@ MAILGUN_FROM_ADDRESS Type: string == Changelog == += 1.9.5 (2023-06-20): = +- Fix bug with sending emails + + = 1.9.4 (2023-06-10): = - Fixed bug `Fatal error on lists page when another plugin redeclare wp_mail()` - = 1.9.3 (2023-04-08) = - Fixed `Reply-to` header. It's not overridden anymore