updated plugin WP Mail SMTP version 2.2.1

This commit is contained in:
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

@ -1,283 +1,331 @@
<?php
namespace WPMailSMTP\Providers\Gmail;
use WPMailSMTP\Admin\Area;
use WPMailSMTP\Debug;
use WPMailSMTP\Options as PluginOptions;
use WPMailSMTP\Providers\AuthAbstract;
/**
* Class Auth to request access and refresh tokens.
*
* @since 1.0.0
*/
class Auth extends AuthAbstract {
/**
* Auth constructor.
*
* @since 1.0.0
*/
public function __construct() {
$options = new PluginOptions();
$this->mailer_slug = $options->get( 'mail', 'mailer' );
if ( $this->mailer_slug !== Options::SLUG ) {
return;
}
$this->options = $options->get_group( $this->mailer_slug );
if ( $this->is_clients_saved() ) {
$this->include_vendor_lib();
$this->client = $this->get_client();
}
}
/**
* Get the url, that users will be redirected back to finish the OAuth process.
*
* @since 1.5.2 Returned to the old, pre-1.5, structure of the link to preserve BC.
*
* @return string
*/
public static function get_plugin_auth_url() {
return add_query_arg(
array(
'page' => Area::SLUG,
'tab' => 'auth',
),
admin_url( 'options-general.php' )
);
}
/**
* Init and get the Google Client object.
*
* @since 1.0.0
* @since 1.5.0 Add ability to apply custom options to the client via a filter.
*
* @return \Google_Client
*/
public function get_client() {
// Doesn't load client twice + gives ability to overwrite.
if ( ! empty( $this->client ) ) {
return $this->client;
}
$this->include_vendor_lib();
$client = new \Google_Client(
array(
'client_id' => $this->options['client_id'],
'client_secret' => $this->options['client_secret'],
'redirect_uris' => array(
self::get_plugin_auth_url(),
),
)
);
$client->setApplicationName( 'WP Mail SMTP v' . WPMS_PLUGIN_VER );
$client->setAccessType( 'offline' );
$client->setApprovalPrompt( 'force' );
$client->setIncludeGrantedScopes( true );
// We request only the sending capability, as it's what we only need to do.
$client->setScopes( array( \Google_Service_Gmail::MAIL_GOOGLE_COM ) );
$client->setRedirectUri( self::get_plugin_auth_url() );
// Apply custom options to the client.
$client = apply_filters( 'wp_mail_smtp_providers_gmail_auth_get_client_custom_options', $client );
if (
$this->is_auth_required() &&
! empty( $this->options['auth_code'] )
) {
try {
$creds = $client->fetchAccessTokenWithAuthCode( $this->options['auth_code'] );
} catch ( \Exception $e ) {
$creds['error'] = $e->getMessage();
Debug::set(
'Mailer: Gmail' . "\r\n" .
$creds['error']
);
}
// Bail if we have an error.
if ( ! empty( $creds['error'] ) ) {
return $client;
}
$this->update_access_token( $client->getAccessToken() );
$this->update_refresh_token( $client->getRefreshToken() );
}
if ( ! empty( $this->options['access_token'] ) ) {
$client->setAccessToken( $this->options['access_token'] );
}
// Refresh the token if it's expired.
if ( $client->isAccessTokenExpired() ) {
$refresh = $client->getRefreshToken();
if ( empty( $refresh ) && isset( $this->options['refresh_token'] ) ) {
$refresh = $this->options['refresh_token'];
}
if ( ! empty( $refresh ) ) {
try {
$creds = $client->fetchAccessTokenWithRefreshToken( $refresh );
} catch ( \Exception $e ) {
$creds['error'] = $e->getMessage();
Debug::set(
'Mailer: Gmail' . "\r\n" .
$e->getMessage()
);
}
// Bail if we have an error.
if ( ! empty( $creds['error'] ) ) {
return $client;
}
$this->update_access_token( $client->getAccessToken() );
$this->update_refresh_token( $client->getRefreshToken() );
}
}
return $client;
}
/**
* Get the auth code from the $_GET and save it.
* Redirect user back to settings with an error message, if failed.
*
* @since 1.0.0
*/
public function process() {
if ( ! ( isset( $_GET['tab'] ) && $_GET['tab'] === 'auth' ) ) {
wp_safe_redirect( wp_mail_smtp()->get_admin()->get_admin_page_url() );
exit;
}
// We can't process without saved client_id/secret.
if ( ! $this->is_clients_saved() ) {
Debug::set(
esc_html__( 'There was an error while processing the Google authentication request. Please make sure that you have Client ID and Client Secret both valid and saved.', 'wp-mail-smtp' )
);
wp_safe_redirect(
add_query_arg(
'error',
'google_no_clients',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
$this->include_vendor_lib();
$code = '';
$scope = '';
$error = '';
if ( isset( $_GET['error'] ) ) {
$error = sanitize_key( $_GET['error'] );
}
// In case of any error: display a message to a user.
if ( ! empty( $error ) ) {
wp_safe_redirect(
add_query_arg(
'error',
'google_' . $error,
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
if ( isset( $_GET['code'] ) ) {
$code = $_GET['code'];
}
if ( isset( $_GET['scope'] ) ) {
$scope = urldecode( $_GET['scope'] );
}
// Let's try to get the access token.
if (
! empty( $code ) &&
(
$scope === \Google_Service_Gmail::MAIL_GOOGLE_COM . ' ' . \Google_Service_Gmail::GMAIL_SEND ||
$scope === \Google_Service_Gmail::GMAIL_SEND . ' ' . \Google_Service_Gmail::MAIL_GOOGLE_COM ||
$scope === \Google_Service_Gmail::GMAIL_SEND ||
$scope === \Google_Service_Gmail::MAIL_GOOGLE_COM
)
) {
// Save the auth code. So \Google_Client can reuse it to retrieve the access token.
$this->update_auth_code( $code );
} else {
wp_safe_redirect(
add_query_arg(
'error',
'google_no_code_scope',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
wp_safe_redirect(
add_query_arg(
'success',
'google_site_linked',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
/**
* Get the auth URL used to proceed to Provider to request access to send emails.
*
* @since 1.0.0
*
* @return string
*/
public function get_auth_url() {
if (
! empty( $this->client ) &&
class_exists( 'Google_Client', false ) &&
$this->client instanceof \Google_Client
) {
return filter_var( $this->client->createAuthUrl(), FILTER_SANITIZE_URL );
}
return '#';
}
/**
* Get user information (like email etc) that is associated with the current connection.
*
* @since 1.5.0
*
* @return array
*/
public function get_user_info() {
$gmail = new \Google_Service_Gmail( $this->get_client() );
try {
$email = $gmail->users->getProfile( 'me' )->getEmailAddress();
} catch ( \Exception $e ) {
$email = '';
}
return array( 'email' => $email );
}
}
<?php
namespace WPMailSMTP\Providers\Gmail;
use WPMailSMTP\Admin\Area;
use WPMailSMTP\Debug;
use WPMailSMTP\Options as PluginOptions;
use WPMailSMTP\Providers\AuthAbstract;
/**
* Class Auth to request access and refresh tokens.
*
* @since 1.0.0
*/
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.
*
* @since 1.0.0
*/
public function __construct() {
$options = new PluginOptions();
$this->mailer_slug = $options->get( 'mail', 'mailer' );
if ( $this->mailer_slug !== Options::SLUG ) {
return;
}
$this->options = $options->get_group( $this->mailer_slug );
if ( $this->is_clients_saved() ) {
$this->include_vendor_lib();
$this->client = $this->get_client();
}
}
/**
* Get the url, that users will be redirected back to finish the OAuth process.
*
* @since 1.5.2 Returned to the old, pre-1.5, structure of the link to preserve BC.
*
* @return string
*/
public static function get_plugin_auth_url() {
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' )
)
);
}
/**
* Init and get the Google Client object.
*
* @since 1.0.0
* @since 1.5.0 Add ability to apply custom options to the client via a filter.
*
* @return \Google_Client
*/
public function get_client() {
// Doesn't load client twice + gives ability to overwrite.
if ( ! empty( $this->client ) ) {
return $this->client;
}
$this->include_vendor_lib();
$client = new \Google_Client(
array(
'client_id' => $this->options['client_id'],
'client_secret' => $this->options['client_secret'],
'redirect_uris' => array(
self::get_plugin_auth_url(),
),
)
);
$client->setApplicationName( 'WP Mail SMTP v' . WPMS_PLUGIN_VER );
$client->setAccessType( 'offline' );
$client->setApprovalPrompt( 'force' );
$client->setIncludeGrantedScopes( true );
// We request only the sending capability, as it's what we only need to do.
$client->setScopes( array( \Google_Service_Gmail::MAIL_GOOGLE_COM ) );
$client->setRedirectUri( self::get_plugin_auth_url() );
// Apply custom options to the client.
$client = apply_filters( 'wp_mail_smtp_providers_gmail_auth_get_client_custom_options', $client );
if (
$this->is_auth_required() &&
! empty( $this->options['auth_code'] )
) {
try {
$creds = $client->fetchAccessTokenWithAuthCode( $this->options['auth_code'] );
} catch ( \Exception $e ) {
$creds['error'] = $e->getMessage();
Debug::set(
'Mailer: Gmail' . "\r\n" .
$creds['error']
);
}
// Bail if we have an error.
if ( ! empty( $creds['error'] ) ) {
return $client;
}
$this->update_access_token( $client->getAccessToken() );
$this->update_refresh_token( $client->getRefreshToken() );
}
if ( ! empty( $this->options['access_token'] ) ) {
$client->setAccessToken( $this->options['access_token'] );
}
// Refresh the token if it's expired.
if ( $client->isAccessTokenExpired() ) {
$refresh = $client->getRefreshToken();
if ( empty( $refresh ) && isset( $this->options['refresh_token'] ) ) {
$refresh = $this->options['refresh_token'];
}
if ( ! empty( $refresh ) ) {
try {
$creds = $client->fetchAccessTokenWithRefreshToken( $refresh );
} catch ( \Exception $e ) {
$creds['error'] = $e->getMessage();
Debug::set(
'Mailer: Gmail' . "\r\n" .
$e->getMessage()
);
}
// Bail if we have an error.
if ( ! empty( $creds['error'] ) ) {
return $client;
}
$this->update_access_token( $client->getAccessToken() );
$this->update_refresh_token( $client->getRefreshToken() );
}
}
return $client;
}
/**
* Get the auth code from the $_GET and save it.
* Redirect user back to settings with an error message, if failed.
*
* @since 1.0.0
*/
public function process() {
if ( ! ( isset( $_GET['tab'] ) && $_GET['tab'] === 'auth' ) ) {
wp_safe_redirect( wp_mail_smtp()->get_admin()->get_admin_page_url() );
exit;
}
// We can't process without saved client_id/secret.
if ( ! $this->is_clients_saved() ) {
Debug::set(
esc_html__( 'There was an error while processing the Google authentication request. Please make sure that you have Client ID and Client Secret both valid and saved.', 'wp-mail-smtp' )
);
wp_safe_redirect(
add_query_arg(
'error',
'google_no_clients',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
$this->include_vendor_lib();
$code = '';
$scope = '';
$error = '';
if ( isset( $_GET['error'] ) ) {
$error = sanitize_key( $_GET['error'] );
}
// In case of any error: display a message to a user.
if ( ! empty( $error ) ) {
wp_safe_redirect(
add_query_arg(
'error',
'google_' . $error,
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
if ( isset( $_GET['code'] ) ) {
$code = $_GET['code'];
}
if ( isset( $_GET['scope'] ) ) {
$scope = urldecode( $_GET['scope'] );
}
// Let's try to get the access token.
if (
! empty( $code ) &&
(
$scope === \Google_Service_Gmail::MAIL_GOOGLE_COM . ' ' . \Google_Service_Gmail::GMAIL_SEND ||
$scope === \Google_Service_Gmail::GMAIL_SEND . ' ' . \Google_Service_Gmail::MAIL_GOOGLE_COM ||
$scope === \Google_Service_Gmail::GMAIL_SEND ||
$scope === \Google_Service_Gmail::MAIL_GOOGLE_COM
)
) {
// Save the auth code. So \Google_Client can reuse it to retrieve the access token.
$this->update_auth_code( $code );
} else {
wp_safe_redirect(
add_query_arg(
'error',
'google_no_code_scope',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
wp_safe_redirect(
add_query_arg(
'success',
'google_site_linked',
wp_mail_smtp()->get_admin()->get_admin_page_url()
)
);
exit;
}
/**
* Get the auth URL used to proceed to Provider to request access to send emails.
*
* @since 1.0.0
*
* @return string
*/
public function get_auth_url() {
if (
! empty( $this->client ) &&
class_exists( 'Google_Client', false ) &&
$this->client instanceof \Google_Client
) {
return filter_var( $this->client->createAuthUrl(), FILTER_SANITIZE_URL );
}
return '#';
}
/**
* Get user information (like email etc) that is associated with the current connection.
*
* @since 1.5.0
*
* @return array
*/
public function get_user_info() {
$gmail = new \Google_Service_Gmail( $this->get_client() );
try {
$email = $gmail->users->getProfile( 'me' )->getEmailAddress();
} catch ( \Exception $e ) {
$email = '';
}
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,10 +37,11 @@ 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 );
parent::__construct( $phpmailer );
if ( ! $this->is_php_compatible() ) {
return;
@ -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

@ -1,234 +1,251 @@
<?php
namespace WPMailSMTP\Providers\Gmail;
use WPMailSMTP\Providers\OptionsAbstract;
/**
* Class Option.
*
* @since 1.0.0
*/
class Options extends OptionsAbstract {
/**
* Mailer slug.
*
* @since 1.5.0
*/
const SLUG = 'gmail';
/**
* Gmail Options constructor.
*
* @since 1.0.0
*/
public function __construct() {
parent::__construct(
array(
'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/google.svg',
'slug' => self::SLUG,
'title' => esc_html__( 'Gmail', 'wp-mail-smtp' ),
'description' => sprintf(
wp_kses( /* translators: %s - URL to our Gmail doc. */
__( 'Send emails using your Gmail or G Suite (formerly Google Apps) account, all while keeping your login credentials safe. Other Google SMTP methods require enabling less secure apps in your account and entering your password. However, this integration uses the Google API to improve email delivery issues while keeping your site secure.<br><br>Read our <a href="%s" target="_blank" rel="noopener noreferrer">Gmail documentation</a> to learn how to configure Gmail or G Suite.', 'wp-mail-smtp' ),
array(
'br' => array(),
'a' => array(
'href' => array(),
'rel' => array(),
'target' => array(),
),
)
),
'https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/'
),
'notices' => array(
'educational' => esc_html__( 'The Gmail mailer works well for sites that send low numbers of emails. However, Gmail\'s API has rate limitations and a number of additional restrictions that can lead to challenges during setup. If you expect to send a high volume of emails, or if you find that your web host is not compatible with the Gmail API restrictions, then we recommend considering a different mailer option.', 'wp-mail-smtp' ),
),
'php' => '5.5',
)
);
}
/**
* @inheritdoc
*/
public function display_options() {
// Do not display options if PHP version is not correct.
if ( ! $this->is_php_correct() ) {
$this->display_php_warning();
return;
}
?>
<!-- Client ID -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"><?php esc_html_e( 'Client ID', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<input name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][client_id]" type="text"
value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'client_id' ) ); ?>"
<?php echo $this->options->is_const_defined( $this->get_slug(), 'client_id' ) ? 'disabled' : ''; ?>
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_id" spellcheck="false"
/>
</div>
</div>
<!-- Client Secret -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"><?php esc_html_e( 'Client Secret', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<?php if ( $this->options->is_const_defined( $this->get_slug(), 'client_secret' ) ) : ?>
<input type="text" disabled value="****************************************"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
/>
<?php $this->display_const_set_message( 'WPMS_GMAIL_CLIENT_SECRET' ); ?>
<?php else : ?>
<input type="password" spellcheck="false"
name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][client_secret]"
value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'client_secret' ) ); ?>"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
/>
<?php endif; ?>
</div>
</div>
<!-- Authorized redirect URI -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"><?php esc_html_e( 'Authorized redirect URI', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<input type="text" readonly="readonly"
value="<?php echo esc_attr( Auth::get_plugin_auth_url() ); ?>"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"
/>
<button type="button" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-light-grey wp-mail-smtp-setting-copy"
title="<?php esc_attr_e( 'Copy URL to clipboard', 'wp-mail-smtp' ); ?>"
data-source_id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect">
<span class="dashicons dashicons-admin-page"></span>
</button>
<p class="desc">
<?php esc_html_e( 'Please copy this URL into the "Authorized redirect URIs" field of your Google web application.', 'wp-mail-smtp' ); ?>
</p>
</div>
</div>
<!-- Auth users button -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-authorize"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label><?php esc_html_e( 'Authorization', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<?php $this->display_auth_setting_action(); ?>
</div>
</div>
<?php
}
/**
* Display either an "Allow..." or "Remove..." button.
*
* @since 1.3.0
*/
protected function display_auth_setting_action() {
// Do the processing on the fly, as having ajax here is too complicated.
$this->process_provider_remove();
$auth = new Auth();
?>
<?php if ( $auth->is_clients_saved() ) : ?>
<?php if ( $auth->is_auth_required() ) : ?>
<a href="<?php echo esc_url( $auth->get_auth_url() ); ?>" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-orange">
<?php esc_html_e( 'Allow plugin to send emails using your Google account', 'wp-mail-smtp' ); ?>
</a>
<p class="desc">
<?php esc_html_e( 'Click the button above to confirm authorization.', 'wp-mail-smtp' ); ?>
</p>
<?php else : ?>
<a href="<?php echo esc_url( wp_nonce_url( wp_mail_smtp()->get_admin()->get_admin_page_url(), 'gmail_remove', 'gmail_remove_nonce' ) ); ?>#wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-authorize" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-red js-wp-mail-smtp-provider-remove">
<?php esc_html_e( 'Remove Connection', 'wp-mail-smtp' ); ?>
</a>
<span class="connected-as">
<?php
$user = $auth->get_user_info();
if ( ! empty( $user['email'] ) ) {
printf(
/* translators: %s - email address, as received from Google API. */
esc_html__( 'Connected as %s', 'wp-mail-smtp' ),
'<code>' . esc_html( $user['email'] ) . '</code>'
);
}
?>
</span>
<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>
<?php endif; ?>
<?php else : ?>
<p class="inline-notice inline-error">
<?php esc_html_e( 'You need to save settings with Client ID and Client Secret before you can proceed.', 'wp-mail-smtp' ); ?>
</p>
<?php
endif;
}
/**
* Remove Provider connection.
*
* @since 1.3.0
*/
public function process_provider_remove() {
if ( ! is_super_admin() ) {
return;
}
if (
! isset( $_GET['gmail_remove_nonce'] ) ||
! wp_verify_nonce( $_GET['gmail_remove_nonce'], 'gmail_remove' ) // phpcs:ignore
) {
return;
}
$options = new \WPMailSMTP\Options();
if ( $options->get( 'mail', 'mailer' ) !== $this->get_slug() ) {
return;
}
$old_opt = $options->get_all();
foreach ( $old_opt[ $this->get_slug() ] as $key => $value ) {
// Unset everything except Client ID and Secret.
if ( ! in_array( $key, array( 'client_id', 'client_secret' ), true ) ) {
unset( $old_opt[ $this->get_slug() ][ $key ] );
}
}
$options->set( $old_opt );
}
}
<?php
namespace WPMailSMTP\Providers\Gmail;
use WPMailSMTP\Providers\OptionsAbstract;
/**
* Class Option.
*
* @since 1.0.0
*/
class Options extends OptionsAbstract {
/**
* Mailer slug.
*
* @since 1.5.0
*/
const SLUG = 'gmail';
/**
* Gmail Options constructor.
*
* @since 1.0.0
*/
public function __construct() {
parent::__construct(
array(
'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/google.svg',
'slug' => self::SLUG,
'title' => esc_html__( 'Gmail', 'wp-mail-smtp' ),
'description' => sprintf(
wp_kses( /* translators: %s - URL to our Gmail doc. */
__( 'Send emails using your Gmail or G Suite (formerly Google Apps) account, all while keeping your login credentials safe. Other Google SMTP methods require enabling less secure apps in your account and entering your password. However, this integration uses the Google API to improve email delivery issues while keeping your site secure.<br><br>Read our <a href="%s" target="_blank" rel="noopener noreferrer">Gmail documentation</a> to learn how to configure Gmail or G Suite.', 'wp-mail-smtp' ),
array(
'br' => array(),
'a' => array(
'href' => array(),
'rel' => array(),
'target' => array(),
),
)
),
'https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/'
),
'notices' => array(
'educational' => esc_html__( 'The Gmail mailer works well for sites that send low numbers of emails. However, Gmail\'s API has rate limitations and a number of additional restrictions that can lead to challenges during setup. If you expect to send a high volume of emails, or if you find that your web host is not compatible with the Gmail API restrictions, then we recommend considering a different mailer option.', 'wp-mail-smtp' ),
),
'php' => '5.5',
)
);
}
/**
* @inheritdoc
*/
public function display_options() {
// Do not display options if PHP version is not correct.
if ( ! $this->is_php_correct() ) {
$this->display_php_warning();
return;
}
?>
<!-- Client ID -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"><?php esc_html_e( 'Client ID', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<input name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][client_id]" type="text"
value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'client_id' ) ); ?>"
<?php echo $this->options->is_const_defined( $this->get_slug(), 'client_id' ) ? 'disabled' : ''; ?>
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_id" spellcheck="false"
/>
</div>
</div>
<!-- Client Secret -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"><?php esc_html_e( 'Client Secret', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<?php if ( $this->options->is_const_defined( $this->get_slug(), 'client_secret' ) ) : ?>
<input type="text" disabled value="****************************************"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
/>
<?php $this->display_const_set_message( 'WPMS_GMAIL_CLIENT_SECRET' ); ?>
<?php else : ?>
<input type="password" spellcheck="false"
name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][client_secret]"
value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'client_secret' ) ); ?>"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_secret"
/>
<?php endif; ?>
</div>
</div>
<!-- Authorized redirect URI -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"><?php esc_html_e( 'Authorized redirect URI', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<input type="text" readonly="readonly"
value="<?php echo esc_attr( Auth::get_plugin_auth_url() ); ?>"
id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect"
/>
<button type="button" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-light-grey wp-mail-smtp-setting-copy"
title="<?php esc_attr_e( 'Copy URL to clipboard', 'wp-mail-smtp' ); ?>"
data-source_id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-client_redirect">
<span class="dashicons dashicons-admin-page"></span>
</button>
<p class="desc">
<?php esc_html_e( 'Please copy this URL into the "Authorized redirect URIs" field of your Google web application.', 'wp-mail-smtp' ); ?>
</p>
</div>
</div>
<!-- Auth users button -->
<div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-authorize"
class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
<div class="wp-mail-smtp-setting-label">
<label><?php esc_html_e( 'Authorization', 'wp-mail-smtp' ); ?></label>
</div>
<div class="wp-mail-smtp-setting-field">
<?php $this->display_auth_setting_action(); ?>
</div>
</div>
<?php
}
/**
* Display either an "Allow..." or "Remove..." button.
*
* @since 1.3.0
*/
protected function display_auth_setting_action() {
// Do the processing on the fly, as having ajax here is too complicated.
$this->process_provider_remove();
$auth = new Auth();
?>
<?php if ( $auth->is_clients_saved() ) : ?>
<?php if ( $auth->is_auth_required() ) : ?>
<a href="<?php echo esc_url( $auth->get_auth_url() ); ?>" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-orange">
<?php esc_html_e( 'Allow plugin to send emails using your Google account', 'wp-mail-smtp' ); ?>
</a>
<p class="desc">
<?php esc_html_e( 'Click the button above to confirm authorization.', 'wp-mail-smtp' ); ?>
</p>
<?php else : ?>
<a href="<?php echo esc_url( wp_nonce_url( wp_mail_smtp()->get_admin()->get_admin_page_url(), 'gmail_remove', 'gmail_remove_nonce' ) ); ?>#wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-authorize" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-red js-wp-mail-smtp-provider-remove">
<?php esc_html_e( 'Remove Connection', 'wp-mail-smtp' ); ?>
</a>
<span class="connected-as">
<?php
$user = $auth->get_user_info();
if ( ! empty( $user['email'] ) ) {
printf(
/* translators: %s - email address, as received from Google API. */
esc_html__( 'Connected as %s', 'wp-mail-smtp' ),
'<code>' . esc_html( $user['email'] ) . '</code>'
);
}
?>
</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>
<?php endif; ?>
<?php else : ?>
<p class="inline-notice inline-error">
<?php esc_html_e( 'You need to save settings with Client ID and Client Secret before you can proceed.', 'wp-mail-smtp' ); ?>
</p>
<?php
endif;
}
/**
* Remove Provider connection.
*
* @since 1.3.0
*/
public function process_provider_remove() {
if ( ! is_super_admin() ) {
return;
}
if (
! isset( $_GET['gmail_remove_nonce'] ) ||
! wp_verify_nonce( $_GET['gmail_remove_nonce'], 'gmail_remove' ) // phpcs:ignore
) {
return;
}
$options = new \WPMailSMTP\Options();
if ( $options->get( 'mail', 'mailer' ) !== $this->get_slug() ) {
return;
}
$old_opt = $options->get_all();
foreach ( $old_opt[ $this->get_slug() ] as $key => $value ) {
// Unset everything except Client ID and Secret.
if ( ! in_array( $key, array( 'client_id', 'client_secret' ), true ) ) {
unset( $old_opt[ $this->get_slug() ][ $key ] );
}
}
$options->set( $old_opt );
}
}

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;
@ -132,17 +132,14 @@ class Loader {
*
* @since 1.0.0
*
* @param string $provider The provider name.
* @param MailCatcher|\PHPMailer $phpmailer The MailCatcher object.
* @param string $provider The provider name.
* @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

@ -1,83 +1,86 @@
<?php
namespace WPMailSMTP\Providers;
/**
* Interface MailerInterface.
*
* @since 1.0.0
*/
interface MailerInterface {
/**
* Send the email.
*
* @since 1.0.0
*/
public function send();
/**
* 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.
*
* @since 1.0.0
*
* @return bool
*/
public function is_email_sent();
/**
* Whether the mailer supports the current PHP version or not.
*
* @since 1.0.0
*
* @return bool
*/
public function is_php_compatible();
/**
* Whether the mailer has all its settings correctly set up and saved.
*
* @since 1.4.0
*
* @return bool
*/
public function is_mailer_complete();
/**
* Get the email body.
*
* @since 1.0.0
*
* @return string|array
*/
public function get_body();
/**
* Get the email headers.
*
* @since 1.0.0
*
* @return array
*/
public function get_headers();
/**
* Get an array of all debug information relevant to the mailer.
*
* @since 1.2.0
*
* @return array
*/
public function get_debug_info();
/**
* Re-use the MailCatcher class methods and properties.
*
* @since 1.2.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
*/
public function process_phpmailer( $phpmailer );
}
<?php
namespace WPMailSMTP\Providers;
use WPMailSMTP\MailCatcher;
use WPMailSMTP\MailCatcherV6;
/**
* Interface MailerInterface.
*
* @since 1.0.0
*/
interface MailerInterface {
/**
* Send the email.
*
* @since 1.0.0
*/
public function send();
/**
* 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.
*
* @since 1.0.0
*
* @return bool
*/
public function is_email_sent();
/**
* Whether the mailer supports the current PHP version or not.
*
* @since 1.0.0
*
* @return bool
*/
public function is_php_compatible();
/**
* Whether the mailer has all its settings correctly set up and saved.
*
* @since 1.4.0
*
* @return bool
*/
public function is_mailer_complete();
/**
* Get the email body.
*
* @since 1.0.0
*
* @return string|array
*/
public function get_body();
/**
* Get the email headers.
*
* @since 1.0.0
*
* @return array
*/
public function get_headers();
/**
* Get an array of all debug information relevant to the mailer.
*
* @since 1.2.0
*
* @return array
*/
public function get_debug_info();
/**
* Re-use the MailCatcher class methods and properties.
*
* @since 1.2.0
*
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function process_phpmailer( $phpmailer );
}

View File

@ -1,431 +1,466 @@
<?php
namespace WPMailSMTP\Providers\Mailgun;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
/**
* Class Mailer.
*
* @since 1.0.0
*/
class Mailer extends MailerAbstract {
/**
* Which response code from HTTP provider is considered to be successful?
*
* @since 1.0.0
*
* @var int
*/
protected $email_sent_code = 200;
/**
* API endpoint used for sites from all regions.
*
* @since 1.4.0
*
* @var string
*/
const API_BASE_US = 'https://api.mailgun.net/v3/';
/**
* API endpoint used for sites from EU region.
*
* @since 1.4.0
*
* @var string
*/
const API_BASE_EU = 'https://api.eu.mailgun.net/v3/';
/**
* URL to make an API request to.
*
* @since 1.0.0
*
* @var string
*/
protected $url = '';
/**
* @inheritdoc
*/
public function __construct( $phpmailer ) {
// 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.
parent::__construct( $phpmailer );
// We have a special API URL to query in case of EU region.
if ( 'EU' === $this->options->get( $this->mailer, 'region' ) ) {
$this->url = self::API_BASE_EU;
}
/*
* Append the url with a domain,
* to avoid passing the domain name as a query parameter with all requests.
*/
$this->url .= sanitize_text_field( $this->options->get( $this->mailer, 'domain' ) . '/messages' );
$this->set_header( 'Authorization', 'Basic ' . base64_encode( 'api:' . $this->options->get( $this->mailer, 'api_key' ) ) );
}
/**
* @inheritdoc
*/
public function set_from( $email, $name = '' ) {
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
if ( ! empty( $name ) ) {
$this->set_body_param(
array(
'from' => $name . ' <' . $email . '>',
)
);
} else {
$this->set_body_param(
array(
'from' => $email,
)
);
}
}
/**
* @inheritdoc
*/
public function set_recipients( $recipients ) {
if ( empty( $recipients ) ) {
return;
}
$default = array( 'to', 'cc', 'bcc' );
foreach ( $recipients as $kind => $emails ) {
if (
! in_array( $kind, $default, true ) ||
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$data = array();
foreach ( $emails as $email ) {
$addr = isset( $email[0] ) ? $email[0] : false;
$name = isset( $email[1] ) ? $email[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
if ( ! empty( $name ) ) {
$data[] = $name . ' <' . $addr . '>';
} else {
$data[] = $addr;
}
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
$kind => implode( ', ', $data ),
)
);
}
}
}
/**
* @inheritdoc
*/
public function set_content( $content ) {
if ( is_array( $content ) ) {
$default = array( 'text', 'html' );
foreach ( $content as $type => $mail ) {
if (
! in_array( $type, $default, true ) ||
empty( $mail )
) {
continue;
}
$this->set_body_param(
array(
$type => $mail,
)
);
}
} else {
$type = 'html';
if ( $this->phpmailer->ContentType === 'text/plain' ) {
$type = 'text';
}
if ( ! empty( $content ) ) {
$this->set_body_param(
array(
$type => $content,
)
);
}
}
}
/**
* Redefine the way custom headers are process for this mailer - they should be in body.
*
* @since 1.5.0
*
* @param array $headers
*/
public function set_headers( $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 );
}
// Add custom PHPMailer-specific header.
$this->set_body_header( 'X-Mailer', 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
}
/**
* This mailer supports email-related custom headers inside a body of the message with a special prefix "h:".
*
* @since 1.5.0
*/
public function set_body_header( $name, $value ) {
$name = sanitize_text_field( $name );
$this->set_body_param(
array(
'h:' . $name => WP::sanitize_value( $value ),
)
);
}
/**
* It's the last one, so we can modify the whole body.
*
* @since 1.0.0
*
* @param array $attachments
*/
public function set_attachments( $attachments ) {
if ( empty( $attachments ) ) {
return;
}
$payload = '';
$data = array();
foreach ( $attachments as $attachment ) {
$file = false;
/*
* We are not using WP_Filesystem API as we can't reliably work with it.
* It is not always available, same as credentials for FTP.
*/
try {
if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
$file = file_get_contents( $attachment[0] );
}
}
catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = array(
'content' => $file,
'name' => $attachment[2],
);
}
if ( ! empty( $data ) ) {
// First, generate a boundary for the multipart message.
$boundary = base_convert( uniqid( 'boundary', true ), 10, 36 );
// Iterate through pre-built params and build a payload.
foreach ( $this->body as $key => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $child_key => $child_value ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . "\"\r\n\r\n";
$payload .= $child_value;
$payload .= "\r\n";
}
} else {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n";
$payload .= $value;
$payload .= "\r\n";
}
}
// Now iterate through our attachments, and add them too.
foreach ( $data as $key => $attachment ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="attachment[' . $key . ']"; filename="' . $attachment['name'] . '"' . "\r\n\r\n";
$payload .= $attachment['content'];
$payload .= "\r\n";
}
$payload .= '--' . $boundary . '--';
// Redefine the body the "dirty way".
$this->body = $payload;
$this->set_header( 'Content-Type', 'multipart/form-data; boundary=' . $boundary );
}
}
/**
* @inheritdoc
*/
public function set_reply_to( $reply_to ) {
if ( empty( $reply_to ) ) {
return;
}
$data = array();
foreach ( $reply_to as $key => $emails ) {
if (
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$addr = isset( $emails[0] ) ? $emails[0] : false;
$name = isset( $emails[1] ) ? $emails[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
if ( ! empty( $name ) ) {
$data[] = $name . ' <' . $addr . '>';
} else {
$data[] = $addr;
}
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
'h:Reply-To' => implode( ',', $data ),
)
);
}
}
/**
* @inheritdoc
*/
public function set_return_path( $email ) {
if (
$this->options->get( 'mail', 'return_path' ) !== true ||
! filter_var( $email, FILTER_VALIDATE_EMAIL )
) {
return;
}
$this->set_body_param(
array(
'sender' => $email,
)
);
}
/**
* Get a Mailgun-specific response with a helpful error.
*
* @since 1.2.0
*
* @return string
*/
protected function get_response_error() {
$body = (array) wp_remote_retrieve_body( $this->response );
$error_text = array();
if ( ! empty( $body['message'] ) ) {
if ( is_string( $body['message'] ) ) {
$error_text[] = $body['message'];
} else {
$error_text[] = \json_encode( $body['message'] );
}
} elseif ( ! empty( $body[0] ) ) {
if ( is_string( $body[0] ) ) {
$error_text[] = $body[0];
} else {
$error_text[] = \json_encode( $body[0] );
}
}
return implode( '<br>', array_map( 'esc_textarea', $error_text ) );
}
/**
* @inheritdoc
*/
public function get_debug_info() {
$mg_text = array();
$options = new \WPMailSMTP\Options();
$mailgun = $options->get_group( 'mailgun' );
$mg_text[] = '<strong>Api Key / Domain:</strong> ' . ( ! empty( $mailgun['api_key'] ) && ! empty( $mailgun['domain'] ) ? 'Yes' : 'No' );
return implode( '<br>', $mg_text );
}
/**
* @inheritdoc
*/
public function is_mailer_complete() {
$options = $this->options->get_group( $this->mailer );
// API key is the only required option.
if (
! empty( $options['api_key'] ) &&
! empty( $options['domain'] )
) {
return true;
}
return false;
}
}
<?php
namespace WPMailSMTP\Providers\Mailgun;
use WPMailSMTP\Debug;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
/**
* Class Mailer.
*
* @since 1.0.0
*/
class Mailer extends MailerAbstract {
/**
* Which response code from HTTP provider is considered to be successful?
*
* @since 1.0.0
*
* @var int
*/
protected $email_sent_code = 200;
/**
* API endpoint used for sites from all regions.
*
* @since 1.4.0
*
* @var string
*/
const API_BASE_US = 'https://api.mailgun.net/v3/';
/**
* API endpoint used for sites from EU region.
*
* @since 1.4.0
*
* @var string
*/
const API_BASE_EU = 'https://api.eu.mailgun.net/v3/';
/**
* URL to make an API request to.
*
* @since 1.0.0
*
* @var string
*/
protected $url = '';
/**
* @inheritdoc
*/
public function __construct( $phpmailer ) {
// Default value should be defined before the parent class contructor fires.
$this->url = self::API_BASE_US;
// 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.
if ( 'EU' === $this->options->get( $this->mailer, 'region' ) ) {
$this->url = self::API_BASE_EU;
}
/*
* Append the url with a domain,
* to avoid passing the domain name as a query parameter with all requests.
*/
$this->url .= sanitize_text_field( $this->options->get( $this->mailer, 'domain' ) . '/messages' );
$this->set_header( 'Authorization', 'Basic ' . base64_encode( 'api:' . $this->options->get( $this->mailer, 'api_key' ) ) );
}
/**
* @inheritdoc
*/
public function set_from( $email, $name = '' ) {
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
if ( ! empty( $name ) ) {
$this->set_body_param(
array(
'from' => $name . ' <' . $email . '>',
)
);
} else {
$this->set_body_param(
array(
'from' => $email,
)
);
}
}
/**
* @inheritdoc
*/
public function set_recipients( $recipients ) {
if ( empty( $recipients ) ) {
return;
}
$default = array( 'to', 'cc', 'bcc' );
foreach ( $recipients as $kind => $emails ) {
if (
! in_array( $kind, $default, true ) ||
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$data = array();
foreach ( $emails as $email ) {
$addr = isset( $email[0] ) ? $email[0] : false;
$name = isset( $email[1] ) ? $email[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
if ( ! empty( $name ) ) {
$data[] = $name . ' <' . $addr . '>';
} else {
$data[] = $addr;
}
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
$kind => implode( ', ', $data ),
)
);
}
}
}
/**
* @inheritdoc
*/
public function set_content( $content ) {
if ( is_array( $content ) ) {
$default = array( 'text', 'html' );
foreach ( $content as $type => $mail ) {
if (
! in_array( $type, $default, true ) ||
empty( $mail )
) {
continue;
}
$this->set_body_param(
array(
$type => $mail,
)
);
}
} else {
$type = 'html';
if ( $this->phpmailer->ContentType === 'text/plain' ) {
$type = 'text';
}
if ( ! empty( $content ) ) {
$this->set_body_param(
array(
$type => $content,
)
);
}
}
}
/**
* Redefine the way custom headers are process for this mailer - they should be in body.
*
* @since 1.5.0
*
* @param array $headers
*/
public function set_headers( $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 );
}
// Add custom PHPMailer-specific header.
$this->set_body_header( 'X-Mailer', 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
}
/**
* This mailer supports email-related custom headers inside a body of the message with a special prefix "h:".
*
* @since 1.5.0
*/
public function set_body_header( $name, $value ) {
$name = sanitize_text_field( $name );
$this->set_body_param(
array(
'h:' . $name => WP::sanitize_value( $value ),
)
);
}
/**
* It's the last one, so we can modify the whole body.
*
* @since 1.0.0
*
* @param array $attachments
*/
public function set_attachments( $attachments ) {
if ( empty( $attachments ) ) {
return;
}
$payload = '';
$data = array();
foreach ( $attachments as $attachment ) {
$file = false;
/*
* We are not using WP_Filesystem API as we can't reliably work with it.
* It is not always available, same as credentials for FTP.
*/
try {
if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
$file = file_get_contents( $attachment[0] );
}
}
catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = array(
'content' => $file,
'name' => $attachment[2],
);
}
if ( ! empty( $data ) ) {
// First, generate a boundary for the multipart message.
$boundary = base_convert( uniqid( 'boundary', true ), 10, 36 );
// Iterate through pre-built params and build a payload.
foreach ( $this->body as $key => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $child_key => $child_value ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . "\"\r\n\r\n";
$payload .= $child_value;
$payload .= "\r\n";
}
} else {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n";
$payload .= $value;
$payload .= "\r\n";
}
}
// Now iterate through our attachments, and add them too.
foreach ( $data as $key => $attachment ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="attachment[' . $key . ']"; filename="' . $attachment['name'] . '"' . "\r\n\r\n";
$payload .= $attachment['content'];
$payload .= "\r\n";
}
$payload .= '--' . $boundary . '--';
// Redefine the body the "dirty way".
$this->body = $payload;
$this->set_header( 'Content-Type', 'multipart/form-data; boundary=' . $boundary );
}
}
/**
* @inheritdoc
*/
public function set_reply_to( $reply_to ) {
if ( empty( $reply_to ) ) {
return;
}
$data = array();
foreach ( $reply_to as $key => $emails ) {
if (
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$addr = isset( $emails[0] ) ? $emails[0] : false;
$name = isset( $emails[1] ) ? $emails[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
if ( ! empty( $name ) ) {
$data[] = $name . ' <' . $addr . '>';
} else {
$data[] = $addr;
}
}
if ( ! empty( $data ) ) {
$this->set_body_param(
array(
'h:Reply-To' => implode( ',', $data ),
)
);
}
}
/**
* @inheritdoc
*/
public function set_return_path( $email ) {
if (
$this->options->get( 'mail', 'return_path' ) !== true ||
! filter_var( $email, FILTER_VALIDATE_EMAIL )
) {
return;
}
$this->set_body_param(
array(
'sender' => $email,
)
);
}
/**
* 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.
*
* @since 1.2.0
*
* @return string
*/
protected function get_response_error() {
$body = (array) wp_remote_retrieve_body( $this->response );
$error_text = array();
if ( ! empty( $body['message'] ) ) {
if ( is_string( $body['message'] ) ) {
$error_text[] = $body['message'];
} else {
$error_text[] = \json_encode( $body['message'] );
}
} elseif ( ! empty( $body[0] ) ) {
if ( is_string( $body[0] ) ) {
$error_text[] = $body[0];
} else {
$error_text[] = \json_encode( $body[0] );
}
}
return implode( '<br>', array_map( 'esc_textarea', $error_text ) );
}
/**
* @inheritdoc
*/
public function get_debug_info() {
$mg_text = array();
$options = new \WPMailSMTP\Options();
$mailgun = $options->get_group( 'mailgun' );
$mg_text[] = '<strong>Api Key / Domain:</strong> ' . ( ! empty( $mailgun['api_key'] ) && ! empty( $mailgun['domain'] ) ? 'Yes' : 'No' );
return implode( '<br>', $mg_text );
}
/**
* @inheritdoc
*/
public function is_mailer_complete() {
$options = $this->options->get_group( $this->mailer );
// API key is the only required option.
if (
! empty( $options['api_key'] ) &&
! empty( $options['domain'] )
) {
return true;
}
return false;
}
}

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;
}
$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;
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
}
array_push( $data[ $type ], $holder );
}
if ( ! empty( $recipients['to'] ) ) {
$data['to'] = $this->prepare_list_of_to_emails( $recipients['to'] );
}
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
);
}
if ( ! empty( $recipients['cc'] ) ) {
$data['cc'] = $this->prepare_list_of_emails( $recipients['cc'] );
}
if ( ! empty( $recipients['bcc'] ) ) {
$data['bcc'] = $this->prepare_list_of_emails( $recipients['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 {
$this->set_body_param(
array(
'content' => array( $data ),
)
);
if ( ! empty( $content['html'] ) ) {
$html = $content['html'];
} elseif ( ! empty( $content['text'] ) ) {
$html = nl2br( $content['text'] );
}
}
$this->set_body_param(
[
'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 );
/**
* This mailer supports email-related custom headers inside a body of the message.
*
* @since 1.8.0
*
* @param string $name
* @param string $value
*/
public function set_body_header( $name, $value ) {
$name = sanitize_text_field( $name );
if ( empty( $name ) ) {
return;
if ( ! empty( $valid_headers ) ) {
$this->set_body_personalizations( [ 'headers' => $valid_headers ] );
}
$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.
* 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 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;
}
$addr = isset( $emails[0] ) ? $emails[0] : false;
$name = isset( $emails[1] ) ? $emails[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
$data['email'] = $addr;
if ( ! empty( $name ) ) {
$data['name'] = $name;
}
if ( empty( $email_array[0] ) ) {
return;
}
if ( ! empty( $data ) ) {
$email = $email_array[0];
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
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

@ -1,393 +1,391 @@
<?php
namespace WPMailSMTP\Providers\Sendinblue;
use WPMailSMTP\Debug;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
/**
* Class Mailer.
*
* @since 1.6.0
*/
class Mailer extends MailerAbstract {
/**
* Which response code from HTTP provider is considered to be successful?
*
* @since 1.6.0
*
* @var int
*/
protected $email_sent_code = 201;
/**
* URL to make an API request to.
* Not actually used, because we use a lib to make requests.
*
* @since 1.6.0
*
* @var string
*/
protected $url = 'https://api.sendinblue.com/v3';
/**
* The list of allowed attachment files extensions.
*
* @see https://developers.sendinblue.com/reference#sendTransacEmail_attachment__title
*
* @since 1.6.0
*
* @var array
*/
// @formatter:off
protected $allowed_attach_ext = array( 'xlsx', 'xls', 'ods', 'docx', 'docm', 'doc', 'csv', 'pdf', 'txt', 'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'rtf', 'bmp', 'cgm', 'css', 'shtml', 'html', 'htm', 'zip', 'xml', 'ppt', 'pptx', 'tar', 'ez', 'ics', 'mobi', 'msg', 'pub', 'eps', 'odt', 'mp3', 'm4a', 'm4v', 'wma', 'ogg', 'flac', 'wav', 'aif', 'aifc', 'aiff', 'mp4', 'mov', 'avi', 'mkv', 'mpeg', 'mpg', 'wmv' );
// @formatter:on
/**
* Mailer constructor.
*
* @since 1.6.0
*
* @param \WPMailSMTP\MailCatcher $phpmailer
*/
public function __construct( $phpmailer ) {
parent::__construct( $phpmailer );
if ( ! $this->is_php_compatible() ) {
return;
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*/
public function set_header( $name, $value ) {
$name = sanitize_text_field( $name );
$this->body['headers'][ $name ] = WP::sanitize_value( $value );
}
/**
* Set the From information for an email.
*
* @since 1.6.0
*
* @param string $email
* @param string $name
*/
public function set_from( $email, $name ) {
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
$this->body['sender'] = array(
'email' => $email,
'name' => ! empty( $name ) ? WP::sanitize_value( $name ) : '',
);
}
/**
* Set email recipients: to, cc, bcc.
*
* @since 1.6.0
*
* @param array $recipients
*/
public function set_recipients( $recipients ) {
if ( empty( $recipients ) ) {
return;
}
// Allow for now only these recipient types.
$default = array( 'to', 'cc', 'bcc' );
$data = array();
foreach ( $recipients as $type => $emails ) {
if (
! in_array( $type, $default, true ) ||
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$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;
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
}
array_push( $data[ $type ], $holder );
}
}
foreach ( $data as $type => $type_recipients ) {
$this->body[ $type ] = $type_recipients;
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*/
public function set_subject( $subject ) {
$this->body['subject'] = $subject;
}
/**
* Set email content.
*
* @since 1.6.0
*
* @param string|array $content
*/
public function set_content( $content ) {
if ( empty( $content ) ) {
return;
}
if ( is_array( $content ) ) {
if ( ! empty( $content['text'] ) ) {
$this->body['textContent'] = $content['text'];
}
if ( ! empty( $content['html'] ) ) {
$this->body['htmlContent'] = $content['html'];
}
} else {
if ( $this->phpmailer->ContentType === 'text/plain' ) {
$this->body['textContent'] = $content;
} else {
$this->body['htmlContent'] = $content;
}
}
}
/**
* Doesn't support this.
*
* @since 1.6.0
*
* @param string $email
*/
public function set_return_path( $email ) {
}
/**
* Set the Reply To headers if not set already.
*
* @since 1.6.0
*
* @param array $emails
*/
public function set_reply_to( $emails ) {
if ( empty( $emails ) ) {
return;
}
$data = array();
foreach ( $emails as $user ) {
$holder = array();
$addr = isset( $user[0] ) ? $user[0] : false;
$name = isset( $user[1] ) ? $user[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
}
$data[] = $holder;
}
if ( ! empty( $data ) ) {
$this->body['replyTo'] = $data[0];
}
}
/**
* Set attachments for an email.
*
* @since 1.6.0
*
* @param array $attachments
*/
public function set_attachments( $attachments ) {
if ( empty( $attachments ) ) {
return;
}
foreach ( $attachments as $attachment ) {
$file = false;
/*
* We are not using WP_Filesystem API as we can't reliably work with it.
* It is not always available, same as credentials for FTP.
*/
try {
if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
$ext = pathinfo( $attachment[0], PATHINFO_EXTENSION );
if ( in_array( $ext, $this->allowed_attach_ext, true ) ) {
$file = file_get_contents( $attachment[0] ); // phpcs:ignore
}
}
}
catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$this->body['attachment'][] = array(
'name' => $attachment[2],
'content' => base64_encode( $file ),
);
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*
* @return \SendinBlue\Client\Model\SendSmtpEmail
*/
public function get_body() {
return new \SendinBlue\Client\Model\SendSmtpEmail( $this->body );
}
/**
* Use a library to send emails.
*
* @since 1.6.0
*/
public function send() {
try {
$api = new Api();
$response = $api->get_smtp_client()->sendTransacEmail( $this->get_body() );
$this->process_response( $response );
}
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 )
);
}
}
catch ( \Exception $e ) {
Debug::set(
'Mailer: Sendinblue' . "\r\n" .
$e->getMessage()
);
return;
}
}
/**
* Save response from the API to use it later.
* All the actually response processing is done in send() method,
* because SendinBlue throws exception if any error occurs.
*
* @since 1.6.0
*
* @param \SendinBlue\Client\Model\CreateSmtpEmail $response
*/
protected function process_response( $response ) {
$this->response = $response;
}
/**
* Check whether the email was sent.
*
* @since 1.6.0
*
* @return bool
*/
public function is_email_sent() {
$is_sent = false;
if ( $this->response instanceof \SendinBlue\Client\Model\CreateSmtpEmail ) {
$is_sent = $this->response->valid();
}
// Clear debug messages if email is successfully sent.
if ( $is_sent ) {
Debug::clear();
}
return $is_sent;
}
/**
* @inheritdoc
*
* @since 1.6.0
*/
public function get_debug_info() {
$mailjet_text[] = '<strong>API Key:</strong> ' . ( $this->is_mailer_complete() ? 'Yes' : 'No' );
return implode( '<br>', $mailjet_text );
}
/**
* @inheritdoc
*
* @since 1.6.0
*/
public function is_mailer_complete() {
$options = $this->options->get_group( $this->mailer );
// API key is the only required option.
if ( ! empty( $options['api_key'] ) ) {
return true;
}
return false;
}
}
<?php
namespace WPMailSMTP\Providers\Sendinblue;
use WPMailSMTP\Debug;
use WPMailSMTP\MailCatcherInterface;
use WPMailSMTP\Providers\MailerAbstract;
use WPMailSMTP\WP;
/**
* Class Mailer.
*
* @since 1.6.0
*/
class Mailer extends MailerAbstract {
/**
* Which response code from HTTP provider is considered to be successful?
*
* @since 1.6.0
*
* @var int
*/
protected $email_sent_code = 201;
/**
* URL to make an API request to.
* Not actually used, because we use a lib to make requests.
*
* @since 1.6.0
*
* @var string
*/
protected $url = 'https://api.sendinblue.com/v3';
/**
* The list of allowed attachment files extensions.
*
* @see https://developers.sendinblue.com/reference#sendTransacEmail_attachment__title
*
* @since 1.6.0
*
* @var array
*/
// @formatter:off
protected $allowed_attach_ext = array( 'xlsx', 'xls', 'ods', 'docx', 'docm', 'doc', 'csv', 'pdf', 'txt', 'gif', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'rtf', 'bmp', 'cgm', 'css', 'shtml', 'html', 'htm', 'zip', 'xml', 'ppt', 'pptx', 'tar', 'ez', 'ics', 'mobi', 'msg', 'pub', 'eps', 'odt', 'mp3', 'm4a', 'm4v', 'wma', 'ogg', 'flac', 'wav', 'aif', 'aifc', 'aiff', 'mp4', 'mov', 'avi', 'mkv', 'mpeg', 'mpg', 'wmv' );
// @formatter:on
/**
* Mailer constructor.
*
* @since 1.6.0
*
* @param MailCatcherInterface $phpmailer The MailCatcher object.
*/
public function __construct( $phpmailer ) {
parent::__construct( $phpmailer );
if ( ! $this->is_php_compatible() ) {
return;
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*/
public function set_header( $name, $value ) {
$name = sanitize_text_field( $name );
$this->body['headers'][ $name ] = WP::sanitize_value( $value );
}
/**
* Set the From information for an email.
*
* @since 1.6.0
*
* @param string $email
* @param string $name
*/
public function set_from( $email, $name ) {
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
return;
}
$this->body['sender'] = array(
'email' => $email,
'name' => ! empty( $name ) ? WP::sanitize_value( $name ) : '',
);
}
/**
* Set email recipients: to, cc, bcc.
*
* @since 1.6.0
*
* @param array $recipients
*/
public function set_recipients( $recipients ) {
if ( empty( $recipients ) ) {
return;
}
// Allow for now only these recipient types.
$default = array( 'to', 'cc', 'bcc' );
$data = array();
foreach ( $recipients as $type => $emails ) {
if (
! in_array( $type, $default, true ) ||
empty( $emails ) ||
! is_array( $emails )
) {
continue;
}
$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;
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
}
array_push( $data[ $type ], $holder );
}
}
foreach ( $data as $type => $type_recipients ) {
$this->body[ $type ] = $type_recipients;
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*/
public function set_subject( $subject ) {
$this->body['subject'] = $subject;
}
/**
* Set email content.
*
* @since 1.6.0
*
* @param string|array $content
*/
public function set_content( $content ) {
if ( empty( $content ) ) {
return;
}
if ( is_array( $content ) ) {
if ( ! empty( $content['text'] ) ) {
$this->body['textContent'] = $content['text'];
}
if ( ! empty( $content['html'] ) ) {
$this->body['htmlContent'] = $content['html'];
}
} else {
if ( $this->phpmailer->ContentType === 'text/plain' ) {
$this->body['textContent'] = $content;
} else {
$this->body['htmlContent'] = $content;
}
}
}
/**
* Doesn't support this.
*
* @since 1.6.0
*
* @param string $email
*/
public function set_return_path( $email ) {
}
/**
* Set the Reply To headers if not set already.
*
* @since 1.6.0
*
* @param array $emails
*/
public function set_reply_to( $emails ) {
if ( empty( $emails ) ) {
return;
}
$data = array();
foreach ( $emails as $user ) {
$holder = array();
$addr = isset( $user[0] ) ? $user[0] : false;
$name = isset( $user[1] ) ? $user[1] : false;
if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
continue;
}
$holder['email'] = $addr;
if ( ! empty( $name ) ) {
$holder['name'] = $name;
}
$data[] = $holder;
}
if ( ! empty( $data ) ) {
$this->body['replyTo'] = $data[0];
}
}
/**
* Set attachments for an email.
*
* @since 1.6.0
*
* @param array $attachments
*/
public function set_attachments( $attachments ) {
if ( empty( $attachments ) ) {
return;
}
foreach ( $attachments as $attachment ) {
$file = false;
/*
* We are not using WP_Filesystem API as we can't reliably work with it.
* It is not always available, same as credentials for FTP.
*/
try {
if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
$ext = pathinfo( $attachment[0], PATHINFO_EXTENSION );
if ( in_array( $ext, $this->allowed_attach_ext, true ) ) {
$file = file_get_contents( $attachment[0] ); // phpcs:ignore
}
}
}
catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$this->body['attachment'][] = array(
'name' => $attachment[2],
'content' => base64_encode( $file ),
);
}
}
/**
* @inheritDoc
*
* @since 1.6.0
*
* @return \SendinBlue\Client\Model\SendSmtpEmail
*/
public function get_body() {
return new \SendinBlue\Client\Model\SendSmtpEmail( $this->body );
}
/**
* Use a library to send emails.
*
* @since 1.6.0
*/
public function send() {
try {
$api = new Api();
$response = $api->get_smtp_client()->sendTransacEmail( $this->get_body() );
$this->process_response( $response );
} catch ( \SendinBlue\Client\ApiException $e ) {
$error = json_decode( $e->getResponseBody() );
if ( json_last_error() === JSON_ERROR_NONE && ! empty( $error ) ) {
$message = '[' . sanitize_key( $error->code ) . ']: ' . esc_html( $error->message );
} else {
$message = $e->getMessage();
}
Debug::set( 'Mailer: Sendinblue' . PHP_EOL . $message );
} catch ( \Exception $e ) {
Debug::set( 'Mailer: Sendinblue' . PHP_EOL . $e->getMessage() );
return;
}
}
/**
* Save response from the API to use it later.
* All the actually response processing is done in send() method,
* because SendinBlue throws exception if any error occurs.
*
* @since 1.6.0
*
* @param \SendinBlue\Client\Model\CreateSmtpEmail $response
*/
protected function process_response( $response ) {
$this->response = $response;
}
/**
* Check whether the email was sent.
*
* @since 1.6.0
*
* @return bool
*/
public function is_email_sent() {
$is_sent = false;
if ( $this->response instanceof \SendinBlue\Client\Model\CreateSmtpEmail ) {
$is_sent = $this->response->valid();
}
// Clear debug messages if email is successfully sent.
if ( $is_sent ) {
Debug::clear();
}
return $is_sent;
}
/**
* @inheritdoc
*
* @since 1.6.0
*/
public function get_debug_info() {
$mailjet_text[] = '<strong>API Key:</strong> ' . ( $this->is_mailer_complete() ? 'Yes' : 'No' );
return implode( '<br>', $mailjet_text );
}
/**
* @inheritdoc
*
* @since 1.6.0
*/
public function is_mailer_complete() {
$options = $this->options->get_group( $this->mailer );
// API key is the only required option.
if ( ! empty( $options['api_key'] ) ) {
return true;
}
return false;
}
}