updated plugin WP Mail SMTP version 2.2.1

This commit is contained in:
KawaiiPunk 2020-07-24 14:08:58 +00:00 committed by Gitium
parent 4b9aecd896
commit fa69c9daa6
40 changed files with 3546 additions and 2570 deletions

View File

@ -3,7 +3,7 @@ Contributors: wpforms, jaredatch, smub, slaFFik
Tags: smtp, wp mail smtp, wordpress smtp, gmail smtp, sendgrid smtp, mailgun smtp, mail, mailer, phpmailer, wp_mail, email, mailgun, sengrid, gmail, pepipost, sendinblue, wp smtp
Requires at least: 4.9
Tested up to: 5.4
Stable tag: 2.1.1
Stable tag: 2.2.1
Requires PHP: 5.5.0
The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million sites.
@ -229,6 +229,14 @@ By all means please contact us to discuss features or options you'd like to see
== Changelog ==
= 2.2.1 - 2020-07-09 =
* Added: Gmail mailer now supports aliases.
* Added: Support both old PHPMailer v5 (WordPress <=5.4) and PHPMailer v6 (WordPress >=5.5).
* Changed: Pepipost mailer is now using the native API v5 instead of the SendGrid migration API.
* Fixed: Incorrect Mailgun Domain Name option was not showing an email delivery error.
* Fixed: Empty debug errors for the Sendinblue mailer are no more.
* Fixed: Properly compare From Email option value with a correct default email address from WP core.
= 2.1.1 - 2020-06-08 =
* Changed: Remove current automatic default reply-to address and add WP filter `wp_mail_smtp_processor_set_default_reply_to` for setting default reply-to addresses.
* Changed: Improve description for several options with links to an article about how to properly use constants.

View File

@ -271,7 +271,7 @@ class Area {
* @since 1.5.0 Added new assets for new pages.
* @since 1.7.0 Added jQuery Confirm library css/js files.
*
* @param string $hook
* @param string $hook Current hook.
*/
public function enqueue_assets( $hook ) {
@ -305,9 +305,7 @@ class Area {
'title' => esc_html__( 'Heads up!', 'wp-mail-smtp' ),
'content' => wp_kses(
__( '<p>The Default (PHP) mailer is currently selected, but is not recommended because in most cases it does not resolve email delivery issues.</p><p>Please consider selecting and configuring one of the other mailers.</p>', 'wp-mail-smtp' ),
array(
'p' => true,
)
[ 'p' => [] ]
),
'save_button' => esc_html__( 'Save Settings', 'wp-mail-smtp' ),
'cancel_button' => esc_html__( 'Cancel', 'wp-mail-smtp' ),
@ -322,11 +320,11 @@ class Area {
'upgrade_bonus' => '<p>' .
wp_kses(
__( '<strong>Bonus:</strong> WP Mail SMTP users get <span>$50 off</span> regular price,<br>applied at checkout.', 'wp-mail-smtp' ),
array(
'strong' => true,
'span' => true,
'br' => true,
)
[
'strong' => [],
'span' => [],
'br' => [],
]
)
. '</p>',
'upgrade_doc' => '<a href="https://wpmailsmtp.com/docs/how-to-upgrade-wp-mail-smtp-to-pro-version/?utm_source=WordPress&amp;utm_medium=link&amp;utm_campaign=liteplugin" target="_blank" rel="noopener noreferrer" class="already-purchased">
@ -893,7 +891,7 @@ class Area {
return add_query_arg(
'page',
$page,
admin_url( 'admin.php' )
WP::admin_url( 'admin.php' )
);
}

View File

@ -2,6 +2,8 @@
namespace WPMailSMTP\Admin;
use WPMailSMTP\WP;
/**
* Class PageAbstract.
*
@ -23,7 +25,7 @@ abstract class PageAbstract implements PageInterface {
add_query_arg(
'tab',
$this->slug,
admin_url( 'admin.php?page=' . Area::SLUG )
WP::admin_url( 'admin.php?page=' . Area::SLUG )
)
);
}

View File

@ -6,6 +6,7 @@ use WPMailSMTP\Admin\Area;
use WPMailSMTP\Admin\PageAbstract;
use WPMailSMTP\Admin\PluginsInstallSkin;
use WPMailSMTP\Admin\PluginsInstallUpgrader;
use WPMailSMTP\WP;
/**
* Class About to display a page with About Us and Versus content.
@ -42,7 +43,7 @@ class About extends PageAbstract {
return add_query_arg(
'tab',
$this->get_defined_tab( $tab ),
admin_url( 'admin.php?page=' . Area::SLUG . '-' . $this->slug )
WP::admin_url( 'admin.php?page=' . Area::SLUG . '-' . $this->slug )
);
}
@ -214,6 +215,24 @@ class About extends PageAbstract {
</div>
<?php
// Do not display the plugin section if the user can't install or activate them.
if ( ! current_user_can( 'install_plugins' ) && ! current_user_can( 'activate_plugins' ) ) {
return;
}
$this->display_plugins();
}
/**
* Display the plugins section.
*
* @since 2.2.0
*/
protected function display_plugins() {
?>
<div class="wp-mail-smtp-admin-about-plugins">
<div class="plugins-container">
<?php
@ -229,11 +248,16 @@ class About extends PageAbstract {
$data = array_merge( $data, $this->get_about_plugins_data( $plugin, true ) );
}
// Do not display a plugin which has to be installed and the user can't install it.
if ( ! current_user_can( 'install_plugins' ) && $data['status_class'] === 'status-download' ) {
continue;
}
?>
<div class="plugin-container">
<div class="plugin-item">
<div class="details wp-mail-smtp-clear">
<img src="<?php echo \esc_url( $plugin['icon'] ); ?>">
<img src="<?php echo \esc_url( $plugin['icon'] ); ?>" alt="<?php esc_attr_e( 'Plugin icon', 'wp-mail-smtp' ); ?>">
<h5 class="plugin-name">
<?php echo $plugin['name']; ?>
</h5>
@ -430,7 +454,7 @@ class About extends PageAbstract {
$error = \esc_html__( 'Could not install the plugin.', 'wp-mail-smtp' );
// Check for permissions.
if ( ! \current_user_can( 'activate_plugins' ) ) {
if ( ! \current_user_can( 'install_plugins' ) ) {
\wp_send_json_error( $error );
}

View File

@ -4,11 +4,10 @@ namespace WPMailSMTP\Admin\Pages;
use WPMailSMTP\Admin\Area;
use WPMailSMTP\Admin\PageAbstract;
use WPMailSMTP\WP;
/**
* Class Logs
*
* @since 1.5.0
*/
class Logs extends PageAbstract {
@ -34,7 +33,7 @@ class Logs extends PageAbstract {
return add_query_arg(
'tab',
$this->slug,
admin_url( 'admin.php?page=' . Area::SLUG )
WP::admin_url( 'admin.php?page=' . Area::SLUG )
);
}

View File

@ -198,7 +198,12 @@ class MiscTab extends PageAbstract {
}
/**
* @inheritdoc
* Process tab form submission ($_POST).
*
* @since 1.0.0
* @since 2.2.0 Fixed checkbox saving and use the correct merge to prevent breaking other 'general' checkboxes.
*
* @param array $data Tab data specific for the plugin ($_POST).
*/
public function process_post( $data ) {
@ -207,14 +212,20 @@ class MiscTab extends PageAbstract {
$options = new Options();
// Unchecked checkboxes doesn't exist in $_POST, so we need to ensure we actually have them in data to save.
if ( empty( $data['general']['do_not_send'] ) ) {
$data['general']['do_not_send'] = false;
}
if ( empty( $data['general']['am_notifications_hidden'] ) ) {
$data['general']['am_notifications_hidden'] = false;
}
if ( empty( $data['general']['email_delivery_errors_hidden'] ) ) {
$data['general']['email_delivery_errors_hidden'] = false;
}
if ( empty( $data['general']['uninstall'] ) ) {
$data['general']['uninstall'] = false;
}
$to_save = array_merge( $options->get_all(), $data );
$to_save = Options::array_merge_recursive( $options->get_all(), $data );
// All the sanitization is done there.
$options->set( $to_save );

View File

@ -5,6 +5,7 @@ namespace WPMailSMTP\Admin\Pages;
use WPMailSMTP\Admin\PageAbstract;
use WPMailSMTP\Debug;
use WPMailSMTP\Options;
use WPMailSMTP\Providers\Gmail\Auth;
use WPMailSMTP\WP;
/**
@ -58,6 +59,8 @@ class SettingsTab extends PageAbstract {
<form method="POST" action="" autocomplete="off">
<?php $this->wp_nonce_field(); ?>
<?php ob_start(); ?>
<!-- License Section Title -->
<div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading" id="wp-mail-smtp-setting-row-license-heading">
<div class="wp-mail-smtp-setting-field">
@ -92,16 +95,37 @@ class SettingsTab extends PageAbstract {
<label for="wp-mail-smtp-setting-from_email"><?php esc_html_e( 'From Email', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<?php if ( 'gmail' !== $mailer ) : ?>
<input name="wp-mail-smtp[mail][from_email]" type="email"
value="<?php echo esc_attr( $options->get( 'mail', 'from_email' ) ); ?>"
<?php echo $options->is_const_defined( 'mail', 'from_email' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
id="wp-mail-smtp-setting-from_email" spellcheck="false"
placeholder="<?php echo esc_attr( wp_mail_smtp()->get_processor()->get_default_email() ); ?>">
<?php else : ?>
<?php
// Gmail mailer From Email selector.
$gmail_auth = new Auth();
$gmail_aliases = $gmail_auth->is_clients_saved() ? $gmail_auth->get_user_possible_send_from_addresses() : [];
?>
<?php if ( empty( $gmail_aliases ) ) : ?>
<select name="wp-mail-smtp[mail][from_email]" id="wp-mail-smtp-setting-from_email" disabled>
<option value=""><?php esc_html_e( 'Please first authorize the Gmail mailer below', 'wp-mail-smtp' ); ?></option>
</select>
<?php else : ?>
<select name="wp-mail-smtp[mail][from_email]" id="wp-mail-smtp-setting-from_email">
<?php foreach ( $gmail_aliases as $gmail_email_address ) : ?>
<option value="<?php echo esc_attr( $gmail_email_address ); ?>" <?php selected( $options->get( 'mail', 'from_email' ), $gmail_email_address ); ?>><?php echo esc_html( $gmail_email_address ); ?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
<?php endif; ?>
<?php if ( empty( $disabled_email ) ) : ?>
<p class="desc">
<?php esc_html_e( 'The email address which emails are sent from.', 'wp-mail-smtp' ); ?><br/>
<?php esc_html_e( 'If you using an email provider (Gmail, Yahoo, Outlook.com, etc) this should be your email address for that account.', 'wp-mail-smtp' ); ?>
<?php esc_html_e( 'If you\'re using an email provider (Yahoo, Outlook.com, etc) this should be your email address for that account.', 'wp-mail-smtp' ); ?>
</p>
<p class="desc">
<?php esc_html_e( 'Please note that other plugins can change this, to prevent this use the setting below.', 'wp-mail-smtp' ); ?>
@ -110,10 +134,16 @@ class SettingsTab extends PageAbstract {
<hr class="wp-mail-smtp-setting-mid-row-sep">
<?php if ( 'gmail' !== $mailer ) : ?>
<input name="wp-mail-smtp[mail][from_email_force]" type="checkbox"
value="true" <?php checked( true, (bool) $options->get( 'mail', 'from_email_force' ) ); ?>
<?php echo $options->is_const_defined( 'mail', 'from_email_force' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
id="wp-mail-smtp-setting-from_email_force">
<?php else : ?>
<input name="wp-mail-smtp[mail][from_email_force]" type="checkbox"
value="true" checked="checked" disabled
id="wp-mail-smtp-setting-from_email_force">
<?php endif; ?>
<label for="wp-mail-smtp-setting-from_email_force">
<?php esc_html_e( 'Force From Email', 'wp-mail-smtp' ); ?>
@ -121,7 +151,13 @@ class SettingsTab extends PageAbstract {
<?php if ( ! empty( $disabled_email ) ) : ?>
<p class="desc">
<?php esc_html_e( 'Current provider will automatically force From Email to be the email address that you use to set up the connection below.', 'wp-mail-smtp' ); ?>
<?php
if ( 'gmail' !== $mailer ) :
esc_html_e( 'Current provider will automatically force From Email to be the email address that you use to set up the connection below.', 'wp-mail-smtp' );
else :
esc_html_e( 'Gmail mailer will automatically force From Email to be the email address that you selected above.', 'wp-mail-smtp' );
endif;
?>
</p>
<?php else : ?>
<p class="desc">
@ -292,6 +328,11 @@ class SettingsTab extends PageAbstract {
</div>
<?php
$settings_content = apply_filters( 'wp_mail_smtp_admin_settings_tab_display', ob_get_clean() );
echo $settings_content; // phpcs:ignore
?>
<?php $this->display_save_btn(); ?>
</form>
@ -525,6 +566,8 @@ class SettingsTab extends PageAbstract {
}
}
$data = apply_filters( 'wp_mail_smtp_settings_tab_process_post', $data );
// New gmail clients data will be added from new $data.
$to_save = Options::array_merge_recursive( $old_opt, $data );

View File

@ -4,7 +4,7 @@ namespace WPMailSMTP\Admin\Pages;
use WPMailSMTP\Conflicts;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Options;
use WPMailSMTP\WP;
use WPMailSMTP\Admin\PageAbstract;
@ -393,8 +393,8 @@ Lead Developer, WP Mail SMTP';
*
* @since 1.0.0
*
* @param MailCatcher $phpmailer
* @param string $smtp_debug
* @param MailCatcherInterface $phpmailer The MailCatcher object.
* @param string $smtp_debug The SMTP debug message.
*
* @return string
*/

View File

@ -59,6 +59,10 @@ class Core {
if ( $this->is_not_loadable() ) {
add_action( 'admin_notices', 'wp_mail_smtp_insecure_php_version_notice' );
if ( WP::use_global_plugin_settings() ) {
add_action( 'network_admin_notices', 'wp_mail_smtp_insecure_php_version_notice' );
}
return;
}
@ -95,6 +99,11 @@ class Core {
*/
public function hooks() {
// Force from_email_force to always return true if current mailer is Gmail.
if ( ( new Options() )->get( 'mail', 'mailer' ) === 'gmail' ) {
add_filter( 'wp_mail_smtp_options_get', [ $this, 'gmail_mailer_get_from_email_force' ], 1, 3 );
}
// Action Scheduler requires a special early loading procedure.
add_action( 'plugins_loaded', array( $this, 'load_action_scheduler' ), - 10 );
@ -148,6 +157,11 @@ class Core {
if ( current_user_can( 'manage_options' ) ) {
add_action( 'admin_notices', array( '\WPMailSMTP\WP', 'display_admin_notices' ) );
add_action( 'admin_notices', array( $this, 'display_general_notices' ) );
if ( WP::use_global_plugin_settings() ) {
add_action( 'network_admin_notices', array( '\WPMailSMTP\WP', 'display_admin_notices' ) );
add_action( 'network_admin_notices', array( $this, 'display_general_notices' ) );
}
}
}
@ -455,7 +469,6 @@ class Core {
}
if ( wp_mail_smtp()->get_admin()->is_error_delivery_notice_enabled() ) {
$notice = Debug::get_last();
if ( ! empty( $notice ) ) {
@ -556,7 +569,7 @@ class Core {
*
* @since 1.0.0
*
* @return \WPMailSMTP\MailCatcher
* @return MailCatcherInterface
*/
public function replace_phpmailer() {
@ -573,11 +586,11 @@ class Core {
*
* @param null $obj PhpMailer object to override with own implementation.
*
* @return \WPMailSMTP\MailCatcher
* @return MailCatcherInterface
*/
protected function replace_w_fake_phpmailer( &$obj = null ) {
$obj = new MailCatcher( true );
$obj = $this->generate_mail_catcher( true );
return $obj;
}
@ -733,4 +746,99 @@ class Core {
require_once $this->plugin_path . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
}
/**
* Get the list of all custom DB tables that should be present in the DB.
*
* @since 2.1.2
*
* @return array List of table names.
*/
public function get_custom_db_tables() {
$tables = [
\WPMailSMTP\Tasks\Meta::get_table_name(),
];
return apply_filters( 'wp_mail_smtp_core_get_custom_db_tables', $tables );
}
/**
* Generate the correct MailCatcher object based on the PHPMailer version used in WP.
*
* Also conditionally require the needed class files.
*
* @see https://make.wordpress.org/core/2020/07/01/external-library-updates-in-wordpress-5-5-call-for-testing/
*
* @since 2.2.0
*
* @param bool $exceptions True if external exceptions should be thrown.
*
* @return MailCatcherInterface
*/
public function generate_mail_catcher( $exceptions = null ) {
if ( version_compare( get_bloginfo( 'version' ), '5.5-alpha', '<' ) ) {
if ( ! class_exists( '\PHPMailer', false ) ) {
require_once ABSPATH . WPINC . '/class-phpmailer.php';
}
$mail_catcher = new MailCatcher( $exceptions );
} else {
if ( ! class_exists( '\PHPMailer\PHPMailer\PHPMailer', false ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
}
if ( ! class_exists( '\PHPMailer\PHPMailer\Exception', false ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
}
if ( ! class_exists( '\PHPMailer\PHPMailer\SMTP', false ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
}
$mail_catcher = new MailCatcherV6( $exceptions );
}
return $mail_catcher;
}
/**
* Check if the passed object is a valid PHPMailer object.
*
* @since 2.2.0
*
* @param object $phpmailer A potential PHPMailer object to be tested.
*
* @return bool
*/
public function is_valid_phpmailer( $phpmailer ) {
return $phpmailer instanceof MailCatcherInterface ||
$phpmailer instanceof \PHPMailer ||
$phpmailer instanceof \PHPMailer\PHPMailer\PHPMailer;
}
/**
* Force the `mail.from_email_force` plugin option to always return true if the current saved mailer is Gmail.
* Alters the plugin options retrieving via the Options::get method.
*
* The gmail mailer check is performed when this filter is added.
*
* @since 2.2.0
*
* @param mixed $value The value of the plugin option that is being retrieved via Options::get method.
* @param string $group The group of the plugin option that is being retrieved via Options::get method.
* @param string $key The key of the plugin option that is being retrieved via Options::get method.
*
* @return mixed
*/
public function gmail_mailer_get_from_email_force( $value, $group, $key ) {
if ( $group === 'mail' && $key === 'from_email_force' ) {
$value = true;
}
return $value;
}
}

View File

@ -13,7 +13,7 @@ if ( ! class_exists( 'PHPMailer', false ) ) {
*
* @since 1.0.0
*/
class MailCatcher extends \PHPMailer {
class MailCatcher extends \PHPMailer implements MailCatcherInterface {
/**
* Callback Action function name.
@ -77,6 +77,9 @@ class MailCatcher extends \PHPMailer {
$mail_mailer === 'pepipost'
) {
try {
// Allow to hook early to catch any early failed emails.
do_action( 'wp_mail_smtp_mailcatcher_smtp_pre_send_before', $this );
// Prepare all the headers.
if ( ! $this->preSend() ) {
return false;
@ -151,4 +154,16 @@ class MailCatcher extends \PHPMailer {
return $this->CustomHeader;
}
/**
* Get the PHPMailer line ending.
*
* @since 2.2.0
*
* @return string
*/
public function get_line_ending() {
return $this->LE; // phpcs:ignore
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace WPMailSMTP;
/**
* Interface MailCatcherInterface.
*
* @since 2.2.0
*/
interface MailCatcherInterface {
/**
* Modify the default send() behaviour.
* For those mailers, that relies on PHPMailer class - call it directly.
* For others - init the correct provider and process it.
*
* @since 2.2.0
*
* @throws \phpmailerException|\PHPMailer\PHPMailer\Exception When sending via PhpMailer fails for some reason.
*
* @return bool
*/
public function send();
/**
* Get the PHPMailer line ending.
*
* @since 2.2.0
*
* @return string
*/
public function get_line_ending();
}

View File

@ -0,0 +1,149 @@
<?php
namespace WPMailSMTP;
/**
* Class MailCatcher replaces the \PHPMailer\PHPMailer\PHPMailer introduced in WP 5.5 and
* modifies the email sending logic. Thus, we can use other mailers API to do what we need, or stop emails completely.
*
* @since 2.2.0
*/
class MailCatcherV6 extends \PHPMailer\PHPMailer\PHPMailer implements MailCatcherInterface {
/**
* Callback Action function name.
*
* The function that handles the result of the send email action.
* It is called out by send() for each email sent.
*
* @since 2.2.0
*
* @var string
*/
public $action_function = '\WPMailSMTP\Processor::send_callback';
/**
* Modify the default send() behaviour.
* For those mailers, that relies on PHPMailer class - call it directly.
* For others - init the correct provider and process it.
*
* @since 2.2.0
*
* @throws \PHPMailer\PHPMailer\Exception When sending via PhpMailer fails for some reason.
*
* @return bool
*/
public function send() { // phpcs:ignore
$options = new Options();
$mail_mailer = sanitize_key( $options->get( 'mail', 'mailer' ) );
$is_emailing_blocked = false;
if ( wp_mail_smtp()->is_blocked() ) {
$is_emailing_blocked = true;
}
// Always allow a test email - check for the specific header.
foreach ( (array) $this->getCustomHeaders() as $header ) {
if (
! empty( $header[0] ) &&
! empty( $header[1] ) &&
$header[0] === 'X-Mailer-Type' &&
trim( $header[1] ) === 'WPMailSMTP/Admin/Test'
) {
$is_emailing_blocked = false;
}
};
// Do not send emails if admin desired that.
if ( $is_emailing_blocked ) {
return false;
}
// Define a custom header, that will be used to identify the plugin and the mailer.
$this->XMailer = 'WPMailSMTP/Mailer/' . $mail_mailer . ' ' . WPMS_PLUGIN_VER; // phpcs:ignore
// Use the default PHPMailer, as we inject our settings there for certain providers.
if (
$mail_mailer === 'mail' ||
$mail_mailer === 'smtp' ||
$mail_mailer === 'pepipost'
) {
try {
// Allow to hook early to catch any early failed emails.
do_action( 'wp_mail_smtp_mailcatcher_smtp_pre_send_before', $this );
// Prepare all the headers.
if ( ! $this->preSend() ) {
return false;
}
// Allow to hook after all the preparation before the actual sending.
do_action( 'wp_mail_smtp_mailcatcher_smtp_send_before', $this );
return $this->postSend();
} catch ( \PHPMailer\PHPMailer\Exception $e ) {
$this->mailHeader = ''; // phpcs:ignore
$this->setError( $e->getMessage() );
// Set the debug error, but not for default PHP mailer.
if ( $mail_mailer !== 'mail' ) {
Debug::set(
'Mailer: ' . esc_html( wp_mail_smtp()->get_providers()->get_options( $mail_mailer )->get_title() ) . PHP_EOL .
$e->getMessage()
);
}
if ( $this->exceptions ) {
throw $e;
}
return false;
}
}
// We need this so that the PHPMailer class will correctly prepare all the headers.
$this->Mailer = 'mail'; // phpcs:ignore
// Prepare everything (including the message) for sending.
if ( ! $this->preSend() ) {
return false;
}
$mailer = wp_mail_smtp()->get_providers()->get_mailer( $mail_mailer, $this );
if ( ! $mailer ) {
return false;
}
if ( ! $mailer->is_php_compatible() ) {
return false;
}
/*
* Send the actual email.
* We reuse everything, that was preprocessed for usage in PHPMailer.
*/
$mailer->send();
$is_sent = $mailer->is_email_sent();
// Allow to perform any actions with the data.
do_action( 'wp_mail_smtp_mailcatcher_send_after', $mailer, $this );
return $is_sent;
}
/**
* Get the PHPMailer line ending.
*
* @since 2.2.0
*
* @return string
*/
public function get_line_ending() {
return static::$LE; // phpcs:ignore
}
}

View File

@ -170,9 +170,11 @@ class Options {
* Retrieve all options of the plugin.
*
* @since 1.0.0
* @since 2.2.0 Added the filter.
*/
protected function populate_options() {
$this->_options = get_option( self::META_KEY, array() );
$this->_options = apply_filters( 'wp_mail_smtp_populate_options', get_option( self::META_KEY, [] ) );
}
/**

View File

@ -146,28 +146,19 @@ class Processor {
$forced = $options->get( 'mail', 'from_email_force' );
$from_email = $options->get( 'mail', 'from_email' );
if ( ! empty( $reply_to ) ) {
if ( ! empty( $reply_to ) || empty( $this->wp_mail_from ) ) {
return false;
}
if ( in_array( $mailer, array( 'gmail', 'outlook' ), true ) ) {
if ( $mailer === 'gmail' ) {
$forced = true;
switch ( $mailer ) {
case 'gmail':
$sender = wp_mail_smtp()->get_providers()->get_auth( 'gmail' )->get_user_info();
break;
case 'outlook':
} elseif ( $mailer === 'outlook' ) {
$sender = $options->get( 'outlook', 'user_details' );
break;
}
$from_email = ! empty( $sender['email'] ) ? $sender['email'] : '';
$forced = true;
}
if (
empty( $this->wp_mail_from ) ||
$from_email === $this->wp_mail_from ||
! $forced
) {
@ -213,7 +204,7 @@ class Processor {
* @since 1.3.0 Forcing email rewrite if option is selected.
* @since 1.7.0 Default email may be empty, so pay attention to that as well.
*
* @param string $wp_email
* @param string $wp_email The email address passed by the filter.
*
* @return string
*/
@ -222,11 +213,11 @@ class Processor {
$options = new Options();
$forced = $options->get( 'mail', 'from_email_force' );
$from_email = $options->get( 'mail', 'from_email' );
$def_email = $this->get_default_email();
$def_email = WP::get_default_email();
// Save the "original" set WP email from address for later use.
if ( $wp_email !== $def_email ) {
$this->wp_mail_from = $wp_email;
$this->wp_mail_from = filter_var( $wp_email, FILTER_VALIDATE_EMAIL );
}
// Return FROM EMAIL if forced in settings.
@ -308,7 +299,7 @@ class Processor {
*
* @since 1.9.0
*
* @return \WPMailSMTP\MailCatcher
* @return MailCatcherInterface
*/
public function get_phpmailer() {
@ -316,8 +307,7 @@ class Processor {
// Make sure the PHPMailer class has been instantiated.
if ( ! is_object( $phpmailer ) || ! is_a( $phpmailer, 'PHPMailer' ) ) {
require_once ABSPATH . WPINC . '/class-phpmailer.php';
$phpmailer = new MailCatcher( true ); // phpcs:ignore
$phpmailer = wp_mail_smtp()->generate_mail_catcher( true ); // phpcs:ignore
}
return $phpmailer;
@ -330,7 +320,7 @@ class Processor {
*
* @since 2.1.1
*
* @param \PHPMailer $phpmailer The PHPMailer object.
* @param MailCatcherInterface $phpmailer The PHPMailer object.
*/
private function set_default_reply_to( $phpmailer ) {

View File

@ -14,6 +14,15 @@ use WPMailSMTP\Providers\AuthAbstract;
*/
class Auth extends AuthAbstract {
/**
* List of all possible "from email" email addresses (aliases).
*
* @since 2.2.0
*
* @var null|array
*/
private $aliases = null;
/**
* Auth constructor.
*
@ -47,12 +56,15 @@ class Auth extends AuthAbstract {
*/
public static function get_plugin_auth_url() {
return add_query_arg(
return apply_filters(
'wp_mail_smtp_gmail_get_plugin_auth_url',
add_query_arg(
array(
'page' => Area::SLUG,
'tab' => 'auth',
),
admin_url( 'options-general.php' )
)
);
}
@ -280,4 +292,40 @@ class Auth extends AuthAbstract {
return array( 'email' => $email );
}
/**
* Get the registered email addresses that the user can use as the "from email".
*
* @since 2.2.0
*
* @return array The list of possible from email addresses.
*/
public function get_user_possible_send_from_addresses() {
if ( isset( $this->aliases ) ) {
return $this->aliases;
}
$gmail = new \Google_Service_Gmail( $this->get_client() );
try {
$response = $gmail->users_settings_sendAs->listUsersSettingsSendAs( 'me' ); // phpcs:ignore
// phpcs:disable
if ( isset( $response->sendAs ) ) {
$this->aliases = array_map(
function( $sendAsObject ) {
return $sendAsObject->sendAsEmail;
},
$response->sendAs
);
}
// phpcs:enable
} catch ( \Exception $exception ) {
$this->aliases = [];
}
return $this->aliases;
}
}

View File

@ -3,7 +3,7 @@
namespace WPMailSMTP\Providers\Gmail;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Providers\MailerAbstract;
/**
@ -37,9 +37,10 @@ class Mailer extends MailerAbstract {
*
* @since 1.0.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( $phpmailer ) {
parent::__construct( $phpmailer );
if ( ! $this->is_php_compatible() ) {
@ -52,14 +53,12 @@ class Mailer extends MailerAbstract {
*
* @since 1.2.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function process_phpmailer( $phpmailer ) {
// Make sure that we have access to MailCatcher class methods.
if (
! $phpmailer instanceof MailCatcher &&
! $phpmailer instanceof \PHPMailer
) {
// Make sure that we have access to PHPMailer class methods.
if ( ! wp_mail_smtp()->is_valid_phpmailer( $phpmailer ) ) {
return;
}
@ -79,25 +78,23 @@ class Mailer extends MailerAbstract {
$auth = new Auth();
$message = new \Google_Service_Gmail_Message();
/*
* Right now Gmail doesn't allow to redefine From and Sender email headers.
* It always uses the email address that was used to connect to its API.
* With code below we are making sure that Email Log archive and single Email Log
* have the save value for From email header.
*/
$gmail_creds = $auth->get_user_info();
// Set the authorized Gmail email address as the "from email" if the set email is not on the list of aliases.
$possible_from_emails = $auth->get_user_possible_send_from_addresses();
if ( ! empty( $gmail_creds['email'] ) ) {
$this->phpmailer->From = $gmail_creds['email'];
$this->phpmailer->Sender = $gmail_creds['email'];
if ( ! in_array( $this->phpmailer->From, $possible_from_emails, true ) ) {
$user_info = $auth->get_user_info();
if ( ! empty( $user_info['email'] ) ) {
$this->phpmailer->From = $user_info['email'];
$this->phpmailer->Sender = $user_info['email'];
}
}
try {
// Prepare a message for sending.
// Prepare a message for sending if any changes happened above.
$this->phpmailer->preSend();
// Get the raw MIME email using MailCatcher data.
// We need here to make base64URL-safe string.
// Get the raw MIME email using MailCatcher data. We need to make base64URL-safe string.
$base64 = str_replace(
[ '+', '/', '=' ],
[ '-', '_', '' ],

View File

@ -180,6 +180,23 @@ class Options extends OptionsAbstract {
}
?>
</span>
<p class="desc">
<?php
printf(
wp_kses( /* translators: %s - URL to Google Gmail alias documentation page. */
__( 'If you want to use a different From Email address you can set-up a Google email alias. <a href="%s" target="_blank" rel="noopener noreferrer">Follow these instructions</a> and then select the From Email at the top of this page.', 'wp-mail-smtp' ),
[
'a' => [
'href' => [],
'rel' => [],
'target' => [],
],
]
),
'https://support.google.com/a/answer/33327'
);
?>
</p>
<p class="desc">
<?php esc_html_e( 'Removing the connection will give you an ability to redo the connection or link to another Google account.', 'wp-mail-smtp' ); ?>
</p>

View File

@ -3,7 +3,7 @@
namespace WPMailSMTP\Providers;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Options;
/**
@ -39,7 +39,7 @@ class Loader {
/**
* @since 1.0.0
*
* @var MailCatcher
* @var MailCatcherInterface
*/
private $phpmailer;
@ -133,16 +133,13 @@ class Loader {
* @since 1.0.0
*
* @param string $provider The provider name.
* @param MailCatcher|\PHPMailer $phpmailer The MailCatcher object.
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*
* @return MailerAbstract|null
*/
public function get_mailer( $provider, $phpmailer ) {
if (
$phpmailer instanceof MailCatcher ||
$phpmailer instanceof \PHPMailer
) {
if ( wp_mail_smtp()->is_valid_phpmailer( $phpmailer ) ) {
$this->phpmailer = $phpmailer;
}

View File

@ -4,7 +4,7 @@ namespace WPMailSMTP\Providers;
use WPMailSMTP\Conflicts;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Options;
use WPMailSMTP\WP;
@ -32,7 +32,7 @@ abstract class MailerAbstract implements MailerInterface {
/**
* @since 1.0.0
*
* @var MailCatcher
* @var MailCatcherInterface
*/
protected $phpmailer;
/**
@ -74,9 +74,9 @@ abstract class MailerAbstract implements MailerInterface {
*
* @since 1.0.0
*
* @param MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( MailCatcher $phpmailer ) {
public function __construct( MailCatcherInterface $phpmailer ) {
$this->options = new Options();
$this->mailer = $this->options->get( 'mail', 'mailer' );
@ -94,15 +94,12 @@ abstract class MailerAbstract implements MailerInterface {
*
* @since 1.0.0
*
* @param MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function process_phpmailer( $phpmailer ) {
// Make sure that we have access to MailCatcher class methods.
if (
! $phpmailer instanceof MailCatcher &&
! $phpmailer instanceof \PHPMailer
) {
// Make sure that we have access to PHPMailer class methods.
if ( ! wp_mail_smtp()->is_valid_phpmailer( $phpmailer ) ) {
return;
}

View File

@ -2,6 +2,9 @@
namespace WPMailSMTP\Providers;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherV6;
/**
* Interface MailerInterface.
*
@ -77,7 +80,7 @@ interface MailerInterface {
*
* @since 1.2.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function process_phpmailer( $phpmailer );
}

View File

@ -2,6 +2,7 @@
namespace WPMailSMTP\Providers\Mailgun;
use WPMailSMTP\Debug;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
@ -56,7 +57,7 @@ class Mailer extends MailerAbstract {
// Default value should be defined before the parent class contructor fires.
$this->url = self::API_BASE_US;
// We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
// We want to prefill everything from MailCatcher class, which extends PHPMailer.
parent::__construct( $phpmailer );
// We have a special API URL to query in case of EU region.
@ -366,6 +367,40 @@ class Mailer extends MailerAbstract {
);
}
/**
* Whether the email is sent or not.
* We basically check the response code from a request to provider.
* Might not be 100% correct, not guarantees that email is delivered.
*
* In Mailgun's case it looks like we have to check if the response body has the message ID.
* All successful API responses should have `id` key in the response body.
*
* @since 2.2.0
*
* @return bool
*/
public function is_email_sent() {
$is_sent = parent::is_email_sent();
if (
$is_sent &&
isset( $this->response['body'] ) &&
! array_key_exists( 'id', (array) $this->response['body'] )
) {
$message = 'Mailer: Mailgun' . PHP_EOL .
esc_html__( 'Mailgun API request was successful, but it could not queue the email for delivery.', 'wp-mail-smtp' ) . PHP_EOL .
esc_html__( 'This could point to an incorrect Domain Name in the plugin settings.', 'wp-mail-smtp' ) . PHP_EOL .
esc_html__( 'Please check the WP Mail SMTP plugin settings and make sure the Mailgun Domain Name setting is correct.', 'wp-mail-smtp' );
Debug::set( $message );
return false;
}
return $is_sent;
}
/**
* Get a Mailgun-specific response with a helpful error.
*

View File

@ -2,14 +2,16 @@
namespace WPMailSMTP\Providers\PepipostAPI;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Options as PluginOptions;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
/**
* Class Mailer is basically a Sendgrid copy-paste, as Pepipost support SG migration.
* In the future we may rewrite the class to use the native Pepipost API.
* Pepipost API mailer.
*
* @since 1.8.0
* @since 1.8.0 Pepipost - SendGrid migration API.
* @since 2.2.0 Rewrote this class to use native Pepipost API.
*/
class Mailer extends MailerAbstract {
@ -26,24 +28,26 @@ class Mailer extends MailerAbstract {
* URL to make an API request to.
*
* @since 1.8.0
* @since 2.2.0 Changed the API url to Pepipost API v5.
*
* @var string
*/
protected $url = 'https://sgapi.pepipost.com/v3/mail/send';
protected $url = 'https://api.pepipost.com/v5/mail/send';
/**
* Mailer constructor.
*
* @since 1.8.0
* @since 2.2.0 Changed the API key header (API v5 changes).
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher instance.
*/
public function __construct( $phpmailer ) {
// We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
// We want to prefill everything from MailCatcher class, which extends PHPMailer.
parent::__construct( $phpmailer );
$this->set_header( 'Authorization', 'Bearer ' . $this->options->get( $this->mailer, 'api_key' ) );
$this->set_header( 'api_key', $this->options->get( $this->mailer, 'api_key' ) );
$this->set_header( 'content-type', 'application/json' );
}
@ -67,6 +71,7 @@ class Mailer extends MailerAbstract {
* Set the FROM header of the email.
*
* @since 1.8.0
* @since 2.2.0 Changed the attribute names (API v5 changes).
*
* @param string $email From mail.
* @param string $name From name.
@ -84,9 +89,9 @@ class Mailer extends MailerAbstract {
}
$this->set_body_param(
array(
[
'from' => $from,
)
]
);
}
@ -94,6 +99,7 @@ class Mailer extends MailerAbstract {
* Set the names/emails of people who will receive the email.
*
* @since 1.8.0
* @since 2.2.0 change the attribute names (API v5 changes).
*
* @param array $recipients List of recipients: cc/bcc/to.
*/
@ -103,62 +109,29 @@ class Mailer extends MailerAbstract {
return;
}
// Allow for now only these recipient types.
$default = array( 'to', 'cc', 'bcc' );
$data = array();
$data = [];
foreach ( $recipients as $type => $emails ) {
if (
! in_array( $type, $default, true ) ||
empty( $emails ) ||
! is_array( $emails )
) {
continue;
if ( ! empty( $recipients['to'] ) ) {
$data['to'] = $this->prepare_list_of_to_emails( $recipients['to'] );
}
$data[ $type ] = array();
// Iterate over all emails for each type.
// There might be multiple cc/to/bcc emails.
foreach ( $emails as $email ) {
$holder = array();
$addr = isset( $email[0] ) ? $email[0] : false;
$name = isset( $email[1] ) ? $email[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
if ( ! empty( $recipients['cc'] ) ) {
$data['cc'] = $this->prepare_list_of_emails( $recipients['cc'] );
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
if ( ! empty( $recipients['bcc'] ) ) {
$data['bcc'] = $this->prepare_list_of_emails( $recipients['bcc'] );
}
array_push( $data[ $type ], $holder );
}
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
'personalizations' => array( $data ),
)
);
if ( ! empty( $data['bcc'] ) ) {
// Only the 1st BCC email address, ignore the rest - is not supported by Pepipost.
$bcc['mail_settings']['bcc']['email'] = $data['bcc'][0]['email'];
$this->set_body_param(
$bcc
);
}
}
$this->set_body_personalizations( $data );
}
/**
* Set the email content.
* Pepipost API only supports HTML emails, so we have to replace new lines in plain text emails with <br>.
*
* @since 1.8.0
* @since 2.2.0 Change the way the content is prepared (API v5 changes).
*
* @param array|string $content Email content.
*/
@ -168,109 +141,70 @@ class Mailer extends MailerAbstract {
return;
}
if ( is_array( $content ) ) {
$html = '';
$default = array( 'text', 'html' );
$data = array();
foreach ( $content as $type => $body ) {
if (
! in_array( $type, $default, true ) ||
empty( $body )
) {
continue;
}
$content_type = 'text/plain';
$content_value = $body;
if ( $type === 'html' ) {
$content_type = 'text/html';
} else {
$content_value = nl2br( $content_value );
}
$data[] = array(
'type' => $content_type,
'value' => $content_value,
);
}
$this->set_body_param(
array(
'content' => $data,
)
);
} else {
$data['type'] = 'text/html';
$data['value'] = $content;
if ( ! is_array( $content ) ) {
$html = $content;
if ( $this->phpmailer->ContentType === 'text/plain' ) {
$data['type'] = 'text/plain';
$data['value'] = nl2br( $data['value'] );
$html = nl2br( $html );
}
} else {
if ( ! empty( $content['html'] ) ) {
$html = $content['html'];
} elseif ( ! empty( $content['text'] ) ) {
$html = nl2br( $content['text'] );
}
}
$this->set_body_param(
array(
'content' => array( $data ),
)
[
'content' => [
[
'type' => 'html',
'value' => $html,
],
],
]
);
}
}
/**
* Redefine the way custom headers are processed for this mailer - they should be in body.
* Redefine the way custom headers are processed for this mailer - they should be in body (personalizations).
*
* @since 1.8.0
* @since 2.2.0 Change the way the headers are processed (API v5 changes).
*
* @param array $headers
* @param array $headers The email headers to be applied.
*/
public function set_headers( $headers ) {
$valid_headers = [];
foreach ( $headers as $header ) {
$name = isset( $header[0] ) ? $header[0] : false;
$value = isset( $header[1] ) ? $header[1] : false;
$this->set_body_header( $name, $value );
$valid_headers[ $name ] = WP::sanitize_value( $value );
}
// Add custom PHPMailer-specific header.
$this->set_body_header( 'X-Mailer', 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
$valid_headers['X-Mailer'] = WP::sanitize_value( 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
if ( ! empty( $valid_headers ) ) {
$this->set_body_personalizations( [ 'headers' => $valid_headers ] );
}
}
/**
* This mailer supports email-related custom headers inside a body of the message.
* Pepipost API accepts an array of files content in body, so we will include all files and send.
* Doesn't handle exceeding the limits etc, as this will be reported by the API response.
*
* @since 1.8.0
* @since 2.2.0 Change the way the attachments are processed (API v5 changes).
*
* @param string $name
* @param string $value
*/
public function set_body_header( $name, $value ) {
$name = sanitize_text_field( $name );
if ( empty( $name ) ) {
return;
}
$headers = isset( $this->body['headers'] ) ? (array) $this->body['headers'] : array();
$headers[ $name ] = WP::sanitize_value( $value );
$this->set_body_param(
array(
'headers' => $headers,
)
);
}
/**
* Pepipost accepts an array of files content in body, so we will include all files and send.
* Doesn't handle exceeding the limits etc, as this is done and reported by SendGrid API.
*
* @since 1.8.0
*
* @param array $attachments
* @param array $attachments The list of attachments data.
*/
public function set_attachments( $attachments ) {
@ -278,7 +212,29 @@ class Mailer extends MailerAbstract {
return;
}
$data = array();
$data = $this->prepare_attachments( $attachments );
if ( ! empty( $data ) ) {
$this->set_body_param(
[
'attachments' => $data,
]
);
}
}
/**
* Prepare the attachments data for Pepipost API.
*
* @since 2.2.0
*
* @param array $attachments Array of attachments.
*
* @return array
*/
protected function prepare_attachments( $attachments ) {
$data = [];
foreach ( $attachments as $attachment ) {
$file = false;
@ -291,8 +247,7 @@ class Mailer extends MailerAbstract {
if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
$file = file_get_contents( $attachment[0] ); // phpcs:ignore
}
}
catch ( \Exception $e ) {
} catch ( \Exception $e ) {
$file = false;
}
@ -300,27 +255,21 @@ class Mailer extends MailerAbstract {
continue;
}
$data[] = array(
'content' => base64_encode( $file ),
'type' => $attachment[4],
'filename' => $attachment[2],
'disposition' => $attachment[6],
);
$data[] = [
'content' => base64_encode( $file ), // phpcs:ignore
'name' => $attachment[2],
];
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
'attachments' => $data,
)
);
}
return $data;
}
/**
* Set the reply-to property of the email.
* Pepipost API only supports one reply_to email, so we take the first one and discard the rest.
*
* @since 1.8.0
* @since 2.2.0 Change the way the reply_to is processed (API v5 changes).
*
* @param array $reply_to Name/email for reply-to feature.
*/
@ -330,54 +279,44 @@ class Mailer extends MailerAbstract {
return;
}
$data = array();
$email_array = array_shift( $reply_to );
foreach ( $reply_to as $key => $emails ) {
if (
empty( $emails ) ||
! is_array( $emails )
) {
continue;
if ( empty( $email_array[0] ) ) {
return;
}
$addr = isset( $emails[0] ) ? $emails[0] : false;
$name = isset( $emails[1] ) ? $emails[1] : false;
$email = $email_array[0];
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
$data['email'] = $addr;
if ( ! empty( $name ) ) {
$data['name'] = $name;
}
}
if ( ! empty( $data ) ) {
if ( ! empty( $email ) ) {
$this->set_body_param(
array(
'reply_to' => $data,
)
[
'reply_to' => $email,
]
);
}
}
/**
* Pepipost doesn't support sender or return_path params.
* Pepipost API doesn't support sender or return_path params.
* So we do nothing.
*
* @since 1.8.0
*
* @param string $from_email
* @param string $from_email The from email address.
*/
public function set_return_path( $from_email ) {}
/**
* Get a Pepipost-specific response with a helpful error.
*
* @see https://developers.pepipost.com/migration-api/new-subpage/errorcodes
* @see https://developers.pepipost.com/email-api/email-api/sendemail#responses
*
* @since 1.8.0
* @since 2.2.0 Change the way the response error message is processed (API v5 changes).
*
* @return string
*/
@ -385,27 +324,26 @@ class Mailer extends MailerAbstract {
$body = (array) wp_remote_retrieve_body( $this->response );
$error_text = array();
$error = ! empty( $body['error'] ) ? $body['error'] : '';
$info = ! empty( $body['info'] ) ? $body['info'] : '';
$message = '';
if ( ! empty( $body['errors'] ) ) {
foreach ( $body['errors'] as $error ) {
if ( property_exists( $error, 'message' ) ) {
// Prepare additional information from SendGrid API.
$extra = '';
if ( property_exists( $error, 'field' ) && ! empty( $error->field ) ) {
$extra .= $error->field . '; ';
}
if ( property_exists( $error, 'help' ) && ! empty( $error->help ) ) {
$extra .= $error->help;
}
if ( is_string( $error ) ) {
$message = $error . ( ( ! empty( $info ) ) ? ' - ' . $info : '' );
} elseif ( is_array( $error ) ) {
$message = '';
// Assign both the main message and perhaps extra information, if exists.
$error_text[] = $error->message . ( ! empty( $extra ) ? ' - ' . $extra : '' );
}
foreach ( $error as $item ) {
$message .= sprintf(
'%1$s (%2$s - %3$s)',
! empty( $item->description ) ? $item->description : esc_html__( 'General error', 'wp-mail-smtp' ),
! empty( $item->message ) ? $item->message : esc_html__( 'Error', 'wp-mail-smtp' ),
! empty( $item->field ) ? $item->field : ''
) . PHP_EOL;
}
}
return implode( '<br>', array_map( 'esc_textarea', $error_text ) );
return $message;
}
/**
@ -440,4 +378,98 @@ class Mailer extends MailerAbstract {
return false;
}
/**
* A special set method for Pepipost API "personalizations" attribute.
* We are sending one email at a time, so we should set just the first
* personalization item.
*
* Mainly used in set_headers and set_recipients.
*
* @see https://developers.pepipost.com/email-api/email-api/sendemail
*
* @since 2.2.0
*
* @param array $data The personalizations array of data (array of arrays).
*/
private function set_body_personalizations( $data ) {
if ( empty( $data ) ) {
return;
}
if ( ! empty( $this->body['personalizations'][0] ) ) {
$this->body['personalizations'][0] = PluginOptions::array_merge_recursive(
$this->body['personalizations'][0],
$data
);
} else {
$this->set_body_param(
[
'personalizations' => [
$data,
],
]
);
}
}
/**
* Prepare list of emails by filtering valid emails first.
*
* @since 2.2.0
*
* @param array $items A 2D array of email and name pair items (0 = email, 1 = name).
*
* @return array 2D array with 'email' keys.
*/
private function prepare_list_of_emails( $items ) {
$valid_emails = array_filter(
array_column( $items, 0 ),
function ( $email ) {
return filter_var( $email, FILTER_VALIDATE_EMAIL );
}
);
return array_map(
function( $email ) {
return [ 'email' => $email ];
},
$valid_emails
);
}
/**
* Prepare list of TO emails by filtering valid emails first
* and returning array of arrays (email, name).
*
* @since 2.2.0
*
* @param array $items A 2D array of email and name pair items (0 = email, 1 = name).
*
* @return array 2D array with 'email' and optional 'name' attributes.
*/
private function prepare_list_of_to_emails( $items ) {
$data = [];
foreach ( $items as $item ) {
$email = filter_var( $item[0], FILTER_VALIDATE_EMAIL );
if ( empty( $email ) ) {
continue;
}
$pair['email'] = $email;
if ( ! empty( $item[1] ) ) {
$pair['name'] = $item[1];
}
$data[] = $pair;
}
return $data;
}
}

View File

@ -2,6 +2,7 @@
namespace WPMailSMTP\Providers\SMTPcom;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
@ -37,11 +38,11 @@ class Mailer extends MailerAbstract {
*
* @since 2.0.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( $phpmailer ) {
// We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
// We want to prefill everything from MailCatcher class, which extends PHPMailer.
parent::__construct( $phpmailer );
// Set mailer specific headers.

View File

@ -2,6 +2,7 @@
namespace WPMailSMTP\Providers\Sendgrid;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
@ -35,11 +36,11 @@ class Mailer extends MailerAbstract {
*
* @since 1.0.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( $phpmailer ) {
// We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
// We want to prefill everything from MailCatcher class, which extends PHPMailer.
parent::__construct( $phpmailer );
$this->set_header( 'Authorization', 'Bearer ' . $this->options->get( $this->mailer, 'api_key' ) );

View File

@ -3,6 +3,7 @@
namespace WPMailSMTP\Providers\Sendinblue;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
@ -50,7 +51,7 @@ class Mailer extends MailerAbstract {
*
* @since 1.6.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( $phpmailer ) {
@ -305,21 +306,18 @@ class Mailer extends MailerAbstract {
$response = $api->get_smtp_client()->sendTransacEmail( $this->get_body() );
$this->process_response( $response );
}
catch ( \SendinBlue\Client\ApiException $e ) {
} catch ( \SendinBlue\Client\ApiException $e ) {
$error = json_decode( $e->getResponseBody() );
if ( json_last_error() === JSON_ERROR_NONE ) {
Debug::set(
'Mailer: Sendinblue' . "\r\n" .
'[' . sanitize_key( $error->code ) . ']: ' . esc_html( $error->message )
);
if ( json_last_error() === JSON_ERROR_NONE && ! empty( $error ) ) {
$message = '[' . sanitize_key( $error->code ) . ']: ' . esc_html( $error->message );
} else {
$message = $e->getMessage();
}
}
catch ( \Exception $e ) {
Debug::set(
'Mailer: Sendinblue' . "\r\n" .
$e->getMessage()
);
Debug::set( 'Mailer: Sendinblue' . PHP_EOL . $message );
} catch ( \Exception $e ) {
Debug::set( 'Mailer: Sendinblue' . PHP_EOL . $e->getMessage() );
return;
}

View File

@ -68,6 +68,11 @@ class SiteHealth {
'test' => array( $this, 'mailer_setup_complete_test' ),
);
$tests['direct']['wp_mail_smtp_db_tables_exist'] = array(
'label' => esc_html__( 'Do WP Mail SMTP DB tables exist?', 'wp-mail-smtp' ),
'test' => [ $this, 'db_tables_test' ],
);
return $tests;
}
@ -84,24 +89,30 @@ class SiteHealth {
public function register_debug_information( $debug_info ) {
$debug_notices = Debug::get();
$db_tables = $this->get_db_tables( 'existing' );
$debug_info[ self::DEBUG_INFO_SLUG ] = array(
$debug_info[ self::DEBUG_INFO_SLUG ] = [
'label' => $this->get_label(),
'fields' => array(
'version' => array(
'fields' => [
'version' => [
'label' => esc_html__( 'Version', 'wp-mail-smtp' ),
'value' => WPMS_PLUGIN_VER,
),
'license_key_type' => array(
],
'license_key_type' => [
'label' => esc_html__( 'License key type', 'wp-mail-smtp' ),
'value' => wp_mail_smtp()->get_license_type(),
),
'debug' => array(
],
'debug' => [
'label' => esc_html__( 'Debug', 'wp-mail-smtp' ),
'value' => ! empty( $debug_notices ) ? implode( '. ', $debug_notices ) : esc_html__( 'No debug notices found.', 'wp-mail-smtp' ),
),
),
);
],
'db_tables' => [
'label' => esc_html__( 'DB tables', 'wp-mail-smtp' ),
'value' => ! empty( $db_tables ) ?
implode( ', ', $db_tables ) : esc_html__( 'No DB tables found.', 'wp-mail-smtp' ),
],
],
];
return $debug_info;
}
@ -177,4 +188,75 @@ class SiteHealth {
return $result;
}
/**
* Perform the test for checking if all custom plugin DB tables exist.
*
* @since 2.1.2
*
* @return array
*/
public function db_tables_test() {
$result = array(
'label' => esc_html__( 'WP Mail SMTP DB tables are created', 'wp-mail-smtp' ),
'status' => 'good',
'badge' => array(
'label' => $this->get_label(),
'color' => self::BADGE_COLOR,
),
'description' => esc_html__( 'WP Mail SMTP is using custom database tables for some of its features. In order to work properly, the custom tables should be created, and it looks like they exist in your database.', 'wp-mail-smtp' ),
'actions' => '',
'test' => 'wp_mail_smtp_db_tables_exist',
);
$missing_tables = $this->get_db_tables( 'missing' );
if ( ! empty( $missing_tables ) ) {
$result['label'] = esc_html__( 'WP Mail SMTP DB tables check has failed', 'wp-mail-smtp' );
$result['status'] = 'critical';
$result['badge']['color'] = 'red';
$result['description'] = sprintf(
'<p>%s</p><p>%s</p>',
sprintf( /* translators: %s - the list of missing tables separated by comma. */
esc_html( _n( 'Missing table: %s', 'Missing tables: %s', count( $missing_tables ), 'wp-mail-smtp' ) ),
esc_html( implode( ', ', $missing_tables ) )
),
esc_html__( 'WP Mail SMTP is using custom database tables for some of its features. In order to work properly, the custom tables should be created, and it seems they are missing. Please try to re-install the WP Mail SMTP plugin. If this issue persists, please contact our support.', 'wp-mail-smtp' )
);
}
return $result;
}
/**
* Check DB:
* - if any required plugin DB table is missing,
* - which of the required plugin DB tables exist.
*
* @since 2.1.2
*
* @param string $check Which type of tables to return: 'missing' or 'existing'.
*
* @return array Missing or existing tables.
*/
private function get_db_tables( $check = 'missing' ) {
global $wpdb;
$tables = wp_mail_smtp()->get_custom_db_tables();
$missing_tables = [];
$existing_tables = [];
foreach ( $tables as $table ) {
if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) { // phpcs:ignore
$missing_tables[] = $table;
} else {
$existing_tables[] = $table;
}
}
return ( $check === 'existing' ) ? $existing_tables : $missing_tables;
}
}

View File

@ -195,7 +195,7 @@ class Task {
$this->meta_id = $task_meta->add(
[
'action' => $this->action,
'data' => $this->params,
'data' => isset( $this->params ) ? $this->params : [],
]
);

View File

@ -238,4 +238,63 @@ class WP {
return $filtered;
}
/**
* Get default email address.
*
* This is the same code as used in WP core for getting the default email address.
*
* @see https://github.com/WordPress/WordPress/blob/master/wp-includes/pluggable.php#L332
*
* @since 2.2.0
*
* @return string
*/
public static function get_default_email() {
$sitename = strtolower( $_SERVER['SERVER_NAME'] ); // phpcs:ignore
if ( 'www.' === substr( $sitename, 0, 4 ) ) {
$sitename = substr( $sitename, 4 );
}
return 'wordpress@' . $sitename;
}
/**
* Wrapper for the WP `admin_url` method that should be used in the plugin.
*
* We can filter into it, to maybe call `network_admin_url` for multisite support.
*
* @since 2.2.0
*
* @param string $path Optional path relative to the admin URL.
* @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl().
* 'http' or 'https' can be passed to force those schemes.
*
* @return string Admin URL link with optional path appended.
*/
public static function admin_url( $path = '', $scheme = 'admin' ) {
return apply_filters( 'wp_mail_smtp_admin_url', \admin_url( $path, $scheme ), $path, $scheme );
}
/**
* Check if the global plugin option in a multisite should be used.
* If the global plugin option "multisite" is set and true.
*
* @since 2.2.0
*
* @return bool
*/
public static function use_global_plugin_settings() {
if ( ! is_multisite() ) {
return false;
}
$main_site_options = get_blog_option( get_main_site_id(), Options::META_KEY, [] );
return ! empty( $main_site_options['general']['network_wide'] );
}
}

View File

@ -12,17 +12,14 @@ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
// Load plugin file.
require_once 'wp_mail_smtp.php';
require_once dirname( __FILE__ ) . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
// Confirm user has decided to remove all data, otherwise stop.
$settings = get_option( 'wp_mail_smtp', array() );
if ( empty( $settings['general']['uninstall'] ) ) {
return;
}
global $wpdb;
/*
* Remove Legacy options.
*/
$options = array(
$options = [
'_amn_smtp_last_checked',
'pepipost_ssl',
'pepipost_port',
@ -38,37 +35,123 @@ $options = array(
'mailer',
'mail_from_name',
'mail_from',
);
];
/**
* Remove AM announcement posts.
*/
$am_announcement_params = [
'post_type' => [ 'amn_smtp' ],
'post_status' => 'any',
'numberposts' => - 1,
'fields' => 'ids',
];
// WP MS uninstall process.
if ( is_multisite() ) {
$main_site_settings = get_blog_option( get_main_site_id(), 'wp_mail_smtp', [] );
$network_wide = ! empty( $main_site_settings['general']['network_wide'] );
$network_uninstall = ! empty( $main_site_settings['general']['uninstall'] );
$sites = get_sites();
foreach ( $sites as $site ) {
$settings = get_blog_option( $site->blog_id, 'wp_mail_smtp', [] );
// Confirm network site admin has decided to remove all data, otherwise skip.
if (
( $network_wide && ! $network_uninstall ) ||
( ! $network_wide && empty( $settings['general']['uninstall'] ) )
) {
continue;
}
/*
* Delete network site plugin options.
*/
foreach ( $options as $option ) {
delete_blog_option( $site->blog_id, $option );
}
// Switch to the current network site.
switch_to_blog( $site->blog_id );
// Delete plugin settings.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'wp\_mail\_smtp%'" ); // phpcs:ignore WordPress.DB
// Delete plugin user meta.
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
// Remove any transients we've left behind.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_timeout\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_timeout\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
/*
* Delete network site product announcements.
*/
$announcements = get_posts( $am_announcement_params );
if ( ! empty( $announcements ) ) {
foreach ( $announcements as $announcement ) {
wp_delete_post( $announcement, true );
}
}
/*
* Delete network site Logs for Pro plugin only.
*/
if (
function_exists( 'wp_mail_smtp' ) &&
is_readable( wp_mail_smtp()->plugin_path . '/src/Pro/Pro.php' )
) {
$table = \WPMailSMTP\Pro\Emails\Logs\Logs::get_table_name();
$wpdb->query( "DROP TABLE IF EXISTS $table;" ); // phpcs:ignore WordPress.DB
}
/*
* Drop all Action Scheduler data and unschedule all plugin ActionScheduler actions.
*/
( new \WPMailSMTP\Tasks\Tasks() )->cancel_all();
$meta_table = \WPMailSMTP\Tasks\Meta::get_table_name();
$wpdb->query( "DROP TABLE IF EXISTS $meta_table;" ); // phpcs:ignore WordPress.DB
// Restore the current network site back to the original one.
restore_current_blog();
}
} else { // Non WP MS uninstall process (for normal WP installs).
// Confirm user has decided to remove all data, otherwise stop.
$settings = get_option( 'wp_mail_smtp', [] );
if ( empty( $settings['general']['uninstall'] ) ) {
return;
}
/*
* Delete plugin options.
*/
foreach ( $options as $option ) {
delete_option( $option );
}
global $wpdb;
// Delete plugin settings.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'wp\_mail\_smtp%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'wp\_mail\_smtp%'" ); // phpcs:ignore WordPress.DB
// Delete plugin user meta.
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'wp\_mail\_smtp\_%'" );
$wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
// Remove any transients we've left behind.
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_wp\_mail\_smtp\_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_wp\_mail\_smtp\_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_timeout\_wp\_mail\_smtp\_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_timeout\_wp\_mail\_smtp\_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_timeout\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_site\_transient\_timeout\_wp\_mail\_smtp\_%'" ); // phpcs:ignore WordPress.DB
/*
* Remove product announcements.
*/
$announcements = get_posts(
array(
'post_type' => array( 'amn_smtp' ),
'post_status' => 'any',
'numberposts' => - 1,
'fields' => 'ids',
)
);
$announcements = get_posts( $am_announcement_params );
if ( ! empty( $announcements ) ) {
foreach ( $announcements as $announcement ) {
wp_delete_post( $announcement, true );
@ -82,18 +165,15 @@ if (
function_exists( 'wp_mail_smtp' ) &&
is_readable( wp_mail_smtp()->plugin_path . '/src/Pro/Pro.php' )
) {
// DB table.
$logs_table = \WPMailSMTP\Pro\Emails\Logs\Logs::get_table_name();
$wpdb->query( "DROP TABLE IF EXISTS $logs_table;" ); // phpcs:ignore WordPress.DB
$table = \WPMailSMTP\Pro\Emails\Logs\Logs::get_table_name();
$wpdb->query( "DROP TABLE IF EXISTS $table;" ); // phpcs:ignore WordPress.DB
}
/*
* Drop all Action Scheduler data.
* Drop all Action Scheduler data and unschedule all plugin ActionScheduler actions.
*/
require_once dirname( __FILE__ ) . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
// Unschedule all plugin ActionScheduler actions.
( new \WPMailSMTP\Tasks\Tasks() )->cancel_all();
$meta_table = \WPMailSMTP\Tasks\Meta::get_table_name();
$wpdb->query( "DROP TABLE IF EXISTS $meta_table;" ); // phpcs:ignore WordPress.DB
}

View File

@ -34,11 +34,6 @@ use Google\Auth\OAuth2;
*/
class UserRefreshCredentials extends CredentialsLoader implements GetQuotaProjectInterface
{
const CLOUD_SDK_CLIENT_ID =
'764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com';
const SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV = 'SUPPRESS_GCLOUD_CREDS_WARNING';
/**
* The OAuth2 instance used to conduct authorization.
*
@ -97,24 +92,6 @@ class UserRefreshCredentials extends CredentialsLoader implements GetQuotaProjec
if (array_key_exists('quota_project', $jsonKey)) {
$this->quotaProject = (string) $jsonKey['quota_project'];
}
if ($jsonKey['client_id'] === self::CLOUD_SDK_CLIENT_ID
&& is_null($this->quotaProject)
&& getenv(self::SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV) !== 'true') {
trigger_error(
'Your application has authenticated using end user credentials '
. 'from Google Cloud SDK. We recommend that most server '
. 'applications use service accounts instead. If your '
. 'application continues to use end user credentials '
. 'from Cloud SDK, you might receive a "quota exceeded" '
. 'or "API not enabled" error. For more information about '
. 'service accounts, see '
. 'https://cloud.google.com/docs/authentication/. '
. 'To disable this warning, set '
. self::SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV
. ' environment variable to "true".',
E_USER_WARNING
);
}
}
/**

View File

@ -20,6 +20,7 @@ namespace Google\Auth;
use Google\Auth\Credentials\InsecureCredentials;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\Credentials\UserRefreshCredentials;
use GuzzleHttp\ClientInterface;
/**
* CredentialsLoader contains the behaviour used to locate and find default
@ -54,6 +55,24 @@ abstract class CredentialsLoader implements FetchAuthTokenInterface
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
}
/**
* Returns the currently available major Guzzle version.
*
* @return int
*/
private static function getGuzzleMajorVersion()
{
if (defined('GuzzleHttp\ClientInterface::MAJOR_VERSION')) {
return ClientInterface::MAJOR_VERSION;
}
if (defined('GuzzleHttp\ClientInterface::VERSION')) {
return (int) substr(ClientInterface::VERSION, 0, 1);
}
throw new \Exception('Version not supported');
}
/**
* Load a JSON key from the path specified in the environment.
*
@ -145,10 +164,7 @@ abstract class CredentialsLoader implements FetchAuthTokenInterface
callable $httpHandler = null,
callable $tokenCallback = null
) {
$version = \GuzzleHttp\ClientInterface::VERSION;
switch ($version[0]) {
case '5':
if (self::getGuzzleMajorVersion() === 5) {
$client = new \GuzzleHttp\Client($httpClientOptions);
$client->setDefaultOption('auth', 'google_auth');
$subscriber = new Subscriber\AuthTokenSubscriber(
@ -158,7 +174,8 @@ abstract class CredentialsLoader implements FetchAuthTokenInterface
);
$client->getEmitter()->attach($subscriber);
return $client;
case '6':
}
$middleware = new Middleware\AuthTokenMiddleware(
$fetcher,
$httpHandler,
@ -171,9 +188,6 @@ abstract class CredentialsLoader implements FetchAuthTokenInterface
'handler' => $stack,
'auth' => 'google_auth',
] + $httpClientOptions);
default:
throw new \Exception('Version not supported');
}
}
/**

View File

@ -1,5 +1,19 @@
<?php
/**
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Google\Auth\HttpHandler;
use GuzzleHttp\ClientInterface;

View File

@ -0,0 +1,21 @@
<?php
/**
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Google\Auth\HttpHandler;
class Guzzle7HttpHandler extends Guzzle6HttpHandler
{
}

View File

@ -25,19 +25,27 @@ class HttpHandlerFactory
* Builds out a default http handler for the installed version of guzzle.
*
* @param ClientInterface $client
* @return Guzzle5HttpHandler|Guzzle6HttpHandler
* @return Guzzle5HttpHandler|Guzzle6HttpHandler|Guzzle7HttpHandler
* @throws \Exception
*/
public static function build(ClientInterface $client = null)
{
$version = ClientInterface::VERSION;
$client = $client ?: new Client();
switch ($version[0]) {
case '5':
$version = null;
if (defined('GuzzleHttp\ClientInterface::MAJOR_VERSION')) {
$version = ClientInterface::MAJOR_VERSION;
} elseif (defined('GuzzleHttp\ClientInterface::VERSION')) {
$version = (int) substr(ClientInterface::VERSION, 0, 1);
}
switch ($version) {
case 5:
return new Guzzle5HttpHandler($client);
case '6':
case 6:
return new Guzzle6HttpHandler($client);
case 7:
return new Guzzle7HttpHandler($client);
default:
throw new \Exception('Version not supported');
}

View File

@ -15,7 +15,7 @@ interface ClientInterface
/**
* @deprecated Will be removed in Guzzle 7.0.0
*/
const VERSION = '6.5.4';
const VERSION = '6.5.5';
/**
* Send an HTTP request.

View File

@ -81,9 +81,12 @@ final class Utils
}
/*
* The Idn class is marked as @internal. We've locked the version to
* symfony/polyfill-intl-idn to avoid issues in the future.
* The Idn class is marked as @internal. Verify that class and method exists.
*/
if (method_exists(Idn::class, 'idn_to_ascii')) {
return Idn::idn_to_ascii($domain, $options, Idn::INTL_IDNA_VARIANT_UTS46, $info);
}
throw new \RuntimeException('ext-intl or symfony/polyfill-intl-idn not loaded or too old');
}
}

View File

@ -510,6 +510,138 @@ return array(
'Ⴥ' => 'ⴥ',
'Ⴧ' => 'ⴧ',
'Ⴭ' => 'ⴭ',
'' => 'ꭰ',
'' => 'ꭱ',
'' => 'ꭲ',
'Ꭳ' => 'ꭳ',
'Ꭴ' => 'ꭴ',
'' => '',
'Ꭶ' => 'ꭶ',
'Ꭷ' => 'ꭷ',
'Ꭸ' => 'ꭸ',
'' => 'ꭹ',
'' => 'ꭺ',
'' => 'ꭻ',
'' => 'ꭼ',
'Ꭽ' => 'ꭽ',
'' => 'ꭾ',
'Ꭿ' => 'ꭿ',
'Ꮀ' => 'ꮀ',
'Ꮁ' => '',
'Ꮂ' => 'ꮂ',
'' => '',
'Ꮄ' => 'ꮄ',
'Ꮅ' => 'ꮅ',
'Ꮆ' => 'ꮆ',
'' => 'ꮇ',
'Ꮈ' => 'ꮈ',
'Ꮉ' => 'ꮉ',
'Ꮊ' => 'ꮊ',
'' => 'ꮋ',
'Ꮌ' => 'ꮌ',
'' => 'ꮍ',
'Ꮎ' => 'ꮎ',
'Ꮏ' => 'ꮏ',
'' => 'ꮐ',
'Ꮑ' => 'ꮑ',
'' => 'ꮒ',
'' => '',
'Ꮔ' => 'ꮔ',
'Ꮕ' => 'ꮕ',
'Ꮖ' => 'ꮖ',
'Ꮗ' => 'ꮗ',
'Ꮘ' => 'ꮘ',
'Ꮙ' => 'ꮙ',
'Ꮚ' => 'ꮚ',
'Ꮛ' => 'ꮛ',
'Ꮜ' => 'ꮜ',
'Ꮝ' => 'ꮝ',
'' => 'ꮞ',
'' => 'ꮟ',
'Ꮠ' => 'ꮠ',
'Ꮡ' => 'ꮡ',
'' => 'ꮢ',
'Ꮣ' => 'ꮣ',
'' => 'ꮤ',
'' => 'ꮥ',
'Ꮦ' => 'ꮦ',
'Ꮧ' => 'ꮧ',
'Ꮨ' => 'ꮨ',
'' => '',
'' => '',
'Ꮫ' => 'ꮫ',
'Ꮬ' => 'ꮬ',
'Ꮭ' => 'ꮭ',
'' => 'ꮮ',
'' => '',
'Ꮰ' => 'ꮰ',
'Ꮱ' => 'ꮱ',
'' => 'ꮲ',
'Ꮳ' => 'ꮳ',
'Ꮴ' => 'ꮴ',
'Ꮵ' => 'ꮵ',
'' => 'ꮶ',
'' => 'ꮷ',
'Ꮸ' => 'ꮸ',
'Ꮹ' => 'ꮹ',
'Ꮺ' => 'ꮺ',
'Ꮻ' => 'ꮻ',
'Ꮼ' => 'ꮼ',
'Ꮽ' => 'ꮽ',
'' => 'ꮾ',
'Ꮿ' => 'ꮿ',
'Ᏸ' => 'ᏸ',
'Ᏹ' => 'ᏹ',
'Ᏺ' => 'ᏺ',
'' => 'ᏻ',
'' => 'ᏼ',
'Ᏽ' => 'ᏽ',
'Ა' => 'ა',
'Ბ' => 'ბ',
'Გ' => 'გ',
'Დ' => 'დ',
'Ე' => 'ე',
'Ვ' => 'ვ',
'Ზ' => 'ზ',
'Თ' => 'თ',
'Ი' => 'ი',
'Კ' => 'კ',
'Ლ' => 'ლ',
'Მ' => 'მ',
'Ნ' => 'ნ',
'Ო' => 'ო',
'Პ' => 'პ',
'Ჟ' => 'ჟ',
'Რ' => 'რ',
'Ს' => 'ს',
'Ტ' => 'ტ',
'Უ' => 'უ',
'Ფ' => 'ფ',
'Ქ' => 'ქ',
'Ღ' => 'ღ',
'Ყ' => '',
'Შ' => 'შ',
'Ჩ' => 'ჩ',
'Ც' => 'ც',
'Ძ' => 'ძ',
'Წ' => 'წ',
'Ჭ' => 'ჭ',
'Ხ' => 'ხ',
'Ჯ' => 'ჯ',
'Ჰ' => 'ჰ',
'Ჱ' => 'ჱ',
'Ჲ' => 'ჲ',
'Ჳ' => 'ჳ',
'Ჴ' => 'ჴ',
'Ჵ' => 'ჵ',
'Ჶ' => 'ჶ',
'Ჷ' => 'ჷ',
'Ჸ' => 'ჸ',
'Ჹ' => 'ჹ',
'Ჺ' => 'ჺ',
'Ჽ' => 'ჽ',
'Ჾ' => 'ჾ',
'Ჿ' => '',
'Ḁ' => 'ḁ',
'Ḃ' => 'ḃ',
'Ḅ' => 'ḅ',
@ -993,8 +1125,24 @@ return array(
'' => 'ɜ',
'Ɡ' => 'ɡ',
'Ɬ' => 'ɬ',
'Ɪ' => 'ɪ',
'Ʞ' => 'ʞ',
'Ʇ' => 'ʇ',
'' => 'ʝ',
'' => 'ꭓ',
'' => 'ꞵ',
'Ꞷ' => 'ꞷ',
'Ꞹ' => 'ꞹ',
'Ꞻ' => 'ꞻ',
'Ꞽ' => 'ꞽ',
'Ꞿ' => 'ꞿ',
'Ꟃ' => 'ꟃ',
'Ꞔ' => 'ꞔ',
'Ʂ' => 'ʂ',
'Ᶎ' => 'ᶎ',
'Ꟈ' => 'ꟈ',
'Ꟊ' => 'ꟊ',
'Ꟶ' => 'ꟶ',
'' => '',
'' => '',
'' => '',
@ -1061,6 +1209,93 @@ return array(
'𐐥' => '𐑍',
'𐐦' => '𐑎',
'𐐧' => '𐑏',
'𐒰' => '𐓘',
'𐒱' => '𐓙',
'𐒲' => '𐓚',
'𐒳' => '𐓛',
'𐒴' => '𐓜',
'𐒵' => '𐓝',
'𐒶' => '𐓞',
'𐒷' => '𐓟',
'𐒸' => '𐓠',
'𐒹' => '𐓡',
'𐒺' => '𐓢',
'𐒻' => '𐓣',
'𐒼' => '𐓤',
'𐒽' => '𐓥',
'𐒾' => '𐓦',
'𐒿' => '𐓧',
'𐓀' => '𐓨',
'𐓁' => '𐓩',
'𐓂' => '𐓪',
'𐓃' => '𐓫',
'𐓄' => '𐓬',
'𐓅' => '𐓭',
'𐓆' => '𐓮',
'𐓇' => '𐓯',
'𐓈' => '𐓰',
'𐓉' => '𐓱',
'𐓊' => '𐓲',
'𐓋' => '𐓳',
'𐓌' => '𐓴',
'𐓍' => '𐓵',
'𐓎' => '𐓶',
'𐓏' => '𐓷',
'𐓐' => '𐓸',
'𐓑' => '𐓹',
'𐓒' => '𐓺',
'𐓓' => '𐓻',
'𐲀' => '𐳀',
'𐲁' => '𐳁',
'𐲂' => '𐳂',
'𐲃' => '𐳃',
'𐲄' => '𐳄',
'𐲅' => '𐳅',
'𐲆' => '𐳆',
'𐲇' => '𐳇',
'𐲈' => '𐳈',
'𐲉' => '𐳉',
'𐲊' => '𐳊',
'𐲋' => '𐳋',
'𐲌' => '𐳌',
'𐲍' => '𐳍',
'𐲎' => '𐳎',
'𐲏' => '𐳏',
'𐲐' => '𐳐',
'𐲑' => '𐳑',
'𐲒' => '𐳒',
'𐲓' => '𐳓',
'𐲔' => '𐳔',
'𐲕' => '𐳕',
'𐲖' => '𐳖',
'𐲗' => '𐳗',
'𐲘' => '𐳘',
'𐲙' => '𐳙',
'𐲚' => '𐳚',
'𐲛' => '𐳛',
'𐲜' => '𐳜',
'𐲝' => '𐳝',
'𐲞' => '𐳞',
'𐲟' => '𐳟',
'𐲠' => '𐳠',
'𐲡' => '𐳡',
'𐲢' => '𐳢',
'𐲣' => '𐳣',
'𐲤' => '𐳤',
'𐲥' => '𐳥',
'𐲦' => '𐳦',
'𐲧' => '𐳧',
'𐲨' => '𐳨',
'𐲩' => '𐳩',
'𐲪' => '𐳪',
'𐲫' => '𐳫',
'𐲬' => '𐳬',
'𐲭' => '𐳭',
'𐲮' => '𐳮',
'𐲯' => '𐳯',
'𐲰' => '𐳰',
'𐲱' => '𐳱',
'𐲲' => '𐳲',
'𑢠' => '𑣀',
'𑢡' => '𑣁',
'𑢢' => '𑣂',
@ -1093,4 +1328,70 @@ return array(
'𑢽' => '𑣝',
'𑢾' => '𑣞',
'𑢿' => '𑣟',
'𖹀' => '𖹠',
'𖹁' => '𖹡',
'𖹂' => '𖹢',
'𖹃' => '𖹣',
'𖹄' => '𖹤',
'𖹅' => '𖹥',
'𖹆' => '𖹦',
'𖹇' => '𖹧',
'𖹈' => '𖹨',
'𖹉' => '𖹩',
'𖹊' => '𖹪',
'𖹋' => '𖹫',
'𖹌' => '𖹬',
'𖹍' => '𖹭',
'𖹎' => '𖹮',
'𖹏' => '𖹯',
'𖹐' => '𖹰',
'𖹑' => '𖹱',
'𖹒' => '𖹲',
'𖹓' => '𖹳',
'𖹔' => '𖹴',
'𖹕' => '𖹵',
'𖹖' => '𖹶',
'𖹗' => '𖹷',
'𖹘' => '𖹸',
'𖹙' => '𖹹',
'𖹚' => '𖹺',
'𖹛' => '𖹻',
'𖹜' => '𖹼',
'𖹝' => '𖹽',
'𖹞' => '𖹾',
'𖹟' => '𖹿',
'𞤀' => '𞤢',
'𞤁' => '𞤣',
'𞤂' => '𞤤',
'𞤃' => '𞤥',
'𞤄' => '𞤦',
'𞤅' => '𞤧',
'𞤆' => '𞤨',
'𞤇' => '𞤩',
'𞤈' => '𞤪',
'𞤉' => '𞤫',
'𞤊' => '𞤬',
'𞤋' => '𞤭',
'𞤌' => '𞤮',
'𞤍' => '𞤯',
'𞤎' => '𞤰',
'𞤏' => '𞤱',
'𞤐' => '𞤲',
'𞤑' => '𞤳',
'𞤒' => '𞤴',
'𞤓' => '𞤵',
'𞤔' => '𞤶',
'𞤕' => '𞤷',
'𞤖' => '𞤸',
'𞤗' => '𞤹',
'𞤘' => '𞤺',
'𞤙' => '𞤻',
'𞤚' => '𞤼',
'𞤛' => '𞤽',
'𞤜' => '𞤾',
'𞤝' => '𞤿',
'𞤞' => '𞥀',
'𞤟' => '𞥁',
'𞤠' => '𞥂',
'𞤡' => '𞥃',
);

View File

@ -225,6 +225,7 @@ return array(
'ɦ' => 'Ɦ',
'ɨ' => 'Ɨ',
'ɩ' => 'Ɩ',
'ɪ' => 'Ɪ',
'ɫ' => 'Ɫ',
'ɬ' => 'Ɬ',
'ɯ' => 'Ɯ',
@ -233,6 +234,7 @@ return array(
'ɵ' => 'Ɵ',
'ɽ' => 'Ɽ',
'ʀ' => 'Ʀ',
'ʂ' => 'Ʂ',
'ʃ' => 'Ʃ',
'ʇ' => 'Ʇ',
'ʈ' => 'Ʈ',
@ -241,6 +243,7 @@ return array(
'ʋ' => 'Ʋ',
'ʌ' => 'Ʌ',
'ʒ' => 'Ʒ',
'ʝ' => '',
'ʞ' => 'Ʞ',
'ͅ' => 'Ι',
'ͱ' => 'Ͱ',
@ -493,8 +496,70 @@ return array(
'ք' => 'Ք',
'օ' => 'Օ',
'ֆ' => 'Ֆ',
'ა' => 'Ა',
'ბ' => 'Ბ',
'გ' => 'Გ',
'დ' => 'Დ',
'ე' => 'Ე',
'ვ' => 'Ვ',
'ზ' => 'Ზ',
'თ' => 'Თ',
'ი' => 'Ი',
'კ' => 'Კ',
'ლ' => 'Ლ',
'მ' => 'Მ',
'ნ' => 'Ნ',
'ო' => 'Ო',
'პ' => 'Პ',
'ჟ' => 'Ჟ',
'რ' => 'Რ',
'ს' => 'Ს',
'ტ' => 'Ტ',
'უ' => 'Უ',
'ფ' => 'Ფ',
'ქ' => 'Ქ',
'ღ' => 'Ღ',
'' => 'Ყ',
'შ' => 'Შ',
'ჩ' => 'Ჩ',
'ც' => 'Ც',
'ძ' => 'Ძ',
'წ' => 'Წ',
'ჭ' => 'Ჭ',
'ხ' => 'Ხ',
'ჯ' => 'Ჯ',
'ჰ' => 'Ჰ',
'ჱ' => 'Ჱ',
'ჲ' => 'Ჲ',
'ჳ' => 'Ჳ',
'ჴ' => 'Ჴ',
'ჵ' => 'Ჵ',
'ჶ' => 'Ჶ',
'ჷ' => 'Ჷ',
'ჸ' => 'Ჸ',
'ჹ' => 'Ჹ',
'ჺ' => 'Ჺ',
'ჽ' => 'Ჽ',
'ჾ' => 'Ჾ',
'' => 'Ჿ',
'ᏸ' => 'Ᏸ',
'ᏹ' => 'Ᏹ',
'ᏺ' => 'Ᏺ',
'ᏻ' => '',
'ᏼ' => '',
'ᏽ' => 'Ᏽ',
'ᲀ' => 'В',
'ᲁ' => 'Д',
'ᲂ' => 'О',
'ᲃ' => 'С',
'ᲄ' => 'Т',
'ᲅ' => 'Т',
'ᲆ' => 'Ъ',
'ᲇ' => 'Ѣ',
'ᲈ' => 'Ꙋ',
'ᵹ' => 'Ᵹ',
'ᵽ' => 'Ᵽ',
'ᶎ' => 'Ᶎ',
'ḁ' => 'Ḁ',
'ḃ' => 'Ḃ',
'ḅ' => 'Ḅ',
@ -993,6 +1058,7 @@ return array(
'' => 'Ꞌ',
'ꞑ' => 'Ꞑ',
'ꞓ' => 'Ꞓ',
'ꞔ' => 'Ꞔ',
'ꞗ' => 'Ꞗ',
'' => '',
'ꞛ' => 'Ꞛ',
@ -1003,6 +1069,97 @@ return array(
'ꞥ' => 'Ꞥ',
'ꞧ' => 'Ꞧ',
'ꞩ' => 'Ꞩ',
'ꞵ' => '',
'ꞷ' => 'Ꞷ',
'ꞹ' => 'Ꞹ',
'ꞻ' => 'Ꞻ',
'ꞽ' => 'Ꞽ',
'ꞿ' => 'Ꞿ',
'ꟃ' => 'Ꟃ',
'ꟈ' => 'Ꟈ',
'ꟊ' => 'Ꟊ',
'ꟶ' => 'Ꟶ',
'ꭓ' => '',
'ꭰ' => '',
'ꭱ' => '',
'ꭲ' => '',
'ꭳ' => 'Ꭳ',
'ꭴ' => 'Ꭴ',
'' => '',
'ꭶ' => 'Ꭶ',
'ꭷ' => 'Ꭷ',
'ꭸ' => 'Ꭸ',
'ꭹ' => '',
'ꭺ' => '',
'ꭻ' => '',
'ꭼ' => '',
'ꭽ' => 'Ꭽ',
'ꭾ' => '',
'ꭿ' => 'Ꭿ',
'ꮀ' => 'Ꮀ',
'' => 'Ꮁ',
'ꮂ' => 'Ꮂ',
'' => '',
'ꮄ' => 'Ꮄ',
'ꮅ' => 'Ꮅ',
'ꮆ' => 'Ꮆ',
'ꮇ' => '',
'ꮈ' => 'Ꮈ',
'ꮉ' => 'Ꮉ',
'ꮊ' => 'Ꮊ',
'ꮋ' => '',
'ꮌ' => 'Ꮌ',
'ꮍ' => '',
'ꮎ' => 'Ꮎ',
'ꮏ' => 'Ꮏ',
'ꮐ' => '',
'ꮑ' => 'Ꮑ',
'ꮒ' => '',
'' => '',
'ꮔ' => 'Ꮔ',
'ꮕ' => 'Ꮕ',
'ꮖ' => 'Ꮖ',
'ꮗ' => 'Ꮗ',
'ꮘ' => 'Ꮘ',
'ꮙ' => 'Ꮙ',
'ꮚ' => 'Ꮚ',
'ꮛ' => 'Ꮛ',
'ꮜ' => 'Ꮜ',
'ꮝ' => 'Ꮝ',
'ꮞ' => '',
'ꮟ' => '',
'ꮠ' => 'Ꮠ',
'ꮡ' => 'Ꮡ',
'ꮢ' => '',
'ꮣ' => 'Ꮣ',
'ꮤ' => '',
'ꮥ' => '',
'ꮦ' => 'Ꮦ',
'ꮧ' => 'Ꮧ',
'ꮨ' => 'Ꮨ',
'' => '',
'' => '',
'ꮫ' => 'Ꮫ',
'ꮬ' => 'Ꮬ',
'ꮭ' => 'Ꮭ',
'ꮮ' => '',
'' => '',
'ꮰ' => 'Ꮰ',
'ꮱ' => 'Ꮱ',
'ꮲ' => '',
'ꮳ' => 'Ꮳ',
'ꮴ' => 'Ꮴ',
'ꮵ' => 'Ꮵ',
'ꮶ' => '',
'ꮷ' => '',
'ꮸ' => 'Ꮸ',
'ꮹ' => 'Ꮹ',
'ꮺ' => 'Ꮺ',
'ꮻ' => 'Ꮻ',
'ꮼ' => 'Ꮼ',
'ꮽ' => 'Ꮽ',
'ꮾ' => '',
'ꮿ' => 'Ꮿ',
'' => '',
'' => '',
'' => '',
@ -1069,6 +1226,93 @@ return array(
'𐑍' => '𐐥',
'𐑎' => '𐐦',
'𐑏' => '𐐧',
'𐓘' => '𐒰',
'𐓙' => '𐒱',
'𐓚' => '𐒲',
'𐓛' => '𐒳',
'𐓜' => '𐒴',
'𐓝' => '𐒵',
'𐓞' => '𐒶',
'𐓟' => '𐒷',
'𐓠' => '𐒸',
'𐓡' => '𐒹',
'𐓢' => '𐒺',
'𐓣' => '𐒻',
'𐓤' => '𐒼',
'𐓥' => '𐒽',
'𐓦' => '𐒾',
'𐓧' => '𐒿',
'𐓨' => '𐓀',
'𐓩' => '𐓁',
'𐓪' => '𐓂',
'𐓫' => '𐓃',
'𐓬' => '𐓄',
'𐓭' => '𐓅',
'𐓮' => '𐓆',
'𐓯' => '𐓇',
'𐓰' => '𐓈',
'𐓱' => '𐓉',
'𐓲' => '𐓊',
'𐓳' => '𐓋',
'𐓴' => '𐓌',
'𐓵' => '𐓍',
'𐓶' => '𐓎',
'𐓷' => '𐓏',
'𐓸' => '𐓐',
'𐓹' => '𐓑',
'𐓺' => '𐓒',
'𐓻' => '𐓓',
'𐳀' => '𐲀',
'𐳁' => '𐲁',
'𐳂' => '𐲂',
'𐳃' => '𐲃',
'𐳄' => '𐲄',
'𐳅' => '𐲅',
'𐳆' => '𐲆',
'𐳇' => '𐲇',
'𐳈' => '𐲈',
'𐳉' => '𐲉',
'𐳊' => '𐲊',
'𐳋' => '𐲋',
'𐳌' => '𐲌',
'𐳍' => '𐲍',
'𐳎' => '𐲎',
'𐳏' => '𐲏',
'𐳐' => '𐲐',
'𐳑' => '𐲑',
'𐳒' => '𐲒',
'𐳓' => '𐲓',
'𐳔' => '𐲔',
'𐳕' => '𐲕',
'𐳖' => '𐲖',
'𐳗' => '𐲗',
'𐳘' => '𐲘',
'𐳙' => '𐲙',
'𐳚' => '𐲚',
'𐳛' => '𐲛',
'𐳜' => '𐲜',
'𐳝' => '𐲝',
'𐳞' => '𐲞',
'𐳟' => '𐲟',
'𐳠' => '𐲠',
'𐳡' => '𐲡',
'𐳢' => '𐲢',
'𐳣' => '𐲣',
'𐳤' => '𐲤',
'𐳥' => '𐲥',
'𐳦' => '𐲦',
'𐳧' => '𐲧',
'𐳨' => '𐲨',
'𐳩' => '𐲩',
'𐳪' => '𐲪',
'𐳫' => '𐲫',
'𐳬' => '𐲬',
'𐳭' => '𐲭',
'𐳮' => '𐲮',
'𐳯' => '𐲯',
'𐳰' => '𐲰',
'𐳱' => '𐲱',
'𐳲' => '𐲲',
'𑣀' => '𑢠',
'𑣁' => '𑢡',
'𑣂' => '𑢢',
@ -1101,4 +1345,70 @@ return array(
'𑣝' => '𑢽',
'𑣞' => '𑢾',
'𑣟' => '𑢿',
'𖹠' => '𖹀',
'𖹡' => '𖹁',
'𖹢' => '𖹂',
'𖹣' => '𖹃',
'𖹤' => '𖹄',
'𖹥' => '𖹅',
'𖹦' => '𖹆',
'𖹧' => '𖹇',
'𖹨' => '𖹈',
'𖹩' => '𖹉',
'𖹪' => '𖹊',
'𖹫' => '𖹋',
'𖹬' => '𖹌',
'𖹭' => '𖹍',
'𖹮' => '𖹎',
'𖹯' => '𖹏',
'𖹰' => '𖹐',
'𖹱' => '𖹑',
'𖹲' => '𖹒',
'𖹳' => '𖹓',
'𖹴' => '𖹔',
'𖹵' => '𖹕',
'𖹶' => '𖹖',
'𖹷' => '𖹗',
'𖹸' => '𖹘',
'𖹹' => '𖹙',
'𖹺' => '𖹚',
'𖹻' => '𖹛',
'𖹼' => '𖹜',
'𖹽' => '𖹝',
'𖹾' => '𖹞',
'𖹿' => '𖹟',
'𞤢' => '𞤀',
'𞤣' => '𞤁',
'𞤤' => '𞤂',
'𞤥' => '𞤃',
'𞤦' => '𞤄',
'𞤧' => '𞤅',
'𞤨' => '𞤆',
'𞤩' => '𞤇',
'𞤪' => '𞤈',
'𞤫' => '𞤉',
'𞤬' => '𞤊',
'𞤭' => '𞤋',
'𞤮' => '𞤌',
'𞤯' => '𞤍',
'𞤰' => '𞤎',
'𞤱' => '𞤏',
'𞤲' => '𞤐',
'𞤳' => '𞤑',
'𞤴' => '𞤒',
'𞤵' => '𞤓',
'𞤶' => '𞤔',
'𞤷' => '𞤕',
'𞤸' => '𞤖',
'𞤹' => '𞤗',
'𞤺' => '𞤘',
'𞤻' => '𞤙',
'𞤼' => '𞤚',
'𞤽' => '𞤛',
'𞤾' => '𞤜',
'𞤿' => '𞤝',
'𞥀' => '𞤞',
'𞥁' => '𞤟',
'𞥂' => '𞤠',
'𞥃' => '𞤡',
);

View File

@ -1,7 +1,7 @@
<?php
/**
* Plugin Name: WP Mail SMTP
* Version: 2.1.1
* Version: 2.2.1
* Plugin URI: https://wpmailsmtp.com/
* Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.
* Author: WPForms
@ -203,7 +203,7 @@ if ( ! function_exists( 'wp_mail_smtp_insecure_php_version_notice' ) ) {
}
if ( ! defined( 'WPMS_PLUGIN_VER' ) ) {
define( 'WPMS_PLUGIN_VER', '2.1.1' );
define( 'WPMS_PLUGIN_VER', '2.2.1' );
}
if ( ! defined( 'WPMS_PHP_VER' ) ) {
define( 'WPMS_PHP_VER', '5.5.0' );