Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<?php
namespace FluentMail\App;
use FluentMail\Includes\Core\Application;
class App
{
public static function getInstance($module = null)
{
$app = Application::getInstance();
if ($module) {
return $app[$module];
}
return $app;
}
public static function __callStatic($method, $params)
{
return static::getInstance($method);
}
}

View File

@@ -0,0 +1,25 @@
<?php
$singletons = [
'manager' => 'FluentMail\App\Services\Mailer\Manager',
'smtp' => 'FluentMail\App\Services\Mailer\Providers\Smtp\Handler',
'ses' => 'FluentMail\App\Services\Mailer\Providers\AmazonSes\Handler',
'mailgun' => 'FluentMail\App\Services\Mailer\Providers\Mailgun\Handler',
'sendgrid' => 'FluentMail\App\Services\Mailer\Providers\SendGrid\Handler',
'pepipost' => 'FluentMail\App\Services\Mailer\Providers\PepiPost\Handler',
'sparkpost' => 'FluentMail\App\Services\Mailer\Providers\SparkPost\Handler',
'default' => 'FluentMail\App\Services\Mailer\Providers\DefaultMail\Handler',
'sendinblue' => 'FluentMail\App\Services\Mailer\Providers\SendInBlue\Handler',
'gmail' => 'FluentMail\App\Services\Mailer\Providers\Gmail\Handler',
'outlook' => 'FluentMail\App\Services\Mailer\Providers\Outlook\Handler',
'postmark' => 'FluentMail\App\Services\Mailer\Providers\Postmark\Handler',
'elasticmail' => 'FluentMail\App\Services\Mailer\Providers\ElasticMail\Handler',
'smtp2go' => 'FluentMail\App\Services\Mailer\Providers\Smtp2Go\Handler',
];
foreach ($singletons as $key => $className) {
$app->alias($className, $key);
$app->singleton($className, function($app) use ($className) {
return new $className();
});
}

View File

@@ -0,0 +1,894 @@
<?php
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\Providers\AmazonSes\SimpleEmailService;
use FluentMail\App\Services\Mailer\Providers\Factory;
if (!function_exists('fluentMail')) {
/**
* Returns an instance of the FluentMail\App\App class.
*
* @param string|null $module The module name (optional).
* @return FluentMail\App\App The instance of the FluentMail\App\App class.
*/
function fluentMail($module = null) {
return FluentMail\App\App::getInstance($module);
}
}
if (!function_exists('fluentMailMix')) {
/**
* Generate the mixed URL for the given asset path.
*
* @param string $path The asset path.
* @param string $manifestDirectory The directory where the manifest file is located (optional).
* @return string The mixed URL for the asset.
*/
function fluentMailMix($path, $manifestDirectory = '') {
return fluentMail('url.assets') . ltrim($path, '/');
}
}
if (!function_exists('fluentMailAssetUrl')) {
/**
* Returns the URL for the assets of the Fluent Mail plugin.
*
* @param string|null $path The path to the asset file (optional).
* @return string The URL for the assets of the Fluent Mail plugin.
*/
function fluentMailAssetUrl($path = null) {
$assetUrl = fluentMail('url.assets');
return $path ? ($assetUrl . $path) : $assetUrl;
}
}
if (!function_exists('fluentMailIsListedSenderEmail')) {
/**
* Check if the given email is listed as a sender email in the Fluent SMTP plugin settings.
*
* @param string $email The email address to check.
* @return bool Returns true if the email is listed as a sender email, false otherwise.
*/
function fluentMailIsListedSenderEmail($email) {
static $settings;
if (!$settings) {
$settings = fluentMailGetSettings();
}
if (!$settings) {
return false;
}
return !empty($settings['mappings'][$email]);
}
}
if (!function_exists('fluentMailDefaultConnection')) {
/**
* Retrieves the default mail connection settings.
*
* @return array The default mail connection settings.
*/
function fluentMailDefaultConnection() {
static $defaultConnection;
if ($defaultConnection) {
return $defaultConnection;
}
$settings = fluentMailGetSettings();
if (!$settings) {
return [];
}
if (
isset($settings['misc']['default_connection']) &&
isset($settings['connections'][$settings['misc']['default_connection']])
) {
$default = $settings['misc']['default_connection'];
$defaultConnection = $settings['connections'][$default]['provider_settings'];
} else if (count($settings['connections'])) {
$connection = reset($settings['connections']);
$defaultConnection = $connection['provider_settings'];
} else {
$defaultConnection = [];
}
return $defaultConnection;
}
}
if (!function_exists('fluentMailgetConnection')) {
/**
* Get the connection for the given email address.
*
* @param string $email The email address.
* @return Connection The connection object.
*/
function fluentMailgetConnection($email) {
$factory = fluentMail(Factory::class);
if (!($connection = $factory->get($email))) {
$connection = fluentMailDefaultConnection();
}
return $connection;
}
}
if (!function_exists('fluentMailGetProvider')) {
/**
* Get the FluentMail provider for the given email address.
*
* @param string $fromEmail The email address to get the provider for.
* @param bool $cacheClear Whether to clear the provider cache.
* @return \FluentMail\App\Services\Mailer\Providers\Simulator\Handler|false The FluentMail provider for the given email address, or false if no provider is found.
*/
function fluentMailGetProvider($fromEmail, $cacheClear = false) {
static $providers = [];
if (isset($providers[$fromEmail]) && !$cacheClear) {
return $providers[$fromEmail];
}
$manager = fluentMail(Manager::class);
$misc = $manager->getSettings('misc');
if ((!empty($misc['simulate_emails']) && $misc['simulate_emails'] == 'yes') || (defined('FLUENTMAIL_SIMULATE_EMAILS') && FLUENTMAIL_SIMULATE_EMAILS)) {
$providers[$fromEmail] = new FluentMail\App\Services\Mailer\Providers\Simulator\Handler();
return $providers[$fromEmail];
}
$mappings = $manager->getSettings('mappings');
$connection = false;
if (isset($mappings[$fromEmail])) {
$connectionId = $mappings[$fromEmail];
$connections = $manager->getSettings('connections');
if (isset($connections[$connectionId])) {
$connection = $connections[$connectionId]['provider_settings'];
}
}
if (!$connection) {
$connection = fluentMailDefaultConnection();
if ($connection && \FluentMail\Includes\Support\Arr::get($connection, 'force_from_email') != 'no') {
$connection['force_from_email_id'] = $connection['sender_email'];
}
}
if ($connection) {
$factory = fluentMail(Factory::class);
$driver = $factory->make($connection['provider']);
$driver->setSettings($connection);
$providers[$fromEmail] = $driver;
} else {
$providers[$fromEmail] = false;
}
return $providers[$fromEmail];
}
}
if (!function_exists('fluentMailSesConnection')) {
/**
* Establishes a connection to the Fluent SMTP service using the Amazon Simple Email Service (SES).
*
* @param array $connection The connection details including sender email, access key, secret key, and region.
* @return SimpleEmailService The SES driver instance for the specified sender email.
*/
function fluentMailSesConnection($connection) {
static $drivers = [];
if (isset($drivers[$connection['sender_email']])) {
return $drivers[$connection['sender_email']];
}
$region = 'email.' . $connection['region'] . '.amazonaws.com';
$ses = new SimpleEmailService(
$connection['access_key'],
$connection['secret_key'],
$region,
false
);
$drivers[$connection['sender_email']] = $ses;
return $drivers[$connection['sender_email']];
}
}
if (!function_exists('fluentMailSend')) {
/**
* Sends an email using the wp_mail() function with additional filters and pre-send checks.
*
* @param string|array $to Array or comma-separated list of email addresses to send the message to.
* @param string $subject The subject of the email.
* @param string $message The message content of the email.
* @param string|array $headers Additional headers for the email.
* @param array $attachments Paths to files to attach to the email.
*
* @return bool|null Returns true if the email was successfully sent, false if sending was preempted, or null if there was an error.
*
*
* @filter wp_mail
* @filter pre_wp_mail
*/
function fluentMailSend($to, $subject, $message, $headers = '', $attachments = array()) {
// Compact the input, apply the filters, and extract them back out.
/**
* Filters the wp_mail() arguments.
*
* @param array $args A compacted array of wp_mail() arguments, including the "to" email,
* subject, message, headers, and attachments values.
* @since 2.2.0
*
*/
$atts = apply_filters(
'wp_mail', compact('to', 'subject', 'message', 'headers', 'attachments')
);
/**
* Filters whether to preempt sending an email.
*
* Returning a non-null value will short-circuit {@see wp_mail()}, returning
* that value instead. A boolean return value should be used to indicate whether
* the email was successfully sent.
*
* @param null|bool $return Short-circuit return value.
* @param array $atts {
* Array of the `wp_mail()` arguments.
*
* @type string|string[] $to Array or comma-separated list of email addresses to send message.
* @type string $subject Email subject.
* @type string $message Message contents.
* @type string|string[] $headers Additional headers.
* @type string|string[] $attachments Paths to files to attach.
* }
* @since 5.7.0
*
*/
$pre_wp_mail = apply_filters('pre_wp_mail', null, $atts);
if (null !== $pre_wp_mail) {
return $pre_wp_mail;
}
if (isset($atts['to'])) {
$to = $atts['to'];
}
if (!is_array($to)) {
$to = explode(',', $to);
}
if (isset($atts['subject'])) {
$subject = $atts['subject'];
}
if (isset($atts['message'])) {
$message = $atts['message'];
}
if (isset($atts['headers'])) {
$headers = $atts['headers'];
}
if (isset($atts['attachments'])) {
$attachments = $atts['attachments'];
}
if (!is_array($attachments)) {
$attachments = explode("\n", str_replace("\r\n", "\n", $attachments));
}
global $phpmailer;
// (Re)create it, if it's gone missing.
if (!($phpmailer instanceof PHPMailer\PHPMailer\PHPMailer)) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
$phpmailer = new PHPMailer\PHPMailer\PHPMailer(true);
$phpmailer::$validator = static function ($email) {
return (bool) is_email($email);
};
}
// Headers.
$cc = array();
$bcc = array();
$reply_to = array();
if (empty($headers)) {
$headers = array();
} else {
if (!is_array($headers)) {
// Explode the headers out, so this function can take
// both string headers and an array of headers.
$tempheaders = explode("\n", str_replace("\r\n", "\n", $headers));
} else {
$tempheaders = $headers;
}
$headers = array();
// If it's actually got contents.
if (!empty($tempheaders)) {
// Iterate through the raw headers.
foreach ((array) $tempheaders as $header) {
if (strpos($header, ':') === false) {
if (false !== stripos($header, 'boundary=')) {
$parts = preg_split('/boundary=/i', trim($header));
$boundary = trim(str_replace(array("'", '"'), '', $parts[1]));
}
continue;
}
// Explode them out.
list($name, $content) = explode(':', trim($header), 2);
// Cleanup crew.
$name = trim($name);
$content = trim($content);
switch (strtolower($name)) {
// Mainly for legacy -- process a "From:" header if it's there.
case 'from':
$bracket_pos = strpos($content, '<');
if (false !== $bracket_pos) {
// Text before the bracketed email is the "From" name.
if ($bracket_pos > 0) {
$from_name = substr($content, 0, $bracket_pos - 1);
$from_name = str_replace('"', '', $from_name);
$from_name = trim($from_name);
}
$from_email = substr($content, $bracket_pos + 1);
$from_email = str_replace('>', '', $from_email);
$from_email = trim($from_email);
// Avoid setting an empty $from_email.
} elseif ('' !== trim($content)) {
$from_email = trim($content);
}
break;
case 'content-type':
if (strpos($content, ';') !== false) {
list($type, $charset_content) = explode(';', $content);
$content_type = trim($type);
if (false !== stripos($charset_content, 'charset=')) {
$charset = trim(str_replace(array('charset=', '"'), '', $charset_content));
} elseif (false !== stripos($charset_content, 'boundary=')) {
$boundary = trim(str_replace(array('BOUNDARY=', 'boundary=', '"'), '', $charset_content));
$charset = '';
}
// Avoid setting an empty $content_type.
} elseif ('' !== trim($content)) {
$content_type = trim($content);
}
break;
case 'cc':
$cc = array_merge((array) $cc, explode(',', $content));
break;
case 'bcc':
$bcc = array_merge((array) $bcc, explode(',', $content));
break;
case 'reply-to':
$reply_to = array_merge((array) $reply_to, explode(',', $content));
break;
default:
// Add it to our grand headers array.
$headers[trim($name)] = trim($content);
break;
}
}
}
}
// Empty out the values that may be set.
$phpmailer->clearAllRecipients();
$phpmailer->clearAttachments();
$phpmailer->clearCustomHeaders();
$phpmailer->clearReplyTos();
$phpmailer->Body = '';
$phpmailer->AltBody = '';
/*
* If we don't have an email from the input headers, default to wordpress@$sitename
* Some hosts will block outgoing mail from this address if it doesn't exist,
* but there's no easy alternative. Defaulting to admin_email might appear to be
* another option, but some hosts may refuse to relay mail from an unknown domain.
* See https://core.trac.wordpress.org/ticket/5007.
*/
$defaultConnection = false;
if (!isset($from_email)) {
$defaultConnection = fluentMailDefaultConnection();
if (!empty($defaultConnection['sender_email'])) {
$from_email = $defaultConnection['sender_email'];
} else {
// Get the site domain and get rid of www.
$sitename = wp_parse_url(network_home_url(), PHP_URL_HOST);
if ('www.' === substr($sitename, 0, 4)) {
$sitename = substr($sitename, 4);
}
$from_email = 'wordpress@' . $sitename;
}
}
// Set "From" name and email.
// If we don't have a name from the input headers.
if (!isset($from_name)) {
if ($defaultConnection && !empty($defaultConnection['sender_name'])) {
$from_name = $defaultConnection['sender_name'];
} else {
$provider = fluentMailGetProvider($from_email);
if ($provider && !empty($provider->getSetting('sender_name'))) {
$from_name = $provider->getSetting('sender_name');
} else {
$from_name = 'WordPress';
}
}
}
if (!apply_filters('fluentsmtp_disable_from_name_email', false)) {
/**
* Filters the email address to send from.
*
* @param string $from_email Email address to send from.
* @since 2.2.0
*
*/
$from_email = apply_filters('wp_mail_from', $from_email);
}
/**
* Filters the name to associate with the "from" email address.
*
* @param string $from_name Name associated with the "from" email address.
* @since 2.3.0
*
*/
$from_name = apply_filters('wp_mail_from_name', $from_name);
try {
$phpmailer->setFrom($from_email, $from_name, false);
} catch (PHPMailer\PHPMailer\Exception $e) {
$mail_error_data = compact('to', 'subject', 'message', 'headers', 'attachments');
$mail_error_data['phpmailer_exception_code'] = $e->getCode();
/**
* This filter is documented in wp-includes/pluggable.php
*
* @param WP_Error $error A WP_Error object containing the error message.
* @param array $mail_error_data An array of additional error data.
* @return void
*/
do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
return false;
}
// Set mail's subject and body.
$phpmailer->Subject = $subject;
$phpmailer->Body = $message;
// Set destination addresses, using appropriate methods for handling addresses.
$address_headers = compact('to', 'cc', 'bcc', 'reply_to');
foreach ($address_headers as $address_header => $addresses) {
if (empty($addresses)) {
continue;
}
foreach ((array) $addresses as $address) {
try {
// Break $recipient into name and address parts if in the format "Foo <bar@baz.com>".
$recipient_name = '';
if (preg_match('/(.*)<(.+)>/', $address, $matches)) {
if (count($matches) == 3) {
$recipient_name = $matches[1];
$address = $matches[2];
}
}
switch ($address_header) {
case 'to':
$phpmailer->addAddress($address, $recipient_name);
break;
case 'cc':
$phpmailer->addCc($address, $recipient_name);
break;
case 'bcc':
$phpmailer->addBcc($address, $recipient_name);
break;
case 'reply_to':
$phpmailer->addReplyTo($address, $recipient_name);
break;
}
} catch (PHPMailer\PHPMailer\Exception $e) {
continue;
}
}
}
// Set to use PHP's mail().
$phpmailer->isMail();
// Set Content-Type and charset.
// If we don't have a content-type from the input headers.
if (!isset($content_type)) {
$content_type = 'text/plain';
}
/**
* Filters the wp_mail() content type.
*
* @param string $content_type Default wp_mail() content type.
* @since 2.3.0
*
*/
$content_type = apply_filters('wp_mail_content_type', $content_type);
$phpmailer->ContentType = $content_type;
// If we don't have a charset from the input headers.
if (!isset($charset)) {
$charset = get_bloginfo('charset');
}
/**
* Filters the default wp_mail() charset.
*
* @param string $charset Default email charset.
* @since 2.3.0
*
*/
$phpmailer->CharSet = apply_filters('wp_mail_charset', $charset);
// Set custom headers.
if (!empty($headers)) {
foreach ((array) $headers as $name => $content) {
// Only add custom headers not added automatically by PHPMailer.
if (!in_array($name, array('MIME-Version', 'X-Mailer'), true)) {
try {
$phpmailer->addCustomHeader(sprintf('%1$s: %2$s', $name, $content));
} catch (PHPMailer\PHPMailer\Exception $e) {
continue;
}
}
}
if (false !== stripos($content_type, 'multipart') && !empty($boundary)) {
$phpmailer->addCustomHeader(sprintf('Content-Type: %s; boundary="%s"', $content_type, $boundary));
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $filename => $attachment ) {
$filename = is_string( $filename ) ? $filename : '';
try {
$phpmailer->addAttachment( $attachment, $filename );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
continue;
}
}
}
/**
* Fires after PHPMailer is initialized.
*
* @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
* @since 2.2.0
*
*/
do_action_ref_array('phpmailer_init', array(&$phpmailer));
// Set whether it's plaintext, depending on $content_type.
if ('text/html' === $phpmailer->ContentType) {
$phpmailer->isHTML(true);
if (fluentMailSendMultiPartText() && !$phpmailer->AltBody) {
$phpmailer->AltBody = (new \FluentMail\App\Services\Html2Text($message))->getText();
if ($phpmailer->AltBody) {
// Set multipart
$phpmailer->ContentType = 'multipart/alternative';
}
}
}
$mail_data = compact('to', 'subject', 'message', 'headers', 'attachments');
// Send!
try {
// Trap the fluentSMTPMail mailer here
$phpmailer = new FluentMail\App\Services\Mailer\FluentPHPMailer($phpmailer);
$send = $phpmailer->send();
/**
* Fires after a successful email is sent using the wp_mail() function.
*
* @param array $mail_data The data of the sent email.
*/
do_action('wp_mail_succeeded', $mail_data);
return $send;
} catch (PHPMailer\PHPMailer\Exception $e) {
$mail_data['phpmailer_exception_code'] = $e->getCode();
/**
* Fires after a PHPMailer\PHPMailer\Exception is caught.
*
* @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
* containing the mail recipient, subject, message, headers, and attachments.
* @since 4.4.0
*
*/
do_action('wp_mail_failed', new WP_Error('wp_mail_failed', $e->getMessage(), $mail_data));
return false;
}
}
}
if (!function_exists('fluentMailGetSettings')) {
/**
* Retrieves the Fluent Mail settings.
*
* This function retrieves the Fluent Mail settings from the WordPress options.
* If the settings are not found, it returns the default settings provided.
* If the settings are found and the 'use_encrypt' option is enabled, it decrypts
* the secret fields for each connection provider.
*
* @param array $defaults The default settings to return if the Fluent Mail settings are not found.
* @param bool $cached Whether to use the cached settings or not.
* @return array The Fluent Mail settings.
*/
function fluentMailGetSettings($defaults = [], $cached = true) {
static $cachedSettings;
if ($cached && $cachedSettings) {
return $cachedSettings;
}
$settings = get_option('fluentmail-settings');
if (!$settings) {
return $defaults;
}
if (!empty($settings['use_encrypt'])) {
$providerKeyMaps = [
'smtp' => 'password',
'ses' => 'secret_key',
'mailgun' => 'api_key',
'sendgrid' => 'api_key',
'sendinblue' => 'api_key',
'sparkpost' => 'api_key',
'pepipost' => 'api_key',
'postmark' => 'api_key',
'elasticmail' => 'api_key',
'gmail' => 'client_secret',
'outlook' => 'client_secret'
];
if (!empty($settings['connections']) && is_array($settings['connections'])) {
foreach ($settings['connections'] as $key => $connection) {
$providerKey = $connection['provider_settings']['provider'];
if (empty($providerKeyMaps[$providerKey])) {
continue;
}
$secretFieldKey = $providerKeyMaps[$providerKey];
if (empty($connection['provider_settings'][$secretFieldKey])) {
continue;
}
$settings['connections'][$key]['provider_settings'][$secretFieldKey] = fluentMailEncryptDecrypt($connection['provider_settings'][$secretFieldKey], 'd');
}
}
}
$cachedSettings = $settings;
return $settings;
}
}
if (!function_exists('fluentMailSetSettings')) {
/**
* Sets the Fluent Mail settings and encrypts sensitive fields.
*
* @param array $settings The Fluent Mail settings.
*
* @return bool Returns Result of the Settings..
*/
function fluentMailSetSettings($settings) {
/**
* Get the value of the 'use_encrypt' setting.
*
* This function applies the 'fluentsmtp_use_encrypt' filter to the 'yes' default value.
*
* @param string $default The default value for the 'use_encrypt' setting.
* @return string The filtered value of the 'use_encrypt' setting.
*/
$settings['use_encrypt'] = apply_filters('fluentsmtp_use_encrypt', 'yes');
$hasSecretField = false;
if (!empty($settings['use_encrypt'])) {
$providerKeyMaps = [
'smtp' => 'password',
'ses' => 'secret_key',
'mailgun' => 'api_key',
'sendgrid' => 'api_key',
'sendinblue' => 'api_key',
'sparkpost' => 'api_key',
'pepipost' => 'api_key',
'postmark' => 'api_key',
'elasticmail' => 'api_key',
'gmail' => 'client_secret',
'outlook' => 'client_secret'
];
if (!empty($settings['connections']) && is_array($settings['connections'])) {
foreach ($settings['connections'] as $key => $connection) {
$providerKey = $connection['provider_settings']['provider'];
if (empty($providerKeyMaps[$providerKey])) {
continue;
}
$secretFieldKey = $providerKeyMaps[$providerKey];
if (empty($connection['provider_settings'][$secretFieldKey])) {
continue;
}
$hasSecretField = true;
$settings['connections'][$key]['provider_settings'][$secretFieldKey] = fluentMailEncryptDecrypt($connection['provider_settings'][$secretFieldKey], 'e');
}
}
}
if ($hasSecretField) {
$settings['test'] = fluentMailEncryptDecrypt('test', 'e');
} else {
$settings['test'] = '';
$settings['use_encrypt'] = '';
}
$result = update_option('fluentmail-settings', $settings);
fluentMailGetSettings([], false);
return $result;
}
}
if (!function_exists('fluentMailEncryptDecrypt')) {
/**
* Encrypts or decrypts a value using AES-256-CTR encryption.
*
* @param string $value The value to be encrypted or decrypted.
* @param string $type The type of operation to perform. Defaults to 'e' for encryption.
*
* @return string|false The encrypted or decrypted value, or false on failure.
*/
function fluentMailEncryptDecrypt($value, $type = 'e') {
if (!$value) {
return $value;
}
if (!extension_loaded('openssl')) {
return $value;
}
if (defined('FLUENTMAIL_ENCRYPT_SALT')) {
$salt = FLUENTMAIL_ENCRYPT_SALT;
} else {
$salt = (defined('LOGGED_IN_SALT') && '' !== LOGGED_IN_SALT) ? LOGGED_IN_SALT : 'this-is-a-fallback-salt-but-not-secure';
}
if (defined('FLUENTMAIL_ENCRYPT_KEY')) {
$key = FLUENTMAIL_ENCRYPT_KEY;
} else {
$key = (defined('LOGGED_IN_KEY') && '' !== LOGGED_IN_KEY) ? LOGGED_IN_KEY : 'this-is-a-fallback-key-but-not-secure';
}
if ($type == 'e') {
$method = 'aes-256-ctr';
$ivlen = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($ivlen);
$raw_value = openssl_encrypt($value . $salt, $method, $key, 0, $iv);
if (!$raw_value) {
return false;
}
return base64_encode($iv . $raw_value); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}
$raw_value = base64_decode($value, true); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
$method = 'aes-256-ctr';
$ivlen = openssl_cipher_iv_length($method);
$iv = substr($raw_value, 0, $ivlen);
if (strlen($iv) < 16) {
$iv = str_pad($iv, 16, "\0"); // Pad with null bytes if it's shorter
}
$raw_value = substr($raw_value, $ivlen);
$newValue = openssl_decrypt($raw_value, $method, $key, 0, $iv);
if (!$newValue || substr($newValue, -strlen($salt)) !== $salt) {
return false;
}
return substr($newValue, 0, -strlen($salt));
}
}
/**
* Returns the FluentSmtpDb instance.
*
* If the function `FluentSmtpDb` exists, it will be called and the result will be returned.
* Otherwise, the file `wpfluent.php` will be required and the `FluentSmtpDb` function will be called and returned.
*
* @return FluentSmtpDb The FluentSmtpDb instance.
*/
function fluentMailDb() {
if (function_exists('FluentSmtpDb')) {
return FluentSmtpDb();
}
require_once FLUENTMAIL_PLUGIN_PATH . 'app/Services/DB/wpfluent.php';
return FluentSmtpDb();
}
function fluentMailFuncCouldNotBeLoadedRecheckPluginsLoad() {
add_action('admin_notices', function () {
if (!current_user_can('manage_options')) {
return;
}
$details = new ReflectionFunction('wp_mail');
$hints = $details->getFileName() . ':' . $details->getStartLine();
?>
<div class="notice notice-warning fluentsmtp_urgent is-dismissible">
<p>The <strong>FluentSMTP</strong> plugin depends on <a target="_blank" href="https://developer.wordpress.org/reference/functions/wp_mail/">wp_mail</a> pluggable function and plugin is not able to extend it. Please check if another plugin is using this and disable it for <strong>FluentSMTP</strong> to work!</p>
<p style="color: red;">
<?php esc_html_e('Possible Conflict: ', 'fluent-smtp');?>
<?php echo $hints; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
</div>
<?php
});
$activePlugins = get_option('active_plugins');
$index = array_search('fluent-smtp/fluent-smtp.php', $activePlugins);
if ($index !== false) {
if ($index === 0) {
return;
}
unset($activePlugins[$index]);
array_unshift($activePlugins, 'fluent-smtp/fluent-smtp.php');
update_option('active_plugins', $activePlugins, true);
}
}
/**
* Check if the email should be sent as multi-part text.
*
* @return bool Returns true if the email should be sent as multi-part text, false otherwise.
*/
function fluentMailSendMultiPartText() {
$settings = fluentMailGetSettings();
return isset($settings['misc']['send_as_text']) && $settings['misc']['send_as_text'] == 'yes';
}

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1,150 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Hooks\Handlers\AdminMenuHandler;
use FluentMail\App\Hooks\Handlers\SchedulerHandler;
use FluentMail\App\Hooks\Handlers\InitializeSiteHandler;
use WP_REST_Request;
class ActionsRegistrar
{
/**
* Application instance.
*
* @var Application
*/
protected $app;
/**
* Constructor.
*
* @param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Alternative static constructor.
*
* @param Application $app
* @return static
*/
public static function init(Application $app)
{
$instance = new static($app);
$instance->registerHooks();
return $instance;
}
/**
* Register all core hooks and REST routes.
*
* @return void
*/
public function registerHooks()
{
$this->registerAdminMenu();
$this->registerScheduler();
$this->registerSiteInitialization();
$this->registerCustomActions();
$this->registerRestRoutes();
}
/**
* Register admin menu and notices.
*
* @return void
*/
protected function registerAdminMenu()
{
$adminMenuHandler = new AdminMenuHandler($this->app);
$adminMenuHandler->addFluentMailMenu();
$this->app->addAction('admin_notices', 'AdminMenuHandler@maybeAdminNotice');
}
/**
* Register background scheduler hooks.
*
* @return void
*/
protected function registerScheduler()
{
(new SchedulerHandler)->register();
}
/**
* Register site-level initialization logic.
*
* @return void
*/
protected function registerSiteInitialization()
{
(new InitializeSiteHandler)->addHandler();
}
/**
* Register custom application actions.
*
* @return void
*/
protected function registerCustomActions()
{
$this->app->addCustomAction(
'handle_exception', 'ExceptionHandler@handle'
);
}
/**
* Register REST API routes.
*
* @return void
*/
protected function registerRestRoutes()
{
$this->app->addAction('rest_api_init', function () {
register_rest_route('fluent-smtp', '/outlook_callback/', [
'methods' => 'GET',
'callback' => [$this, 'handleOutlookCallback'],
'permission_callback' => [$this, 'verifyOutlookCallbackState'],
]);
});
}
/**
* Handle the Outlook OAuth callback.
*
* @param WP_REST_Request $request
* @return void
*/
public function handleOutlookCallback(WP_REST_Request $request)
{
$code = $request->get_param('code');
$output = $this->app->view->make('admin.html_code', [
'title' => 'Your Access Code',
'body' => sprintf(
'<p>Copy the following code and paste in the fluentSMTP settings</p><textarea readonly>%s</textarea>',
sanitize_textarea_field($code)
),
]);
wp_die($output, 'Access Code');
}
/**
* Verify the 'state' parameter in the OAuth callback.
*
* @return bool
*/
public function verifyOutlookCallbackState()
{
$state = $_REQUEST['state'] ?? null;
return $state === get_option('_fluentmail_last_generated_state');
}
}

View File

@@ -0,0 +1,457 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
use FluentMail\App\Models\Logger;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\Converter;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\TransStrings;
class AdminMenuHandler
{
protected $app = null;
public function __construct(Application $application)
{
$this->app = $application;
}
public function addFluentMailMenu()
{
add_action('admin_menu', array($this, 'addMenu'));
if (isset($_GET['page']) && $_GET['page'] == 'fluent-mail' && is_admin()) {
add_action('admin_enqueue_scripts', array($this, 'enqueueAssets'));
if (isset($_REQUEST['sub_action']) && $_REQUEST['sub_action'] == 'slack_success') {
add_action('admin_init', function () {
$nonce = Arr::get($_REQUEST, '_slacK_nonce');
if (!wp_verify_nonce($nonce, 'fluent_smtp_slack_register_site')) {
wp_redirect(admin_url('options-general.php?page=fluent-mail&slack_security_failed=1#/notification-settings'));
die();
}
$settings = (new Settings())->notificationSettings();
$token = Arr::get($_REQUEST, 'site_token');
if ($token && $token == Arr::get($settings, 'slack.token')) {
$settings['slack'] = [
'status' => 'yes',
'token' => sanitize_text_field($token),
'slack_team' => sanitize_text_field(Arr::get($_REQUEST, 'slack_team')),
'webhook_url' => sanitize_url(Arr::get($_REQUEST, 'slack_webhook'))
];
$settings['active_channel'] = 'slack';
update_option('_fluent_smtp_notify_settings', $settings);
}
wp_redirect(admin_url('options-general.php?page=fluent-mail#/notification-settings'));
die();
});
}
}
add_action('admin_bar_menu', array($this, 'addSimulationBar'), 999);
add_action('admin_init', array($this, 'initAdminWidget'));
add_action('install_plugins_table_header', function () {
if (!isset($_REQUEST['s']) || empty($_REQUEST['s']) || empty($_REQUEST['tab']) || $_REQUEST['tab'] != 'search') {
return;
}
$search = str_replace(['%20', '_', '-'], ' ', $_REQUEST['s']);
$search = trim(strtolower(sanitize_text_field($search)));
$searchTerms = ['wp-mail-smtp', 'wp mail', 'wp mail smtp', 'post mailer', 'wp smtp', 'smtp mail', 'smtp', 'post smtp', 'easy smtp', 'easy wp smtp', 'smtp mailer', 'gmail smtp', 'offload ses'];
if (!strpos($search, 'smtp')) {
if (!in_array($search, $searchTerms)) {
return;
}
}
?>
<div
style="background-color: #fff;border: 1px solid #dcdcde;box-sizing: border-box;padding: 20px;margin: 15px 0;"
class="fluent_smtp_box">
<h3 style="margin: 0;"><?php __('For SMTP, you already have FluentSMTP Installed', 'fluent-smtp'); ?></h3>
<p><?php __('You seem to be looking for an SMTP plugin, but there\'s no need for another one — FluentSMTP is already installed on your site. FluentSMTP is a comprehensive, free, and open-source plugin with full features available without any upsell', 'fluent-smtp'); ?>
(<a href="https://fluentsmtp.com/why-we-built-fluentsmtp-plugin/"><?php __('learn why it\'s free', 'fluent-smtp'); ?></a>)<?php __('. It\'s compatible with various SMTP services, including Amazon SES, SendGrid, MailGun, ElasticEmail, SendInBlue, Google, Microsoft, and others, providing you with a wide range of options for your email needs.', 'fluent-smtp'); ?>
</p><a href="<?php echo esc_url(admin_url('options-general.php?page=fluent-mail#/')); ?>"
class="wp-core-ui button button-primary"><?php __('Go To FluentSMTP Settings', 'fluent-smtp'); ?></a>
<p style="font-size: 80%; margin: 15px 0 0;"><?php __('This notice is from FluentSMTP plugin to prevent plugin
conflict.', 'fluent-smtp') ?></p>
</div>
<?php
}, 1);
add_action('wp_ajax_fluent_smtp_get_dashboard_html', function () {
// This widget should be displayed for certain high-level users only.
if (!current_user_can('manage_options') || apply_filters('fluent_mail_disable_dashboard_widget', false)) {
wp_send_json([
'html' => __('You do not have permission to see this data', 'fluent-smtp')
]);
}
wp_send_json([
'html' => $this->getDashboardWidgetHtml()
]);
});
}
public function addMenu()
{
$title = $this->app->applyCustomFilters('admin-menu-title', __('FluentSMTP', 'fluent-smtp'));
add_submenu_page(
'options-general.php',
$title,
$title,
'manage_options',
'fluent-mail',
[$this, 'renderApp'],
16
);
}
public function renderApp()
{
$dailyTaskHookName = 'fluentmail_do_daily_scheduled_tasks';
if (!wp_next_scheduled($dailyTaskHookName)) {
wp_schedule_event(time(), 'daily', $dailyTaskHookName);
}
$this->app->view->render('admin.menu');
}
public function enqueueAssets()
{
add_action('wp_print_scripts', function () {
$isSkip = apply_filters('fluentsmtp_skip_no_conflict', false);
if ($isSkip) {
return;
}
global $wp_scripts;
if (!$wp_scripts) {
return;
}
$themeUrl = content_url('themes');
$pluginUrl = plugins_url();
foreach ($wp_scripts->queue as $script) {
if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
continue;
}
$src = $wp_scripts->registered[$script]->src;
$isMatched = strpos($src, $pluginUrl) !== false && !strpos($src, 'fluent-smtp') !== false;
if (!$isMatched) {
continue;
}
$isMatched = strpos($src, $themeUrl) !== false;
if ($isMatched) {
wp_dequeue_script($wp_scripts->registered[$script]->handle);
}
}
}, 1);
wp_enqueue_script(
'fluent_mail_admin_app_boot',
fluentMailMix('admin/js/boot.js'),
['jquery'],
FLUENTMAIL_PLUGIN_VERSION
);
wp_enqueue_script('fluentmail-chartjs', fluentMailMix('libs/chartjs/Chart.min.js'), [], FLUENTMAIL_PLUGIN_VERSION);
wp_enqueue_script('fluentmail-vue-chartjs', fluentMailMix('libs/chartjs/vue-chartjs.min.js'), [], FLUENTMAIL_PLUGIN_VERSION);
wp_enqueue_script('dompurify', fluentMailMix('libs/purify/purify.min.js'), [], '2.4.3');
wp_enqueue_style(
'fluent_mail_admin_app', fluentMailMix('admin/css/fluent-mail-admin.css'), [], FLUENTMAIL_PLUGIN_VERSION
);
$user = get_user_by('ID', get_current_user_id());
$disable_recommendation = defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS;
$settings = $this->getMailerSettings();
$recommendedSettings = false;
if (empty($settings['connections'])) {
$recommendedSettings = (new Converter())->getSuggestedConnection();
}
$displayName = trim($user->first_name . ' ' . $user->last_name);
if (!$displayName) {
$displayName = $user->display_name;
}
wp_localize_script('fluent_mail_admin_app_boot', 'FluentMailAdmin', [
'slug' => FLUENTMAIL,
'brand_logo' => esc_url(fluentMailMix('images/logo.svg')),
'nonce' => wp_create_nonce(FLUENTMAIL),
'settings' => $settings,
'images_url' => esc_url(fluentMailMix('images/')),
'has_fluentcrm' => defined('FLUENTCRM'),
'has_fluentform' => defined('FLUENTFORM'),
'user_email' => $user->user_email,
'user_display_name' => $displayName,
'require_optin' => $this->isRequireOptin(),
'has_ninja_tables' => defined('NINJA_TABLES_VERSION'),
'disable_recommendation' => apply_filters('fluentmail_disable_recommendation', false),
'disable_installation' => $disable_recommendation,
'plugin_url' => 'https://fluentsmtp.com/?utm_source=wp&utm_medium=install&utm_campaign=dashboard',
'trans' => $this->getTrans(),
'recommended' => $recommendedSettings,
'is_disabled_defined' => defined('FLUENTMAIL_SIMULATE_EMAILS') && FLUENTMAIL_SIMULATE_EMAILS
]);
do_action('fluent_mail_loading_app');
wp_enqueue_script(
'fluent_mail_admin_app',
fluentMailMix('admin/js/fluent-mail-admin-app.js'),
['fluent_mail_admin_app_boot'],
FLUENTMAIL_PLUGIN_VERSION,
true
);
add_filter('admin_footer_text', function ($text) {
return sprintf(
__('%1$s is a free plugin & it will be always free %2$s. %3$s', 'fluent-smtp'),
'<b>FluentSMTP</b>',
'<a href="https://fluentsmtp.com/why-we-built-fluentsmtp-plugin/" target="_blank" rel="noopener noreferrer">'. esc_html__('(Learn why it\'s free)', 'fluent-smtp') .'</a>',
'<a href="https://wordpress.org/support/plugin/fluent-smtp/reviews/?filter=5" target="_blank" rel="noopener noreferrer">'. esc_html__('Write a review ★★★★★', 'fluent-smtp') .'</a>'
);
});
}
protected function getMailerSettings()
{
$settings = $this->app->make(Manager::class)->getMailerConfigAndSettings(true);
if ($settings['mappings'] && $settings['connections']) {
$validMappings = array_keys(Arr::get($settings, 'connections', []));
$settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) {
return in_array($key, $validMappings);
});
}
$settings['providers']['outlook']['callback_url'] = rest_url('fluent-smtp/outlook_callback');
$settings = array_merge(
$settings,
[
'user_email' => wp_get_current_user()->user_email
]
);
return $settings;
}
public function maybeAdminNotice()
{
if (!current_user_can('manage_options')) {
return;
}
$connections = $this->app->make(Manager::class)->getConfig('connections');
global $wp_version;
$requireUpdate = version_compare($wp_version, '5.5', '<');
if ($requireUpdate) { ?>
<div class="notice notice-warning">
<p>
<?php echo esc_html(sprintf(__('WordPress version 5.5 or greater is required for FluentSMTP. You are using version %s currently. Please update your WordPress Core to use FluentSMTP Plugin.', 'fluent-smtp'), $wp_version)); ?>
</p>
</div>
<?php } else if (empty($connections)) {
?>
<div class="notice notice-warning">
<p>
<?php esc_html_e('FluentSMTP needs to be configured for it to work.', 'fluent-smtp'); ?>
</p>
<p>
<a href="<?php echo esc_url(admin_url('options-general.php?page=fluent-mail#/')); ?>"
class="button button-primary">
<?php esc_html_e('Configure FluentSMTP', 'fluent-smtp'); ?>
</a>
</p>
</div>
<?php
}
}
public function addSimulationBar($adminBar)
{
if (!current_user_can('manage_options')) {
return;
}
$misc = $this->app->make(Manager::class)->getConfig('misc');
if ((!empty($misc['simulate_emails']) && $misc['simulate_emails'] == 'yes') || (defined('FLUENTMAIL_SIMULATE_EMAILS') && FLUENTMAIL_SIMULATE_EMAILS)) {
$args = [
'parent' => 'top-secondary',
'id' => 'fluentsmtp_simulated',
'title' => __('Email Disabled', 'fluent-smtp'),
'href' => admin_url('options-general.php?page=fluent-mail#/connections'),
'meta' => false
];
echo '<style>li#wp-admin-bar-fluentsmtp_simulated a {background: red; color: white;}</style>';
$adminBar->add_node($args);
}
}
public function isRequireOptin()
{
$opted = get_option('_fluentsmtp_sub_update');
if ($opted) {
return 'no';
}
// check if dismissed
$dismissedStamp = get_option('_fluentsmtp_dismissed_timestamp');
if ($dismissedStamp && (time() - $dismissedStamp) < 30 * 24 * 60 * 60) {
return 'no';
}
return 'yes';
}
public function initAdminWidget()
{
// This widget should be displayed for certain high-level users only.
if (!current_user_can('manage_options') || apply_filters('fluent_mail_disable_dashboard_widget', false)) {
return;
}
add_action('wp_dashboard_setup', function () {
$widget_key = 'fluentsmtp_reports_widget';
wp_add_dashboard_widget(
$widget_key,
esc_html__('Fluent SMTP', 'fluent-smtp'),
[$this, 'dashWidgetContent']
);
});
}
public function dashWidgetContent()
{
?>
<style type="text/css">
td.fstmp_failed {
color: red;
font-weight: bold;
}
</style>
<div id="fsmtp_dashboard_widget_html" class="fsmtp_dash_wrapper">
<h3 style="min-height: 170px;"><?php esc_html_e('Loading data...', 'fluent-smtp'); ?></h3>
</div>
<?php
add_action('admin_footer', function () {
?>
<script type="application/javascript">
document.addEventListener('DOMContentLoaded', function () {
// send an ajax request to ajax url with raw javascript
var xhr = new XMLHttpRequest();
xhr.open('POST', '<?php echo esc_url(admin_url('admin-ajax.php?action=fluent_smtp_get_dashboard_html')); ?>', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var response = JSON.parse(xhr.responseText);
if (response && response.html) {
document.getElementById('fsmtp_dashboard_widget_html').innerHTML = response.html;
} else {
document.getElementById('fsmtp_dashboard_widget_html').innerHTML = '<h3>Failed to load FluentSMTP Reports</h3>';
}
}
};
xhr.send();
});
</script>
<?php
});
}
protected function getDashboardWidgetHtml()
{
$stats = [];
$logModel = new Logger();
$currentTimeStamp = current_time('timestamp');
$startToday = gmdate('Y-m-d 00:00:01', $currentTimeStamp);
$allTime = $logModel->getStats();
$stats['today'] = [
'title' => __('Today', 'fluent-smtp'),
'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $startToday) : 0,
'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('failed', $startToday) : 0
];
$lastWeek = gmdate('Y-m-d 00:00:01', strtotime('-7 days'));
$stats['week'] = [
'title' => __('Last 7 days', 'fluent-smtp'),
'sent' => ($allTime['sent']) ? $logModel->getTotalCountStat('sent', $lastWeek) : 0,
'failed' => ($allTime['failed']) ? $logModel->getTotalCountStat('failed', $lastWeek) : 0,
];
$stats['all_time'] = [
'title' => __('All', 'fluent-smtp'),
'sent' => $allTime['sent'],
'failed' => $allTime['failed'],
];
ob_start();
?>
<table class="fsmtp_dash_table wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Date', 'fluent-smtp'); ?></th>
<th><?php esc_html_e('Sent', 'fluent-smtp'); ?></th>
<th><?php esc_html_e('Failed', 'fluent-smtp'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($stats as $stat): ?>
<tr>
<td><?php echo $stat['title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
<td><?php echo $stat['sent']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
<td class="<?php echo ($stat['failed']) ? 'fstmp_failed' : ''; ?>"><?php echo $stat['failed']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<a style="text-decoration: none; padding-top: 10px; display: block"
href="<?php echo esc_url(admin_url('options-general.php?page=fluent-mail#/')); ?>"
class=""><?php esc_html_e('View All', 'fluent-smtp'); ?></a>
<?php
return ob_get_clean();
}
public function getTrans()
{
return TransStrings::getStrings();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
class ExceptionHandler
{
protected $handlers = [
'FluentMail\Includes\Support\ForbiddenException' => 'handleForbiddenException',
'FluentMail\Includes\Support\ValidationException' => 'handleValidationException'
];
public function handle($e)
{
foreach ($this->handlers as $key => $value) {
if ($e instanceof $key) {
return $this->{$value}($e);
}
}
}
public function handleForbiddenException($e)
{
wp_send_json_error([
'message' => $e->getMessage()
], $e->getCode() ?: 403);
}
public function handleValidationException($e)
{
wp_send_json_error([
'message' => $e->getMessage(),
'errors' => $e->errors()
], $e->getCode() ?: 422);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
class InitializeSiteHandler
{
public function addHandler()
{
add_action('wp_initialize_site', array($this, 'handle'));
}
public static function handle( $new_site )
{
require_once(FLUENTMAIL_PLUGIN_PATH . 'database/migrations/EmailLogs.php');
$blog_id = $new_site->blog_id;
switch_to_blog((int)$blog_id);
\FluentMailMigrations\EmailLogs::migrate();
restore_current_blog();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
class ProviderValidator
{
public function handle($provider, $errors = [])
{
if ($validator = $this->getProviderValidator($provider, $errors)) {
return $validator->validate();
}
return $errors;
}
protected function getProviderValidator($provider, $errors)
{
$key = $provider['provider'];
$path = FluentMail('path.app') . 'Services/Mailer/Providers/' . $key;
$file = $path . '/' . 'Validator.php';
if (file_exists($file)) {
$ns = 'FluentMail\App\Services\Mailer\Providers\\' . $key;
$class = $ns . '\Validator';
if (class_exists($class)) {
return new $class($provider, $errors);
}
}
}
}

View File

@@ -0,0 +1,338 @@
<?php
namespace FluentMail\App\Hooks\Handlers;
use FluentMail\App\Models\Logger;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\NotificationHelper;
use FluentMail\Includes\Support\Arr;
class SchedulerHandler
{
protected $dailyActionName = 'fluentmail_do_daily_scheduled_tasks';
public function register()
{
add_action($this->dailyActionName, array($this, 'handleScheduledJobs'));
add_filter('fluentmail_email_sending_failed', array($this, 'maybeHandleFallbackConnection'), 10, 4);
add_action('fluentsmtp_renew_gmail_token', array($this, 'renewGmailToken'));
add_action('fluentmail_email_sending_failed_no_fallback', array($this, 'maybeSendNotification'), 10, 3);
}
public function handleScheduledJobs()
{
$this->deleteOldEmails();
$this->sendDailyDigest();
}
private function deleteOldEmails()
{
$settings = fluentMailGetSettings();
$logSaveDays = intval(Arr::get($settings, 'misc.log_saved_interval_days'));
if ($logSaveDays) {
(new \FluentMail\App\Models\Logger())->deleteLogsOlderThan($logSaveDays);
}
}
public function sendDailyDigest()
{
$settings = (new Settings())->notificationSettings();
if ($settings['enabled'] != 'yes' || empty($settings['notify_days']) || empty($settings['notify_email'])) {
return;
}
$currentDay = gmdate('D');
if (!in_array($currentDay, $settings['notify_days'])) {
return;
}
$sendTo = $settings['notify_email'];
$sendTo = str_replace(['{site_admin}', '{admin_email}'], get_option('admin_email'), $sendTo);
$sendToArray = explode(',', $sendTo);
$sendToArray = array_filter($sendToArray, function ($email) {
return is_email($email);
});
if (!$sendToArray) {
return false;
}
// we can send a summary email
$lastDigestSent = get_option('_fluentmail_last_email_digest');
if ($lastDigestSent) {
if ((time() - strtotime($lastDigestSent)) < 72000) {
return false; // we don't want to send another email if sent time within 20 hours
}
} else {
$lastDigestSent = gmdate('Y-m-d', strtotime('-7 days'));
}
// Let's create the stats
$startDate = gmdate('Y-m-d 00:00:01', (strtotime($lastDigestSent) - 86400));
$endDate = gmdate('Y-m-d 23:59:59', strtotime('-1 days'));
$reportingDays = floor((strtotime($endDate) - strtotime($startDate)) / 86400);
$loggerModel = new Logger();
$sentCount = $loggerModel->getTotalCountStat('sent', $startDate, $endDate);
$sentStats = [
'total' => $sentCount,
'subjects' => [],
'unique_subjects' => 0
];
if ($sentCount) {
$sentStats['unique_subjects'] = $loggerModel->getSubjectCountStat('sent', $startDate, $endDate);
$sentStats['subjects'] = $loggerModel->getSubjectStat('sent', $startDate, $endDate, 10);
}
$failedCount = $loggerModel->getTotalCountStat('failed', $startDate, $endDate);
$failedStats = [
'total' => $sentCount,
'subjects' => [],
'unique_subjects' => 0
];
if ($failedCount) {
$failedStats['unique_subjects'] = $loggerModel->getSubjectCountStat('failed', $startDate, $endDate);
$failedStats['subjects'] = $loggerModel->getSubjectStat('failed', $startDate, $endDate);
}
$sentSubTitle = sprintf(
__('Showing %1$s of %2$s different subject lines sent in the past %3$s', 'fluent-smtp'),
number_format_i18n(count($sentStats['subjects'])),
number_format_i18n($sentStats['unique_subjects']),
($reportingDays < 2) ? 'day' : $reportingDays . ' days'
);
$failedSubTitle = sprintf(
__('Showing %1$s of %2$s different subject lines failed in the past %3$s', 'fluent-smtp'),
number_format_i18n(count($failedStats['subjects'])),
number_format_i18n($failedStats['unique_subjects']),
($reportingDays < 2) ? 'day' : $reportingDays . ' days'
);
$sentTitle = __('Emails Sent', 'fluent-smtp');
if ($sentCount) {
$sentTitle .= ' <span style="font-size: 12px; vertical-align: middle;">(' . number_format_i18n($sentCount) . ')</span>';
}
$failedTitle = __('Email Failures', 'fluent-smtp');
if ($failedCount) {
$failedTitle .= ' <span style="font-size: 12px; vertical-align: middle;">(' . number_format_i18n($failedCount) . ')</span>';
}
$reportingDate = gmdate(get_option('date_format'), strtotime($startDate));
$data = [
'sent' => [
'total' => $sentCount,
'title' => $sentTitle,
'subtitle' => $sentSubTitle,
'subject_items' => $sentStats['subjects']
],
'fail' => [
'total' => $failedCount,
'title' => $failedTitle,
'subtitle' => $failedSubTitle,
'subject_items' => $failedStats['subjects']
],
'date_range' => $reportingDate,
'domain_name' => $this->getDomainName()
];
$emailBody = (string)fluentMail('view')->make('admin.digest_email', $data);
$emailSubject = $reportingDate . ' email sending stats for ' . $this->getDomainName();
$headers = array('Content-Type: text/html; charset=UTF-8');
update_option('_fluentmail_last_email_digest', gmdate('Y-m-d H:i:s'));
return wp_mail($sendToArray, $emailSubject, $emailBody, $headers);
}
private function getDomainName()
{
$parts = parse_url(site_url());
$url = $parts['host'] . (isset($parts['path']) ? $parts['path'] : '');
return untrailingslashit($url);
}
public function maybeHandleFallbackConnection($status, $logId, $handler, $data = [])
{
if (defined('FLUENTMAIL_EMAIL_TESTING')) {
return false;
}
$settings = (new \FluentMail\App\Models\Settings())->getSettings();
$fallbackConnectionId = \FluentMail\Includes\Support\Arr::get($settings, 'misc.fallback_connection');
if (!$fallbackConnectionId) {
do_action('fluentmail_email_sending_failed_no_fallback', $logId, $handler, $data);
return false;
}
$fallbackConnection = \FluentMail\Includes\Support\Arr::get($settings, 'connections.' . $fallbackConnectionId);
if (!$fallbackConnection) {
do_action('fluentmail_email_sending_failed_no_fallback', $logId, $handler, $data);
return false;
}
$phpMailer = $handler->getPhpMailer();
$fallbackSettings = $fallbackConnection['provider_settings'];
$phpMailer->setFrom($fallbackSettings['sender_email'], $phpMailer->FromName);
// Trap the fluentSMTPMail mailer here
$phpMailer = new \FluentMail\App\Services\Mailer\FluentPHPMailer($phpMailer);
return $phpMailer->sendViaFallback($logId);
}
public function renewGmailToken()
{
$settings = fluentMailGetSettings();
if (!$settings) {
return;
}
$connections = Arr::get($settings, 'connections', []);
foreach ($connections as $connection) {
if (Arr::get($connection, 'provider_settings.provider') != 'gmail') {
continue;
}
$providerSettings = $connection['provider_settings'];
if (($providerSettings['expire_stamp'] - 480) < time() && !empty($providerSettings['refresh_token'])) {
$this->callGmailApiForNewToken($connection['provider_settings']);
}
}
}
public function callGmailApiForNewToken($settings)
{
if (Arr::get($settings, 'key_store') == 'wp_config') {
$settings['client_id'] = defined('FLUENTMAIL_GMAIL_CLIENT_ID') ? FLUENTMAIL_GMAIL_CLIENT_ID : '';
$settings['client_secret'] = defined('FLUENTMAIL_GMAIL_CLIENT_SECRET') ? FLUENTMAIL_GMAIL_CLIENT_SECRET : '';
}
if (!class_exists('\FluentSmtpLib\Google\Client')) {
require_once FLUENTMAIL_PLUGIN_PATH . 'includes/libs/google-api-client/build/vendor/autoload.php';
}
try {
$client = new \FluentSmtpLib\Google\Client();
$client->setClientId($settings['client_id']);
$client->setClientSecret($settings['client_secret']);
$client->addScope("https://www.googleapis.com/auth/gmail.compose");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$tokens = [
'access_token' => $settings['access_token'],
'refresh_token' => $settings['refresh_token'],
'expires_in' => $settings['expire_stamp'] - time()
];
$client->setAccessToken($tokens);
$newTokens = $client->refreshToken($tokens['refresh_token']);
$result = $this->saveNewGmailTokens($settings, $newTokens);
if (!$result) {
return new \WP_Error('api_error', __('Failed to renew the token', 'fluent-smtp'));
}
return true;
} catch (\Exception $exception) {
return new \WP_Error('api_error', $exception->getMessage());
}
}
public function maybeSendNotification($rowId, $handler, $logData = [])
{
$channel = NotificationHelper::getActiveChannelSettings();
if (!$channel) {
return false;
}
$lastNotificationSent = get_option('_fsmtp_last_notification_sent');
if ($lastNotificationSent && (time() - $lastNotificationSent) < 60) {
return false;
}
update_option('_fsmtp_last_notification_sent', time());
$driver = $channel['driver'];
if ($driver == 'telegram') {
$data = [
'token_id' => $channel['token'],
'provider' => $handler->getSetting('provider'),
'error_message' => $this->getErrorMessageFromResponse(maybe_unserialize(Arr::get($logData, 'response')))
];
return NotificationHelper::sendFailedNotificationTele($data);
}
if ($driver == 'slack') {
return NotificationHelper::sendSlackMessage(NotificationHelper::formatSlackMessageBlock($handler, $logData), $channel['webhook_url'], false);
}
if ($driver == 'discord') {
return NotificationHelper::sendDiscordMessage(NotificationHelper::formatDiscordMessageBlock($handler, $logData), $channel['webhook_url'], false);
}
return false;
}
private function saveNewGmailTokens($existingData, $tokens)
{
if (empty($tokens['access_token']) || empty($tokens['refresh_token'])) {
return false;
}
$senderEmail = $existingData['sender_email'];
$existingData['access_token'] = $tokens['access_token'];
$existingData['refresh_token'] = $tokens['refresh_token'];
$existingData['expire_stamp'] = $tokens['expires_in'] + time();
$existingData['expires_in'] = $tokens['expires_in'];
(new Settings())->updateConnection($senderEmail, $existingData);
fluentMailGetProvider($senderEmail, true); // we are clearing the static cache here
wp_schedule_single_event($existingData['expire_stamp'] - 360, 'fluentsmtp_renew_gmail_token');
return true;
}
private function getErrorMessageFromResponse($response)
{
if (!$response || !is_array($response)) {
return '';
}
if (!empty($response['fallback_response']['message'])) {
$message = $response['fallback_response']['message'];
} else {
$message = Arr::get($response, 'message');
}
if (!$message) {
return '';
}
if (!is_string($message)) {
$message = json_encode($message);
}
return $message;
}
}

View File

@@ -0,0 +1,8 @@
<?php
/*
* @var $app FluentMail\Includes\Core\Application
*/
use FluentMail\App\Hooks\Handlers\ActionsRegistrar;
ActionsRegistrar::init($app);

View File

@@ -0,0 +1,21 @@
<?php
add_filter('fluent_crm/quick_links', function ($links) {
$links[] = [
'title' => __('SMTP/Mail Settings', 'fluent-smtp'),
'url' => admin_url('options-general.php?page=fluent-mail#/')
];
return $links;
});
add_filter( 'plugin_action_links_' . plugin_basename( FLUENTMAIL_PLUGIN_FILE ), function ($links) {
$links['settings'] = sprintf(
'<a href="%s" aria-label="%s">%s</a>',
admin_url('options-general.php?page=fluent-mail#/connections'),
esc_attr__( 'Go to Fluent SMTP Settings page', 'fluent-smtp' ),
esc_html__( 'Settings', 'fluent-smtp' )
);
return $links;
}, 10, 1 );

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1,66 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\App;
abstract class Controller
{
/**
* @var \FluentMail\App\Plugin
*/
protected $app = null;
/**
* @var \FluentMail\Includes\Request\Request
*/
protected $request = null;
/**
* @var \FluentMail\Includes\Response\Response
*/
protected $response = null;
public function __construct()
{
$this->app = App::getInstance();
$this->request = $this->app['request'];
$this->response = $this->app['response'];
}
public function send($data = null, $code = 200)
{
return $this->response->send($data, $code);
}
public function sendSuccess($data = null, $code = 200)
{
return $this->response->sendSuccess($data, $code);
}
public function sendError($data = null, $code = 422)
{
return $this->response->sendError($data, $code);
}
public function verify()
{
$permission = 'manage_options';
if(!current_user_can($permission)) {
wp_send_json_error([
'message' => __('You do not have permission to do this action', 'fluent-smtp')
]);
die();
}
$nonce = $this->request->get('nonce');
if(!wp_verify_nonce($nonce, FLUENTMAIL)) {
wp_send_json_error([
'message' => __('Security Failed. Please reload the page', 'fluent-smtp')
]);
die();
}
return true;
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\Models\Logger;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Reporting;
use FluentMail\Includes\Request\Request;
use FluentMail\Includes\Support\Arr;
class DashboardController extends Controller
{
public function index(Logger $logger, Manager $manager)
{
$this->verify();
$connections = $manager->getSettings('connections', []);
return $this->send([
'stats' => $logger->getStats(),
'settings_stat' => [
'connection_counts' => count($connections),
'active_senders' => count($manager->getSettings('mappings', [])),
'auto_delete_days' => $manager->getSettings('misc.log_saved_interval_days'),
'log_enabled' => $manager->getSettings('misc.log_emails')
]
]);
}
public function getDayTimeStats()
{
$this->verify();
$lastDay = 0;
if (isset($_REQUEST['last_day'])) {
$lastDay = (int)$_REQUEST['last_day'];
}
global $wpdb;
if ($lastDay > 6) {
$results = $wpdb->get_results("SELECT
DAYNAME(created_at) AS day_of_week,
HOUR(created_at) AS hour_of_day,
COUNT(*) AS count
FROM
{$wpdb->prefix}fsmpt_email_logs
WHERE
created_at >= NOW() - INTERVAL {$lastDay} DAY
GROUP BY
DAYNAME(created_at),
HOUR(created_at)
ORDER BY
FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'),
HOUR(created_at)");
} else {
$results = $wpdb->get_results("SELECT
DAYNAME(created_at) AS day_of_week,
HOUR(created_at) AS hour_of_day,
COUNT(*) AS count
FROM
{$wpdb->prefix}fsmpt_email_logs
GROUP BY
DAYNAME(created_at),
HOUR(created_at)
ORDER BY
FIELD(DAYNAME(created_at), 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'),
HOUR(created_at)");
}
// Assuming $results is the array of records fetched from the database.
$dataItems = [
'Mon' => [], 'Tue' => [], 'Wed' => [], 'Thu' => [], 'Fri' => [], 'Sat' => [], 'Sun' => []
];
$hours = ['0:00', '1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00'];
foreach ($dataItems as $day => $data) {
foreach ($hours as $hour) {
$dataItems[$day][$hour] = 0;
}
}
foreach ($results as $row) {
$day = substr($row->day_of_week, 0, 3); // Shorten 'Monday' to 'Mon', etc.
$hour = $row->hour_of_day . ":00"; // Format hour as '0:00', '1:00', etc.
$dataItems[$day][$hour] = (int)$row->count;
}
return $this->send([
'stats' => $dataItems
]);
}
public function getSendingStats(Request $request, Reporting $reporting)
{
$this->verify();
list($from, $to) = $request->get('date_range');
return $this->send([
'stats' => $reporting->getSendingStats($from, $to)
]);
}
public function getDocs()
{
$this->verify();
$request = wp_remote_get('https://fluentsmtp.com/wp-json/wp/v2/docs?per_page=100');
$docs = json_decode(wp_remote_retrieve_body($request), true);
$formattedDocs = [];
foreach ($docs as $doc) {
$primaryCategory = Arr::get($doc, 'taxonomy_info.doc_category.0', ['value' => 'none', 'label' => 'Other']);
$formattedDocs[] = [
'title' => $doc['title']['rendered'],
'content' => $doc['content']['rendered'],
'link' => $doc['link'],
'category' => $primaryCategory
];
}
return $this->send([
'docs' => $formattedDocs
]);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\NotificationHelper;
use FluentMail\Includes\Request\Request;
use FluentMail\Includes\Support\Arr;
class DiscordController extends Controller
{
public function registerSite(Request $request)
{
$this->verify();
$formData = $request->get('settings', []);
if (empty($formData['webhook_url'])) {
return $this->sendError([
'message' => __('Webhook URL is required', 'fluent-smtp')
], 422);
}
if (empty($formData['channel_name'])) {
return $this->sendError([
'message' => __('Channel Name required', 'fluent-smtp')
], 422);
}
$prevSettings = (new Settings())->notificationSettings();
$prevSettings['discord'] = [
'status' => 'yes',
'channel_name' => Arr::get($formData, 'channel_name'),
'webhook_url' => Arr::get($formData, 'webhook_url'),
];
$prevSettings['active_channel'] = 'discord';
update_option('_fluent_smtp_notify_settings', $prevSettings);
return $this->sendSuccess([
'message' => __('Your settings has been saved', 'fluent-smtp'),
]);
}
public function sendTestMessage(Request $request)
{
// Let's update the notification status
$settings = (new Settings())->notificationSettings();
if (Arr::get($settings, 'discord.status') != 'yes') {
return $this->sendError([
'message' => __('Slack notification is not enabled', 'fluent-smtp')
], 422);
}
$message = 'This is a test message for ' . site_url() . '. If you get this message, then your site is connected successfully.';
$result = NotificationHelper::sendDiscordMessage($message, Arr::get($settings, 'discord.webhook_url'));
if (is_wp_error($result)) {
return $this->sendError([
'message' => $result->get_error_message(),
'errors' => $result->get_error_data(),
], 422);
}
return $this->sendSuccess([
'message' => __('Test message sent successfully', 'fluent-smtp'),
'server_response' => $result
]);
}
public function disconnect()
{
$settings = (new Settings())->notificationSettings();
$settings['discord'] = [
'status' => 'no',
'webhook_url' => '',
'channel_name' => ''
];
$settings['active_channel'] = '';
update_option('_fluent_smtp_notify_settings', $settings);
return $this->sendSuccess([
'message' => __('Discord connection has been disconnected successfully', 'fluent-smtp')
]);
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\Models\Logger;
use FluentMail\Includes\Request\Request;
class LoggerController extends Controller
{
public function get(Request $request, Logger $logger)
{
$this->verify();
return $this->send(
$logger->get(
$request->except(['nonce', 'action'])
)
);
}
public function show(Request $request, Logger $logger)
{
$this->verify();
$result = $logger->navigate($request->all());
return $this->sendSuccess($result);
}
public function delete(Request $request, Logger $logger)
{
$this->verify();
$id = (array) $request->get('id');
$logger->delete($id);
if ($id && $id[0] == 'all') {
$subject = 'All logs';
} else {
$count = count($id);
$subject = $count > 1 ? "{$count} Logs" : 'Log';
}
return $this->sendSuccess([
'message' => sprintf(__('%s deleted successfully.', 'fluent-smtp'), $subject)
]);
}
public function retry(Request $request, Logger $logger)
{
$this->verify();
try {
$this->app->addAction('wp_mail_failed', function($response) use ($logger, $request) {
$log = $logger->find($id = $request->get('id'));
$log['retries'] = $log['retries'] + 1;
$logger->updateLog($log, ['id' => $id]);
return $this->sendError([
'message' => $response->get_error_message(),
'errors' => $response->get_error_data()
], $response->get_error_code());
});
if ($email = $logger->resendEmailFromLog($request->get('id'), $request->get('type'))) {
return $this->sendSuccess([
'email' => $email,
'message' => __('Email sent successfully.', 'fluent-smtp')
]);
}
throw new \Exception(esc_html__('Something went wrong', 'fluent-smtp'), 400);
} catch (\Exception $e) {
return $this->sendError([
'message' => $e->getMessage()
], $e->getCode());
}
}
public function retryBulk(Request $request, Logger $logger)
{
$this->verify();
$logIds = $request->get('log_ids', []);
$failedCount = 0;
$this->app->addAction('wp_mail_failed', function($response) use (&$failedCount) {
$failedCount++;
});
$failedInitiated = 0;
$successCount = 0;
foreach ($logIds as $logId) {
try {
$email = $logger->resendEmailFromLog($logId, 'check_realtime');
$successCount++;
} catch (\Exception $exception) {
$failedInitiated++;
}
}
$message = __('Selected Emails have been proceed to send.', 'fluent-smtp');
if ($failedCount) {
$message .= sprintf(__(' But %d emails are reported to failed to send.', 'fluent-smtp'), $failedCount);
}
if ($failedInitiated) {
$message .= sprintf(__(' And %d emails are failed to init the emails', 'fluent-smtp'), $failedInitiated);
}
return $this->sendSuccess([
'message' => $message
]);
}
}

View File

@@ -0,0 +1,689 @@
<?php
namespace FluentMail\App\Http\Controllers;
use Exception;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Request\Request;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Support\ValidationException;
use FluentMail\App\Services\Mailer\Providers\Factory;
class SettingsController extends Controller
{
public function index(Settings $settings)
{
$this->verify();
try {
$setting = $settings->get();
return $this->sendSuccess([
'settings' => $setting
]);
} catch (Exception $e) {
return $this->sendError([
'message' => $e->getMessage()
], $e->getCode());
}
}
public function validate(Request $request, Settings $settings, Factory $factory)
{
$this->verify();
try {
$data = $request->except(['action', 'nonce']);
$provider = $factory->make($data['provider']['key']);
$provider->validateBasicInformation($data);
$this->sendSuccess();
} catch (ValidationException $e) {
$this->sendError($e->errors(), $e->getCode());
}
}
public function store(Request $request, Settings $settings, Factory $factory)
{
$this->verify();
$passWordKeys = ['password', 'access_key', 'secret_key', 'api_key', 'client_id', 'client_secret', 'auth_token', 'access_token', 'refresh_token'];
try {
$data = $request->except(['action', 'nonce']);
$data = wp_unslash($data);
$provider = $factory->make($data['connection']['provider']);
$connection = $data['connection'];
foreach ($connection as $index => $value) {
if ($index == 'sender_email') {
$connection['sender_email'] = sanitize_email($connection['sender_email']);
}
if (in_array($index, $passWordKeys)) {
if ($value) {
$connection[$index] = trim($value);
}
continue;
}
if (is_string($value) && $value) {
$connection[$index] = sanitize_text_field($value);
}
}
$data['connection'] = $connection;
$this->validateConnection($provider, $connection);
$provider->checkConnection($connection);
$data['valid_senders'] = $provider->getValidSenders($connection);
$data = apply_filters('fluentmail_saving_connection_data', $data, $data['connection']['provider']);
$settings->store($data);
return $this->sendSuccess([
'message' => __('Settings saved successfully.', 'fluent-smtp'),
'connections' => $settings->getConnections(),
'mappings' => $settings->getMappings(),
'misc' => $settings->getMisc()
]);
} catch (ValidationException $e) {
return $this->sendError($e->errors(), 422);
} catch (Exception $e) {
return $this->sendError([
'message' => $e->getMessage()
], 422);
}
}
public function storeMiscSettings(Request $request, Settings $settings)
{
$this->verify();
$misc = $request->get('settings');
$settings->updateMiscSettings($misc);
$this->sendSuccess([
'message' => __('General Settings has been updated', 'fluent-smtp')
]);
}
public function delete(Request $request, Settings $settings)
{
$this->verify();
$settings = $settings->delete($request->get('key'));
return $this->sendSuccess($settings);
}
public function storeGlobals(Request $request, Settings $settings)
{
$this->verify();
$settings->saveGlobalSettings(
$data = $request->except(['action', 'nonce'])
);
return $this->sendSuccess([
'form' => $data,
'message' => __('Settings saved successfully.', 'fluent-smtp')
]);
}
public function sendTestEmil(Request $request, Settings $settings)
{
$this->verify();
try {
$this->app->addAction('wp_mail_failed', [$this, 'onFail']);
$data = $request->except(['action', 'nonce']);
if (!isset($data['email'])) {
return $this->sendError([
'email_error' => __('The email field is required.', 'fluent-smtp')
], 422);
}
if (!defined('FLUENTMAIL_EMAIL_TESTING')) {
define('FLUENTMAIL_EMAIL_TESTING', true);
}
$settings->sendTestEmail($data, $settings->get());
return $this->sendSuccess([
'message' => __('Email delivered successfully.', 'fluent-smtp')
]);
} catch (Exception $e) {
return $this->sendError([
'message' => $e->getMessage()
], $e->getCode());
}
}
public function onFail($response)
{
return $this->sendError([
'message' => $response->get_error_message(),
'errors' => $response->get_error_data()
], 422);
}
public function validateConnection($provider, $connection)
{
$errors = [];
try {
$provider->validateBasicInformation($connection);
} catch (ValidationException $e) {
$errors = $e->errors();
}
try {
$provider->validateProviderInformation($connection);
} catch (ValidationException $e) {
$errors = array_merge($errors, $e->errors());
}
if ($errors) {
throw new ValidationException(esc_html__('Unprocessable Entity', 'fluent-smtp'), 422, null, $errors); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
}
}
public function getConnectionInfo(Request $request, Settings $settings, Factory $factory)
{
$this->verify();
$connectionId = $request->get('connection_id');
$connections = $settings->getConnections();
if (!isset($connections[$connectionId]['provider_settings'])) {
return $this->sendSuccess([
'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp')
]);
}
$connection = $connections[$connectionId]['provider_settings'];
$provider = $factory->make($connection['provider']);
return $this->sendSuccess($provider->getConnectionInfo($connection));
}
public function addNewSenderEmail(Request $request, Settings $settings, Factory $factory)
{
$this->verify();
$connectionId = $request->get('connection_id');
$connections = $settings->getConnections();
if (!isset($connections[$connectionId]['provider_settings'])) {
return $this->sendSuccess([
'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp')
]);
}
$connection = $connections[$connectionId]['provider_settings'];
$provider = $factory->make($connection['provider']);
$email = sanitize_email($request->get('new_sender'));
if (!is_email($email)) {
return $this->sendError([
'message' => __('Please provide a valid email address', 'fluent-smtp')
]);
}
$result = $provider->addNewSenderEmail($connection, $email);
if (is_wp_error($result)) {
return $this->sendError([
'message' => $result->get_error_message()
]);
}
return $this->sendSuccess([
'message' => __('Email has been added successfully', 'fluent-smtp')
]);
}
public function removeSenderEmail(Request $request, Settings $settings, Factory $factory)
{
$this->verify();
$connectionId = $request->get('connection_id');
$connections = $settings->getConnections();
if (!isset($connections[$connectionId]['provider_settings'])) {
return $this->sendSuccess([
'info' => __('Sorry no connection found. Please reload the page and try again', 'fluent-smtp')
]);
}
$connection = $connections[$connectionId]['provider_settings'];
$provider = $factory->make($connection['provider']);
$email = sanitize_email($request->get('email'));
if (!is_email($email)) {
return $this->sendError([
'message' => __('Please provide a valid email address', 'fluent-smtp')
]);
}
$result = $provider->removeSenderEmail($connection, $email);
if (is_wp_error($result)) {
return $this->sendError([
'message' => $result->get_error_message()
]);
}
return $this->sendSuccess([
'message' => __('Email has been removed successfully', 'fluent-smtp')
]);
}
public function installPlugin(Request $request)
{
$this->verify();
$pluginSlug = $request->get('plugin_slug');
$plugin = [
'name' => $pluginSlug,
'repo-slug' => $pluginSlug,
'file' => $pluginSlug . '.php'
];
$UrlMaps = [
'fluentform' => [
'admin_url' => admin_url('admin.php?page=fluent_forms'),
'title' => __('Go to Fluent Forms Dashboard', 'fluent-smtp')
],
'fluent-crm' => [
'admin_url' => admin_url('admin.php?page=fluentcrm-admin'),
'title' => __('Go to FluentCRM Dashboard', 'fluent-smtp')
],
'ninja-tables' => [
'admin_url' => admin_url('admin.php?page=ninja_tables#/'),
'title' => __('Go to Ninja Tables Dashboard', 'fluent-smtp')
]
];
if (!isset($UrlMaps[$pluginSlug]) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) {
$this->sendError([
'message' => __('Sorry, You can not install this plugin', 'fluent-smtp')
]);
}
try {
$this->backgroundInstaller($plugin);
$this->send([
'message' => __('Plugin has been successfully installed.', 'fluent-smtp'),
'info' => $UrlMaps[$pluginSlug]
]);
} catch (\Exception $exception) {
$this->sendError([
'message' => $exception->getMessage()
]);
}
}
private function backgroundInstaller($plugin_to_install)
{
if (!empty($plugin_to_install['repo-slug'])) {
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';
WP_Filesystem();
$skin = new \Automatic_Upgrader_Skin();
$upgrader = new \WP_Upgrader($skin);
$installed_plugins = array_keys(\get_plugins());
$plugin_slug = $plugin_to_install['repo-slug'];
$plugin_file = isset($plugin_to_install['file']) ? $plugin_to_install['file'] : $plugin_slug . '.php';
$installed = false;
$activate = false;
// See if the plugin is installed already.
if (isset($installed_plugins[$plugin_file])) {
$installed = true;
$activate = !is_plugin_active($installed_plugins[$plugin_file]);
}
// Install this thing!
if (!$installed) {
// Suppress feedback.
ob_start();
try {
$plugin_information = plugins_api(
'plugin_information',
array(
'slug' => $plugin_slug,
'fields' => array(
'short_description' => false,
'sections' => false,
'requires' => false,
'rating' => false,
'ratings' => false,
'downloaded' => false,
'last_updated' => false,
'added' => false,
'tags' => false,
'homepage' => false,
'donate_link' => false,
'author_profile' => false,
'author' => false,
),
)
);
if (is_wp_error($plugin_information)) {
throw new \Exception(wp_kses_post($plugin_information->get_error_message()));
}
$package = $plugin_information->download_link;
$download = $upgrader->download_package($package);
if (is_wp_error($download)) {
throw new \Exception(wp_kses_post($download->get_error_message()));
}
$working_dir = $upgrader->unpack_package($download, true);
if (is_wp_error($working_dir)) {
throw new \Exception(wp_kses_post($working_dir->get_error_message()));
}
$result = $upgrader->install_package(
array(
'source' => $working_dir,
'destination' => WP_PLUGIN_DIR,
'clear_destination' => false,
'abort_if_destination_exists' => false,
'clear_working' => true,
'hook_extra' => array(
'type' => 'plugin',
'action' => 'install',
),
)
);
if (is_wp_error($result)) {
throw new \Exception(wp_kses_post($result->get_error_message()));
}
$activate = true;
} catch (\Exception $e) {
throw new \Exception(esc_html($e->getMessage()));
}
// Discard feedback.
ob_end_clean();
}
wp_clean_plugins_cache();
// Activate this thing.
if ($activate) {
try {
$result = activate_plugin($installed ? $installed_plugins[$plugin_file] : $plugin_slug . '/' . $plugin_file);
if (is_wp_error($result)) {
throw new \Exception(esc_html($result->get_error_message()));
}
} catch (\Exception $e) {
throw new \Exception(esc_html($e->getMessage()));
}
}
}
}
public function subscribe()
{
$this->verify();
$email = sanitize_text_field($_REQUEST['email']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$displayName = '';
if (isset($_REQUEST['display_name'])) {
$displayName = sanitize_text_field($_REQUEST['display_name']);
}
if (!is_email($email)) {
return $this->sendError([
'message' => __('Sorry! The provider email is not valid', 'fluent-smtp')
], 422);
}
$shareEssentials = 'no';
if ($_REQUEST['share_essentials'] == 'yes') {
update_option('_fluentsmtp_sub_update', 'shared', 'no');
$shareEssentials = 'yes';
} else {
update_option('_fluentsmtp_sub_update', 'yes', 'no');
}
$this->pushData($email, $shareEssentials, $displayName);
return $this->sendSuccess([
'message' => __('You are subscribed to plugin update and monthly tips', 'fluent-smtp')
]);
}
public function subscribeDismiss()
{
$this->verify();
update_option('_fluentsmtp_dismissed_timestamp', time(), 'no');
return $this->sendSuccess([
'message' => 'success'
]);
}
private function pushData($optinEmail, $shareEssentials, $displayName = '')
{
$user = get_user_by('ID', get_current_user_id());
$url = 'https://fluentsmtp.com/wp-admin/?fluentcrm=1&route=contact&hash=6012116c-90d8-42a5-a65b-3649aa34b356';
if (!$displayName) {
$displayName = trim($user->first_name . ' ' . $user->last_name);
if (!$displayName) {
$displayName = $user->display_name;
}
}
wp_remote_post($url, [
'body' => json_encode([ // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
'full_name' => $displayName,
'email' => $optinEmail,
'source' => 'smtp',
'optin_website' => site_url(),
'share_essential' => $shareEssentials
])
]);
}
public function getGmailAuthUrl(Request $request)
{
$this->verify();
$connection = wp_unslash($request->get('connection'));
$clientId = Arr::get($connection, 'client_id');
$clientSecret = Arr::get($connection, 'client_secret');
if (Arr::get($connection, 'key_store') == 'wp_config') {
if (defined('FLUENTMAIL_GMAIL_CLIENT_ID')) {
$clientId = FLUENTMAIL_GMAIL_CLIENT_ID;
} else {
return $this->sendError([
'client_id' => [
'required' => __('Please define FLUENTMAIL_GMAIL_CLIENT_ID in your wp-config.php file', 'fluent-smtp')
]
]);
}
if (defined('FLUENTMAIL_GMAIL_CLIENT_SECRET')) {
$clientSecret = FLUENTMAIL_GMAIL_CLIENT_SECRET;
} else {
return $this->sendError([
'client_secret' => [
'required' => __('Please define FLUENTMAIL_GMAIL_CLIENT_SECRET in your wp-config.php file', 'fluent-smtp')
]
]);
}
}
if (!$clientId) {
return $this->sendError([
'client_id' => [
'required' => __('Please provide application client id', 'fluent-smtp')
]
]);
}
if (!$clientSecret) {
return $this->sendError([
'client_secret' => [
'required' => __('Please provide application client secret', 'fluent-smtp')
]
]);
}
$authUrl = add_query_arg([
'response_type' => 'code',
'access_type' => 'offline',
'client_id' => $clientId,
'redirect_uri' => apply_filters('fluentsmtp_gapi_callback', 'https://fluentsmtp.com/gapi/'),
'state' => admin_url('options-general.php?page=fluent-mail&gapi=1'),
'scope' => 'https://mail.google.com/',
'approval_prompt' => 'force',
'include_granted_scopes' => 'true'
], 'https://accounts.google.com/o/oauth2/auth');
return $this->sendSuccess([
'auth_url' => filter_var($authUrl, FILTER_SANITIZE_URL)
]);
}
public function getOutlookAuthUrl(Request $request)
{
$this->verify();
$connection = wp_unslash($request->get('connection'));
$clientId = Arr::get($connection, 'client_id');
$clientSecret = Arr::get($connection, 'client_secret');
delete_option('_fluentsmtp_intended_outlook_info');
if (Arr::get($connection, 'key_store') == 'wp_config') {
if (defined('FLUENTMAIL_OUTLOOK_CLIENT_ID')) {
$clientId = FLUENTMAIL_OUTLOOK_CLIENT_ID;
} else {
return $this->sendError([
'client_id' => [
'required' => __('Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in your wp-config.php file', 'fluent-smtp')
]
]);
}
if (defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET')) {
$clientSecret = FLUENTMAIL_OUTLOOK_CLIENT_SECRET;
} else {
return $this->sendError([
'client_secret' => [
'required' => __('Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in your wp-config.php file', 'fluent-smtp')
]
]);
}
} else {
update_option('_fluentsmtp_intended_outlook_info', [
'client_id' => $clientId,
'client_secret' => $clientSecret
]);
}
if (!$clientId) {
return $this->sendError([
'client_id' => [
'required' => __('Please provide application client id', 'fluent-smtp')
]
]);
}
if (!$clientSecret) {
return $this->sendError([
'client_secret' => [
'required' => __('Please provide application client secret', 'fluent-smtp')
]
]);
}
return $this->sendSuccess([
'auth_url' => (new \FluentMail\App\Services\Mailer\Providers\Outlook\API($clientId, $clientSecret))->getAuthUrl()
]);
}
public function getNotificationSettings()
{
$settings = (new Settings())->notificationSettings();
$this->verify();
$settings['telegram_notify_token'] = '';
return $this->sendSuccess([
'settings' => $settings
]);
}
public function saveNotificationSettings(Request $request)
{
$this->verify();
$settings = $request->get('settings', []);
$settings = Arr::only($settings, ['enabled', 'notify_email', 'notify_days']);
$settings['notify_email'] = sanitize_text_field($settings['notify_email']);
$settings['enabled'] = sanitize_text_field($settings['enabled']);
$defaults = [
'enabled' => 'no',
'notify_email' => '{site_admin}',
'notify_days' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
];
$oldSettings = (new Settings())->notificationSettings();
$defaults = wp_parse_args($defaults, $oldSettings);
$settings = wp_parse_args($settings, $defaults);
update_option('_fluent_smtp_notify_settings', $settings, false);
return $this->sendSuccess([
'message' => __('Settings has been updated successfully', 'fluent-smtp')
]);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\NotificationHelper;
use FluentMail\Includes\Request\Request;
use FluentMail\Includes\Support\Arr;
class SlackController extends Controller
{
public function registerSite(Request $request)
{
$this->verify();
$formData = $request->get('settings', []);
$userEmail = sanitize_email(Arr::get($formData, 'user_email'));
if (!is_email($userEmail)) {
return $this->sendError([
'message' => __('Please provide a valid email address', 'fluent-smtp')
], 422);
}
$nonce = wp_create_nonce('fluent_smtp_slack_register_site');
$payload = [
'admin_email' => $userEmail,
'smtp_url' => admin_url('options-general.php?_slacK_nonce='.$nonce.'&page=fluent-mail#/'),
'site_url' => site_url(),
'site_title' => get_bloginfo('name'),
'site_lang' => get_bloginfo('language'),
];
$activationData = NotificationHelper::registerSlackSite($payload);
if (is_wp_error($activationData)) {
return $this->sendError([
'message' => $activationData->get_error_message(),
'errors' => $activationData->get_error_data(),
], 422);
}
$prevSettings = (new Settings())->notificationSettings();
$prevSettings['slack'] = [
'status' => 'pending',
'token' => Arr::get($activationData, 'site_token'),
'redirect_url' => ''
];
update_option('_fluent_smtp_notify_settings', $prevSettings);
return $this->sendSuccess([
'message' => __('Awesome! You are redirecting to slack', 'fluent-smtp'),
'redirect_url' => Arr::get($activationData, 'redirect_url')
]);
}
public function sendTestMessage(Request $request)
{
// Let's update the notification status
$settings = (new Settings())->notificationSettings();
if (Arr::get($settings, 'slack.status') != 'yes') {
return $this->sendError([
'message' => __('Slack notification is not enabled', 'fluent-smtp')
], 422);
}
$message = 'This is a test message for ' . site_url() . '. If you get this message, then your site is connected successfully.';
$result = NotificationHelper::sendSlackMessage($message, Arr::get($settings, 'slack.webhook_url'));
if (is_wp_error($result)) {
return $this->sendError([
'message' => $result->get_error_message(),
'errors' => $result->get_error_data(),
], 422);
}
return $this->sendSuccess([
'message' => __('Test message sent successfully', 'fluent-smtp')
]);
}
public function disconnect()
{
$settings = (new Settings())->notificationSettings();
$settings['slack'] = [
'status' => 'no',
'webhook_url' => '',
'token' => ''
];
update_option('_fluent_smtp_notify_settings', $settings);
return $this->sendSuccess([
'message' => __('Slack connection has been disconnected successfully', 'fluent-smtp')
]);
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace FluentMail\App\Http\Controllers;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\NotificationHelper;
use FluentMail\Includes\Request\Request;
use FluentMail\Includes\Support\Arr;
class TelegramController extends Controller
{
public function issuePinCode(Request $request)
{
$this->verify();
$formData = $request->get('settings', []);
$userEmail = sanitize_email(Arr::get($formData, 'user_email'));
if (!is_email($userEmail)) {
return $this->sendError([
'message' => __('Please provide a valid email address', 'fluent-smtp')
], 422);
}
$payload = [
'admin_email' => $userEmail,
'smtp_url' => admin_url('options-general.php?page=fluent-mail#/'),
'site_url' => site_url(),
'site_title' => get_bloginfo('name'),
'site_lang' => get_bloginfo('language'),
];
$activationData = NotificationHelper::issueTelegramPinCode($payload);
if (is_wp_error($activationData)) {
return $this->sendError([
'message' => $activationData->get_error_message(),
'errors' => $activationData->get_error_data(),
], 422);
}
return $this->sendSuccess([
'message' => __('Awesome! Please activate the connection from your telegram account.', 'fluent-smtp'),
'site_token' => Arr::get($activationData, 'site_token'),
'site_pin' => Arr::get($activationData, 'site_pin'),
]);
}
public function confirmConnection(Request $request)
{
$this->verify();
$siteToken = $request->get('site_token', '');
if (empty($siteToken)) {
return $this->sendError([
'message' => __('Please provide site token', 'fluent-smtp')
], 422);
}
$connectionInfo = NotificationHelper::getTelegramConnectionInfo($siteToken);
if (is_wp_error($connectionInfo)) {
return $this->sendError([
'message' => $connectionInfo->get_error_message(),
'errors' => $connectionInfo->get_error_data(),
], 422);
}
// Let's update the notification status
$previousSettings = (new Settings())->notificationSettings();
$previousSettings['telegram'] = [
'status' => 'yes',
'token' => $siteToken
];
$previousSettings['active_channel'] = 'telegram';
update_option('_fluent_smtp_notify_settings', $previousSettings, false);
return $this->sendSuccess([
'success' => true,
'message' => __('Connection successful', 'fluent-smtp'),
]);
}
public function getTelegramConnectionInfo(Request $request)
{
$this->verify();
$settings = (new Settings())->notificationSettings();
if (Arr::get($settings, 'telegram.status') != 'yes') {
return $this->sendSuccess([
'message' => __('Telegram notification is not enabled', 'fluent-smtp'),
'telegram_notify_status' => 'no'
], 200);
}
$siteToken = Arr::get($settings, 'telegram.token');
$connectionInfo = NotificationHelper::getTelegramConnectionInfo($siteToken);
if (is_wp_error($connectionInfo)) {
return $this->sendSuccess([
'telegram_notify_status' => 'failed',
'message' => $connectionInfo->get_error_message(),
'errors' => $connectionInfo->get_error_data(),
]);
}
return $this->sendSuccess([
'telegram_notify_status' => 'yes',
'telegram_receiver' => Arr::get($connectionInfo, 'telegram_receiver', []),
]);
}
public function sendTestMessage(Request $request)
{
// Let's update the notification status
$settings = (new Settings())->notificationSettings();
if (Arr::get($settings, 'telegram.status') != 'yes') {
return $this->sendError([
'message' => __('Telegram notification is not enabled', 'fluent-smtp')
], 422);
}
$result = NotificationHelper::sendTestTelegramMessage(Arr::get($settings, 'telegram.token'));
if (is_wp_error($result)) {
return $this->sendError([
'message' => $result->get_error_message(),
'errors' => $result->get_error_data(),
], 422);
}
return $this->sendSuccess([
'message' => __('Test message sent successfully', 'fluent-smtp')
]);
}
public function disconnect()
{
$settings = (new Settings())->notificationSettings();
if (Arr::get($settings, 'telegram.status') != 'yes') {
return $this->sendError([
'message' => __('Telegram notification is not enabled', 'fluent-smtp')
], 422);
}
NotificationHelper::disconnectTelegram(Arr::get($settings, 'telegram.token'));
return $this->sendSuccess([
'message' => __('Telegram connection has been disconnected successfully', 'fluent-smtp')
]);
}
}

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1,59 @@
<?php
$app->get('/', 'DashboardController@index');
$app->get('/day-time-stats', 'DashboardController@getDayTimeStats');
$app->get('sending_stats', 'DashboardController@getSendingStats');
$app->get('/settings', 'SettingsController@index');
$app->post('/settings/validate', 'SettingsController@validate');
$app->post('/settings', 'SettingsController@store');
$app->post('/misc-settings', 'SettingsController@storeMiscSettings');
$app->post('/settings/delete', 'SettingsController@delete');
$app->post('/settings/misc', 'SettingsController@storeGlobals');
$app->post('/settings/test', 'SettingsController@sendTestEmil');
$app->post('/settings/subscribe', 'SettingsController@subscribe');
$app->post('/settings/subscribe-dismiss', 'SettingsController@subscribeDismiss');
$app->get('settings/connection_info', 'SettingsController@getConnectionInfo');
$app->post('settings/add_new_sender_email', 'SettingsController@addNewSenderEmail');
$app->post('settings/remove_sender_email', 'SettingsController@removeSenderEmail');
$app->get('settings/notification-settings', 'SettingsController@getNotificationSettings');
$app->post('settings/notification-settings', 'SettingsController@saveNotificationSettings');
$app->post('settings/gmail_auth_url', 'SettingsController@getGmailAuthUrl');
$app->post('settings/outlook_auth_url', 'SettingsController@getOutlookAuthUrl');
/*
* Telegram Routes
*/
$app->post('settings/telegram/issue-pin-code', 'TelegramController@issuePinCode');
$app->post('settings/telegram/confirm', 'TelegramController@confirmConnection');
$app->get('settings/telegram/info', 'TelegramController@getTelegramConnectionInfo');
$app->post('settings/telegram/send-test', 'TelegramController@sendTestMessage');
$app->post('settings/telegram/disconnect', 'TelegramController@disconnect');
/*
* Slack Routes
*/
$app->post('settings/slack/register', 'SlackController@registerSite');
$app->post('settings/slack/send-test', 'SlackController@sendTestMessage');
$app->post('settings/slack/disconnect', 'SlackController@disconnect');
/*
* Discord Routes
*/
$app->post('settings/discord/register', 'DiscordController@registerSite');
$app->post('settings/discord/send-test', 'DiscordController@sendTestMessage');
$app->post('settings/discord/disconnect', 'DiscordController@disconnect');
$app->get('/logs', 'LoggerController@get');
$app->get('/logs/show', 'LoggerController@show');
$app->post('/logs/retry', 'LoggerController@retry');
$app->post('/logs/retry-bulk', 'LoggerController@retryBulk');
$app->post('/logs/delete', 'LoggerController@delete');
$app->post('install_plugin', 'SettingsController@installPlugin');
$app->get('docs', 'DashboardController@getDocs');

View File

@@ -0,0 +1,534 @@
<?php
namespace FluentMail\App\Models;
use Exception;
use InvalidArgumentException;
use FluentMail\Includes\Support\Arr;
class Logger extends Model
{
const STATUS_PENDING = 'pending';
const STATUS_FAILED = 'failed';
const STATUS_SENT = 'sent';
protected $fillables = [
'to',
'from',
'subject',
'body',
'status',
'response',
'extra',
'created_at'
];
protected $searchables = [
'to',
'from',
'subject'
];
protected $table = null;
public function __construct()
{
parent::__construct();
$this->table = $this->db->prefix . FLUENT_MAIL_DB_PREFIX . 'email_logs';
}
public function get($data)
{
$db = $this->getDb();
$page = isset($data['page']) ? (int)$data['page'] : 1;
$perPage = isset($data['per_page']) ? (int)$data['per_page'] : 15;
$offset = ($page - 1) * $perPage;
$query = $db->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->limit($perPage)
->offset($offset)
->orderBy('id', 'DESC');
if (!empty($data['status'])) {
$query->where('status', sanitize_text_field($data['status']));
}
if (!empty($data['date_range']) && is_array($data['date_range']) && count($data['date_range']) == 2) {
$dateRange = $data['date_range'];
$from = $dateRange[0] . ' 00:00:01';
$to = $dateRange[1] . ' 23:59:59';
$query->whereBetween('created_at', $from, $to);
}
if (!empty($data['search'])) {
$search = trim(sanitize_text_field($data['search']));
$query->where(function ($q) use ($search) {
$searchColumns = $this->searchables;
$columnSearch = false;
if (strpos($search, ':')) {
$searchArray = explode(':', $search);
$column = array_shift($searchArray);
if (in_array($column, $this->fillables)) {
$columnSearch = true;
$q->where($column, 'LIKE', '%' . trim(implode(':', $searchArray)) . '%');
}
}
if (!$columnSearch) {
$firstColumn = array_shift($searchColumns);
$q->where($firstColumn, 'LIKE', '%' . $search . '%');
foreach ($searchColumns as $column) {
$q->orWhere($column, 'LIKE', '%' . $search . '%');
}
}
});
}
$result = $query->paginate();
$result['data'] = $this->formatResult($result['data']);
return $result;
}
protected function buildWhere($data)
{
$where = [];
if (isset($data['filter_by_value'])) {
$where[$data['filter_by']] = $data['filter_by_value'];
}
if (isset($data['query'])) {
foreach ($this->searchables as $column) {
if (isset($where[$column])) {
$where[$column] .= '|' . $data['query'];
} else {
$where[$column] = $data['query'];
}
}
}
$args = [1];
$andWhere = $orWhere = '';
$whereClause = "WHERE 1 = '%d'";
foreach ($where as $key => $value) {
if (in_array($key, ['status', 'created_at'])) {
if ($key == 'created_at') {
if (is_array($value)) {
$args[] = $value[0];
$args[] = $value[1];
} else {
$args[] = $value;
$args[] = $value;
}
$andWhere .= " AND `{$key}` >= '%s' AND `{$key}` < '%s' + INTERVAL 1 DAY";
} else {
$args[] = $value;
$andWhere .= " AND `{$key}` = '%s'";
}
} else {
if (strpos($value, '|')) {
$nestedOr = '';
$values = explode('|', $value);
foreach ($values as $itemValue) {
$args[] = '%' . $this->db->esc_like($itemValue) . '%';
$nestedOr .= " OR `{$key}` LIKE '%s'";
}
$orWhere .= ' OR (' . trim($nestedOr, 'OR ') . ')';
} else {
$args[] = '%' . $this->db->esc_like($value) . '%';
$orWhere .= " OR `{$key}` LIKE '%s'";
}
}
}
if ($orWhere) {
$orWhere = 'AND (' . trim($orWhere, 'OR ') . ')';
}
$whereClause = implode(' ', [$whereClause, trim($andWhere), $orWhere]);
return [$whereClause, $args];
}
protected function formatResult($result)
{
$result = is_array($result) ? $result : func_get_args();
foreach ($result as $key => $row) {
$result[$key] = $this->maybeUnserialize((array)$row);
$result[$key]['id'] = (int)$result[$key]['id'];
$result[$key]['retries'] = (int)$result[$key]['retries'];
$result[$key]['from'] = htmlspecialchars($result[$key]['from']);
$result[$key]['subject'] = wp_kses_post(
wp_unslash($result[$key]['subject'])
);
}
return $result;
}
protected function maybeUnserialize(array $data)
{
foreach ($data as $key => $value) {
if ($this->isUnserializable($key)) {
$data[$key] = $this->unserialize($value);
}
}
return $data;
}
protected function isUnserializable($key)
{
$allowedFields = [
'to',
'headers',
'attachments',
'response',
'extra'
];
return in_array($key, $allowedFields);
}
protected function unserialize($data)
{
if (is_serialized($data)) {
if (preg_match('/(^|;)O:[0-9]+:/', $data)) {
return $data;
}
return unserialize(trim($data), ['allowed_classes' => false]);
}
return $data;
}
protected function formatHeaders($headers)
{
foreach ((array)$headers as $key => $header) {
if (is_array($header)) {
$header = $this->formatHeaders($header);
} else {
$header = htmlspecialchars($header);
}
$headers[$key] = $header;
}
return $headers;
}
public function add($data)
{
try {
$data = array_merge($data, [
'created_at' => current_time('mysql')
]);
return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->insert($data);
} catch (Exception $e) {
return $e;
}
}
public function delete(array $id)
{
if ($id && $id[0] == 'all') {
return $this->db->query("TRUNCATE TABLE {$this->table}");
}
$ids = array_filter($id, 'intval');
if ($ids) {
return $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->whereIn('id', $ids)
->delete();
}
return false;
}
public function navigate($data)
{
$filterBy = Arr::get($data, 'filter_by');
foreach (['date', 'daterange', 'datetime', 'datetimerange'] as $field) {
if ($filterBy == $field) {
$data['filter_by'] = 'created_at';
}
}
$id = $data['id'];
$dir = isset($data['dir']) ? $data['dir'] : null;
list($where, $args) = $this->buildWhere($data);
$args = array_merge($args, [$id]);
$sqlNext = "SELECT * FROM {$this->table} {$where} AND `id` > '%d' ORDER BY id LIMIT 2";
$sqlPrev = "SELECT * FROM {$this->table} {$where} AND `id` < '%d' ORDER BY id DESC LIMIT 2";
if ($dir == 'next') {
$query = $this->db->prepare($sqlNext, $args);
} else if ($dir == 'prev') {
$query = $this->db->prepare($sqlPrev, $args);
} else {
foreach (['next' => $sqlNext, 'prev' => $sqlPrev] as $key => $sql) {
$keyResult = $this->db->get_results(
$this->db->prepare($sql, $args)
);
$result[$key] = $this->formatResult($keyResult);
}
return $result;
}
$result = $this->db->get_results($query);
if (count($result) > 1) {
$next = true;
$prev = true;
} else {
if ($dir == 'next') {
$next = false;
$prev = true;
} else {
$next = true;
$prev = false;
}
}
return [
'log' => $result ? $this->formatResult($result[0])[0] : null,
'next' => $next,
'prev' => $prev
];
}
public function find($id)
{
$row = $this->getDb()->table(FLUENT_MAIL_DB_PREFIX . 'email_logs')
->where('id', $id)
->first();
$row->extra = $this->unserialize($row->extra);
$row->response = $this->unserialize($row->response);
return (array)$row;
}
public function resendEmailFromLog($id, $type = 'retry')
{
$email = $this->find($id);
$email['to'] = $this->unserialize($email['to']);
$email['headers'] = $this->unserialize($email['headers']);
$email['attachments'] = $this->unserialize($email['attachments']);
$email['extra'] = $this->unserialize($email['extra']);
// Convert PHPMailer attachment format to wp_mail format
$wpMailAttachments = [];
if (!empty($email['attachments']) && is_array($email['attachments'])) {
foreach ($email['attachments'] as $attachment) {
if (is_array($attachment)) {
// PHPMailer format: [path, filename, name, encoding, type, isString, disposition, cid]
if (isset($attachment[0]) && is_string($attachment[0])) {
$filePath = $attachment[0];
if (file_exists($filePath) && is_readable($filePath)) {
$wpMailAttachments[] = $filePath;
}
}
} elseif (is_string($attachment)) {
if (file_exists($attachment) && is_readable($attachment)) {
$wpMailAttachments[] = $attachment;
}
}
}
}
$headers = [];
foreach ($email['headers'] as $key => $value) {
if($key == 'content-type' && $value == 'multipart/alternative') {
$value = 'text/html';
}
if (is_array($value)) {
$values = [];
$value = array_filter($value);
foreach ($value as $v) {
if (is_array($v) && isset($v['email'])) {
$v = $v['email'];
}
$values[] = $v;
}
if ($values) {
$headers[] = "{$key}: " . implode(';', $values);
}
} else {
if ($value) {
$headers[] = "{$key}: $value";
}
}
}
$headers = array_merge($headers, [
'From: ' . $email['from']
]);
$to = [];
foreach ($email['to'] as $recipient) {
if (isset($recipient['name'])) {
$to[] = $recipient['name'] . ' <' . $recipient['email'] . '>';
} else {
$to[] = $recipient['email'];
}
}
try {
if (!defined('FLUENTMAIL_LOG_OFF')) {
define('FLUENTMAIL_LOG_OFF', true);
}
$result = wp_mail(
$to,
$email['subject'],
$email['body'],
$headers,
$wpMailAttachments // Use the converted attachment format
);
$updateData = [
'status' => 'sent',
'updated_at' => current_time('mysql'),
];
if (!$result && $type == 'check_realtime' && $email['status'] == 'failed') {
$updateData['status'] = 'failed';
}
if ($type == 'resend') {
$updateData['resent_count'] = intval($email['resent_count']) + 1;
} else {
$updateData['retries'] = intval($email['retries']) + 1;
}
if ($this->updateLog($updateData, ['id' => $id])) {
$email = $this->find($id);
$email['to'] = $this->unserialize($email['to']);
$email['headers'] = $this->unserialize($email['headers']);
$email['attachments'] = $this->unserialize($email['attachments']);
$email['extra'] = $this->unserialize($email['extra']);
return $email;
}
} catch (\PHPMailer\PHPMailer\Exception $e) {
throw $e;
}
}
public function updateLog($data, $where)
{
return $this->db->update($this->table, $data, $where);
}
public function getStats()
{
$succeeded = $this->db->get_var("select COUNT(id) from {$this->table} where status='sent'");
$failed = $this->db->get_var("select COUNT(id) from {$this->table} where status='failed'");
return [
'sent' => $succeeded,
'failed' => $failed
];
}
public function deleteLogsOlderThan($days)
{
try {
$date = gmdate('Y-m-d H:i:s', current_time('timestamp') - $days * DAY_IN_SECONDS);
$query = $this->db->prepare("DELETE FROM {$this->table} WHERE `created_at` < %s", $date);
return $this->db->query($query);
} catch (Exception $e) {
if (wp_get_environment_type() != 'production') {
error_log('Message: ' . $e->getMessage());
}
}
}
public function getTotalCountStat($status, $startDate, $endDate = false)
{
if ($endDate) {
$query = $this->db->prepare(
"SELECT COUNT(*)
FROM {$this->table}
WHERE status = %s
AND created_at >= %s
AND created_at <= %s",
$status,
$startDate,
$endDate
);
} else {
$query = $this->db->prepare(
"SELECT COUNT(*)
FROM {$this->table}
WHERE status = %s
AND created_at >= %s",
$status,
$startDate
);
}
return (int)$this->db->get_var($query);
}
public function getSubjectCountStat($status, $startDate, $endDate)
{
$query = $this->db->prepare(
"SELECT COUNT(DISTINCT(subject))
FROM {$this->table}
WHERE status = %s
AND created_at >= %s
AND created_at <= %s",
$status,
$startDate,
$endDate
);
return (int)$this->db->get_var($query);
}
public function getSubjectStat($status, $statDate, $endDate, $limit = 5)
{
$query = $this->db->prepare(
"SELECT subject,
COUNT(DISTINCT id) AS emails_sent
FROM {$this->table}
WHERE created_at >= %s
AND created_at <= %s
AND status = %s
GROUP BY subject
ORDER BY emails_sent DESC
LIMIT {$limit}",
$statDate,
$endDate,
$status
);
return $this->db->get_results($query, ARRAY_A);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FluentMail\App\Models;
class Model
{
protected $db = null;
protected $app = null;
public function __construct()
{
$this->app = fluentMail();
$this->db = $GLOBALS['wpdb'];
}
public function getTable()
{
return $this->table;
}
public function __call($method, $params)
{
return call_user_func_array([$this->db, $method], $params);
}
public function getDb()
{
return fluentMailDb();
}
}

View File

@@ -0,0 +1,257 @@
<?php
namespace FluentMail\App\Models;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Models\Traits\SendTestEmailTrait;
class Settings
{
use SendTestEmailTrait;
protected $optionName = FLUENTMAIL . '-settings';
public function get()
{
return fluentMailGetSettings();
}
public function getSettings()
{
return $this->get();
}
public function store($inputs)
{
$settings = $this->getSettings();
$mappings = $this->getMappings($settings);
$connections = $this->getConnections($settings);
$email = Arr::get($inputs, 'connection.sender_email');
$key = $inputs['connection_key'];
if (isset($connections[$key])) {
$mappings = array_filter($mappings, function ($mappingKey) use ($key) {
return $mappingKey != $key;
});
unset($connections[$key]);
}
$primaryEmails = [];
foreach ($connections as $connection) {
$primaryEmails[] = $connection['provider_settings']['sender_email'];
}
$uniqueKey = $this->generateUniqueKey($email);
$extraMappings = $inputs['valid_senders'];
foreach ($extraMappings as $emailIndex => $email) {
if (in_array($email, $primaryEmails)) {
unset($extraMappings[$emailIndex]);
}
}
$extraMappings[] = $email;
$extraMappings = array_unique($extraMappings);
$extraMappings = array_fill_keys($extraMappings, $uniqueKey);
$mappings = array_merge($mappings, $extraMappings);
$providers = fluentMail(Manager::class)->getConfig('providers');
$title = $providers[$inputs['connection']['provider']]['title'];
$connections[$uniqueKey] = [
'title' => $title,
'provider_settings' => $inputs['connection']
];
$settings['mappings'] = $mappings;
$settings['connections'] = $connections;
if ($settings['mappings'] && $settings['connections']) {
$validMappings = array_keys(Arr::get($settings, 'connections', []));
$settings['mappings'] = array_filter($settings['mappings'], function ($key) use ($validMappings) {
return in_array($key, $validMappings);
});
}
$misc = $this->getMisc();
if (!$misc) {
$misc = [
'log_emails' => 'yes',
'log_saved_interval_days' => '14',
'disable_fluentcrm_logs' => 'no',
'default_connection' => ''
];
}
if (empty($misc['default_connection']) || $misc['default_connection'] == $key) {
$misc['default_connection'] = $uniqueKey;
$settings['misc'] = $misc;
}
fluentMailSetSettings($settings);
return $settings;
}
public function generateUniqueKey($email)
{
return md5($email);
}
public function saveGlobalSettings($data)
{
return fluentMailSetSettings($data);
}
public function delete($key)
{
$settings = $this->getSettings();
$mappings = $settings['mappings'];
$connections = $settings['connections'];
unset($connections[$key]);
foreach ($mappings as $mapKey => $mapValue) {
if ($mapValue == $key) {
unset($mappings[$mapKey]);
}
}
$settings['mappings'] = $mappings;
$settings['connections'] = $connections;
if (Arr::get($settings, 'misc.default_connection') == $key) {
$default = Arr::get($settings, 'mappings', []);
$default = reset($default);
Arr::set($settings, 'misc.default_connection', $default ?: '');
}
if (Arr::get($settings, 'misc.fallback_connection') == $key) {
Arr::set($settings, 'misc.fallback_connection', '');
}
fluentMailSetSettings($settings);
return $settings;
}
public function getDefaults()
{
$url = str_replace(
['http://', 'http://www.', 'www.'],
'',
get_bloginfo('wpurl')
);
return [
'sender_name' => $url,
'sender_email' => get_option('admin_email')
];
}
public function getVerifiedEmails()
{
$optionName = FLUENTMAIL . '-ses-verified-emails';
return get_option($optionName, []);
}
public function saveVerifiedEmails($verifiedEmails)
{
$optionName = FLUENTMAIL . '-ses-verified-emails';
$emails = get_option($optionName, []);
update_option($optionName, array_unique(array_merge(
$emails, $verifiedEmails
)));
}
public function getConnections($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'connections', []);
}
public function getMappings($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'mappings', []);
}
public function getMisc($settings = null)
{
$settings = $settings ?: $this->getSettings();
return Arr::get($settings, 'misc', []);
}
public function getConnection($email)
{
$settings = $this->getSettings();
$mappings = $this->getMappings($settings);
$connections = $this->getConnections($settings);
if (isset($mappings[$email])) {
if (isset($connections[$mappings[$email]])) {
return $connections[$mappings[$email]];
}
}
return [];
}
public function updateMiscSettings($misc)
{
$settings = $this->get();
$settings['misc'] = $misc;
$this->saveGlobalSettings($settings);
}
public function updateConnection($fromEmail, $connection)
{
$key = $this->generateUniqueKey($fromEmail);
$settings = $this->getSettings();
$settings['connections'][$key]['provider_settings'] = $connection;
$this->saveGlobalSettings($settings);
}
public function notificationSettings()
{
$defaults = [
'enabled' => 'no',
'notify_email' => '{site_admin}',
'notify_days' => ['Mon'],
'active_channel' => '',
'telegram' => [
'status' => 'no',
'token' => ''
],
'slack' => [
'status' => 'no',
'token' => '',
'webhook_url' => ''
],
'discord' => [
'status' => 'no',
'channel_name' => '',
'webhook_url' => ''
],
];
$settings = get_option('_fluent_smtp_notify_settings', []);
return wp_parse_args($settings, $defaults);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FluentMail\App\Models\Traits;
trait SendTestEmailTrait
{
public function sendTestEmail($data, $settings)
{
if (empty($settings) || empty($data)) return;
$to = $data['email'];
$subject = sprintf(__('Fluent SMTP: Test Email - %s', 'fluent-smtp'), get_bloginfo('name'));
if ($data['isHtml'] == 'true') {
$headers[] = 'Content-Type: text/html; charset=UTF-8';
$body = (string) fluentMail('view')->make('admin.email_html');
$subject .= ' - HTML Version';
} else {
$headers[] = 'Content-Type: text/plain; charset=UTF-8';
$body = (string) fluentMail('view')->make('admin.email_text');
$subject .= ' - Text Version';
}
if (!empty($data['from'])) {
$headers[] = 'From: ' . $data['from'];
}
if(!defined('FLUENTMAIL_TEST_EMAIL')) {
define('FLUENTMAIL_TEST_EMAIL', true);
}
return wp_mail($to, $subject, $body, $headers);
}
}

View File

@@ -0,0 +1,484 @@
<?php
namespace FluentMail\App\Services;
use FluentMail\Includes\Support\Arr;
class Converter
{
public function getSuggestedConnection()
{
$wpMailSmtp = $this->maybeWPMailSmtp();
if($wpMailSmtp) {
return $wpMailSmtp;
}
$easySMTP = $this->maybeEasySmtp();
if($easySMTP) {
return $easySMTP;
}
return false;
}
private function maybeWPMailSmtp()
{
$wpMailSettings = get_option('wp_mail_smtp');
if (!$wpMailSettings) {
return false;
}
$mailSettings = Arr::get($wpMailSettings, 'mail', []);
$commonSettings = [
'sender_name' => $this->maybeFromWPMailDefined('mail', 'from_name', Arr::get($mailSettings, 'from_name')),
'sender_email' => $this->maybeFromWPMailDefined('mail', 'from_email', Arr::get($mailSettings, 'from_email')),
'force_from_name' => Arr::get($mailSettings, 'from_name_force') == 1 ? 'yes' : 'no',
'force_from_email' => Arr::get($mailSettings, 'from_email_force') == 1 ? 'yes' : 'no',
'return_path' => Arr::get($mailSettings, 'return_path') == 1 ? 'yes' : 'no'
];
// Let's try the SMTP First
$mailer = Arr::get($mailSettings, 'mailer');
if ($mailer == 'smtp') {
$smtp = Arr::get($wpMailSettings, 'smtp', []);
$auth = $this->maybeFromWPMailDefined('smtp', 'auth', Arr::get($smtp, 'auth')) == 1 ? 'yes' : 'no';
$userName = $this->maybeFromWPMailDefined('smtp', 'user', Arr::get($smtp, 'user'));
$password = $this->maybeFromWPMailDefined('smtp', 'pass', '');
if ($auth == 'yes') {
if (!$password) {
$password = $this->wpMailPassDecode(Arr::get($smtp, 'pass'));
}
}
$localSettings = [
'host' => $this->maybeFromWPMailDefined('smtp', 'host', Arr::get($smtp, 'host')),
'port' => $this->maybeFromWPMailDefined('smtp', 'port', Arr::get($smtp, 'port')),
'auth' => $auth,
'username' => $userName,
'password' => $password,
'auto_tls' => $this->maybeFromWPMailDefined('smtp', 'auto_tls', Arr::get($smtp, 'auto_tls')) == 1 ? 'yes' : 'no',
'encryption' => $this->maybeFromWPMailDefined('smtp', 'encryption', Arr::get($smtp, 'encryption', 'none')),
'key_store' => 'db',
'provider' => 'smtp'
];
$commonSettings = wp_parse_args($commonSettings, $localSettings);
} else if ($mailer == 'mailgun') {
$mailgun = Arr::get($wpMailSettings, 'mailgun', []);
$localSettings = [
'api_key' => $this->maybeFromWPMailDefined('mailgun', 'api_key', Arr::get($mailgun, 'api_key')),
'domain_name' => $this->maybeFromWPMailDefined('mailgun', 'domain', Arr::get($mailgun, 'domain')),
'key_store' => 'db',
'region' => strtolower($this->maybeFromWPMailDefined('mailgun', 'region', Arr::get($mailgun, 'region'))),
'provider' => 'mailgun'
];
$commonSettings = wp_parse_args($commonSettings, $localSettings);
unset($commonSettings['force_from_email']);
} else if ($mailer == 'sendinblue' || $mailer == 'sendgrid' || $mailer == 'pepipostapi' || $mailer == 'smtp2go') {
$local = Arr::get($wpMailSettings, $mailer, []);
$localSettings = [
'api_key' => $this->maybeFromWPMailDefined($mailer, 'api_key', Arr::get($local, 'api_key')),
'key_store' => 'db',
'provider' => ($mailer == 'pepipostapi') ? 'pepipost' : $mailer
];
$commonSettings = wp_parse_args($commonSettings, $localSettings);
unset($commonSettings['force_from_email']);
} else if ($mailer == 'amazonses') {
$local = Arr::get($wpMailSettings, $mailer, []);
$localSettings = [
'access_key' => $this->maybeFromWPMailDefined($mailer, 'client_id', Arr::get($local, 'client_id')),
'secret_key' => $this->maybeFromWPMailDefined($mailer, 'client_secret', Arr::get($local, 'client_secret')),
'region' => $this->maybeFromWPMailDefined($mailer, 'region', Arr::get($local, 'region')),
'key_store' => 'db',
'provider' => 'ses'
];
$commonSettings = wp_parse_args($commonSettings, $localSettings);
} else if ($mailer == 'mail') {
$commonSettings['provider'] = 'default';
} else {
return false;
}
return [
'title' => __('Import data from your current plugin (WP Mail SMTP)', 'fluent-smtp'),
'subtitle' => __('We have detected other SMTP plugin\'s settings available on your site. Click bellow to pre-populate the values', 'fluent-smtp'),
'settings' => $commonSettings,
'button_text' => __('Import From WP Mail SMTP', 'fluent-smtp')
];
}
private function wpMailPassDecode($encrypted)
{
if (apply_filters('wp_mail_smtp_helpers_crypto_stop', false)) {
return $encrypted;
}
if (!function_exists('\mb_strlen') || !function_exists('\mb_substr') || !function_exists('\sodium_crypto_secretbox_open')) {
return $encrypted;
}
// Unpack base64 message.
$decoded = base64_decode($encrypted); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
if (false === $decoded) {
return $encrypted;
}
if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) { // phpcs:ignore
return $encrypted;
}
// Pull nonce and ciphertext out of unpacked message.
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit'); // phpcs:ignore
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit'); // phpcs:ignore
$secret_key = $this->getWPMailSecretKey();
if (empty($secret_key)) {
return $encrypted;
}
// Decrypt it.
$message = sodium_crypto_secretbox_open( // phpcs:ignore
$ciphertext,
$nonce,
$secret_key
);
// Check for decryption failures.
if (false === $message) {
return $encrypted;
}
return $message;
}
private function getWPMailSecretKey()
{
if (defined('WPMS_CRYPTO_KEY')) {
return WPMS_CRYPTO_KEY;
}
$secret_key = get_option('wp_mail_smtp_mail_key');
$secret_key = apply_filters('wp_mail_smtp_helpers_crypto_get_secret_key', $secret_key);
// If we already have the secret, send it back.
if (false !== $secret_key) {
$secret_key = base64_decode($secret_key); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
}
return $secret_key;
}
private function maybeFromWPMailDefined($group, $key, $value)
{
if (!defined('WPMS_ON') || !WPMS_ON) {
return $value;
}
// Just to feel safe.
$group = sanitize_key($group);
$key = sanitize_key($key);
$return = false;
switch ($group) {
case 'mail':
switch ($key) {
case 'from_name':
if (defined('WPMS_MAIL_FROM_NAME') && WPMS_MAIL_FROM_NAME) {
$value = WPMS_MAIL_FROM_NAME;
}
break;
case 'from_email':
if (defined('WPMS_MAIL_FROM') && WPMS_MAIL_FROM) {
$value = WPMS_MAIL_FROM;
}
break;
case 'mailer':
if (defined('WPMS_MAILER') && WPMS_MAILER) {
$value = WPMS_MAILER;
}
break;
case 'return_path':
if (defined('WPMS_SET_RETURN_PATH') && WPMS_SET_RETURN_PATH) {
$value = WPMS_SET_RETURN_PATH;
}
break;
case 'from_name_force':
if (defined('WPMS_MAIL_FROM_NAME_FORCE') && WPMS_MAIL_FROM_NAME_FORCE) {
$value = WPMS_MAIL_FROM_NAME_FORCE;
}
break;
case 'from_email_force':
if (defined('WPMS_MAIL_FROM_FORCE') && WPMS_MAIL_FROM_FORCE) {
$value = WPMS_MAIL_FROM_FORCE;
}
break;
}
break;
case 'smtp':
switch ($key) {
case 'host':
if (defined('WPMS_SMTP_HOST') && WPMS_SMTP_HOST) {
$value = WPMS_SMTP_HOST;
}
break;
case 'port':
if (defined('WPMS_SMTP_PORT') && WPMS_SMTP_PORT) {
$value = WPMS_SMTP_PORT;
}
break;
case 'encryption':
if (defined('WPMS_SSL') && WPMS_SSL) {
$value = WPMS_SSL;
}
break;
case 'auth':
if (defined('WPMS_SMTP_AUTH') && WPMS_SMTP_AUTH) {
$value = WPMS_SMTP_AUTH;
}
break;
case 'autotls':
if (defined('WPMS_SMTP_AUTOTLS') && WPMS_SMTP_AUTOTLS) {
$value = WPMS_SMTP_AUTOTLS;
}
break;
case 'user':
if (defined('WPMS_SMTP_USER') && WPMS_SMTP_USER) {
$value = WPMS_SMTP_USER;
}
break;
case 'pass':
if (defined('WPMS_SMTP_PASS') && WPMS_SMTP_PASS) {
$value = WPMS_SMTP_PASS;
}
break;
}
break;
case 'amazonses':
switch ($key) {
case 'client_id':
if (defined('WPMS_AMAZONSES_CLIENT_ID') && WPMS_AMAZONSES_CLIENT_ID) {
$value = WPMS_AMAZONSES_CLIENT_ID;
}
break;
case 'client_secret':
if (defined('WPMS_AMAZONSES_CLIENT_SECRET') && WPMS_AMAZONSES_CLIENT_SECRET) {
$value = WPMS_AMAZONSES_CLIENT_SECRET;
}
break;
case 'region':
if (defined('WPMS_AMAZONSES_REGION') && WPMS_AMAZONSES_REGION) {
$value = WPMS_AMAZONSES_REGION;
}
break;
}
break;
case 'mailgun':
switch ($key) {
case 'api_key':
if (defined('WPMS_MAILGUN_API_KEY') && WPMS_MAILGUN_API_KEY) {
$value = WPMS_MAILGUN_API_KEY;
}
break;
case 'domain':
if (defined('WPMS_MAILGUN_DOMAIN') && WPMS_MAILGUN_DOMAIN) {
$value = WPMS_MAILGUN_DOMAIN;
}
break;
case 'region':
if (defined('WPMS_MAILGUN_REGION') && WPMS_MAILGUN_REGION) {
$value = WPMS_MAILGUN_REGION;
}
break;
}
break;
case 'sendgrid':
switch ($key) {
case 'api_key':
if (defined('WPMS_SENDGRID_API_KEY') && WPMS_SENDGRID_API_KEY) {
$value = WPMS_SENDGRID_API_KEY;
}
break;
case 'domain':
if (defined('WPMS_SENDGRID_DOMAIN') && WPMS_SENDGRID_DOMAIN) {
$value = WPMS_SENDGRID_DOMAIN;
}
break;
}
break;
case 'sendinblue':
switch ($key) {
case 'api_key':
if (defined('WPMS_SENDINBLUE_API_KEY') && WPMS_SENDINBLUE_API_KEY) {
$value = WPMS_SENDINBLUE_API_KEY;
}
break;
case 'domain':
if (defined('WPMS_SENDINBLUE_DOMAIN') && WPMS_SENDINBLUE_DOMAIN) {
$value = WPMS_SENDINBLUE_DOMAIN;
}
break;
}
break;
case 'pepipostapi':
switch ($key) {
case 'api_key':
if (defined('WPMS_PEPIPOST_API_KEY') && WPMS_PEPIPOST_API_KEY) {
$value = WPMS_PEPIPOST_API_KEY;
}
break;
}
break;
case 'elasticmail':
switch ($key) {
case 'api_key':
if (defined('FLUENTMAIL_ELASTICMAIL_API_KEY') && FLUENTMAIL_ELASTICMAIL_API_KEY) {
$value = FLUENTMAIL_ELASTICMAIL_API_KEY;
}
break;
}
break;
}
return $value;
}
/*
* For EasySMTP
*/
private function maybeEasySmtp()
{
$settings = get_option('swpsmtp_options');
if (!$settings || !is_array($settings)) {
return false;
}
$auth = 'no';
if (Arr::get($settings, 'smtp_settings.autentication')) {
$auth = 'yes';
}
$commonSettings = [
'sender_name' => Arr::get($settings, 'from_name_field'),
'sender_email' => Arr::get($settings, 'from_email_field'),
'force_from_name' => Arr::get($settings, 'force_from_name_replace') == 1 ? 'yes' : 'no',
'force_from_email' => 'yes',
'return_path' => 'yes',
'host' => Arr::get($settings, 'smtp_settings.host'),
'port' => Arr::get($settings, 'smtp_settings.port'),
'auth' => $auth,
'username' => Arr::get($settings, 'smtp_settings.username'),
'password' => $this->decryptEasySMTPPass(Arr::get($settings, 'smtp_settings.password')),
'auto_tls' => Arr::get($settings, 'smtp_settings.password') == 1 ? 'yes' : 'no',
'encryption' => Arr::get($settings, 'smtp_settings.type_encryption'),
'key_store' => 'db',
'provider' => 'smtp'
];
return [
'title' => __('Import data from your current plugin (Easy WP SMTP)', 'fluent-smtp'),
'subtitle' => __('We have detected other SMTP plugin\'s settings available on your site. Click bellow to pre-populate the values', 'fluent-smtp'),
'driver' => 'smtp',
'settings' => $commonSettings,
'button_text' => __('Import From Easy WP SMTP', 'fluent-smtp')
];
}
private function decryptEasySMTPPass($temp_password)
{
if (!$temp_password) {
return $temp_password;
}
try {
if (get_option('swpsmtp_pass_encrypted')) {
$key = get_option('swpsmtp_enc_key', false);
if (empty($key)) {
$key = wp_salt();
}
return $this->decryptEasypassword($temp_password, $key);
}
} catch (\Exception $e) {
return $temp_password;
}
$password = '';
$decoded_pass = base64_decode($temp_password); //phpcs:ignore
/* no additional checks for servers that aren't configured with mbstring enabled */
if (!function_exists('mb_detect_encoding')) {
return $decoded_pass;
}
/* end of mbstring check */
if (base64_encode($decoded_pass) === $temp_password) { //phpcs:ignore
//it might be encoded
if (false === mb_detect_encoding($decoded_pass)) { //could not find character encoding.
$password = $temp_password;
} else {
$password = base64_decode($temp_password); //phpcs:ignore
}
} else { //not encoded
$password = $temp_password;
}
return stripslashes($password);
}
private function decryptEasyPassword($in, $key, $fmt = 1)
{
if (!function_exists('\openssl_cipher_iv_length') || !function_exists('\openssl_decrypt') || !function_exists('\openssl_digest')) {
return $in;
}
$raw = base64_decode($in);
$iv_num_bytes = openssl_cipher_iv_length('aes-256-ctr');
// and do an integrity check on the size.
if (strlen($raw) < $iv_num_bytes) {
return $in;
}
// Extract the initialisation vector and encrypted data
$iv = substr($raw, 0, $iv_num_bytes);
$raw = substr($raw, $iv_num_bytes);
$hasAlgo = 'sha256';
// Hash the key
$keyhash = openssl_digest($key, $hasAlgo, true);
// and decrypt.
$opts = 1;
$res = openssl_decrypt($raw, 'aes-256-ctr', $keyhash, $opts, $iv);
if ($res === false) {
return $in;
}
return $res;
}
}

View File

@@ -0,0 +1,43 @@
<?php namespace FluentMail\App\Services\DB;
use FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler;
/**
* This class gives the ability to access non-static methods statically
*
* Class AliasFacade
*
* @package FluentAuthDb
*/
class AliasFacade
{
/**
* @var QueryBuilderHandler
*/
protected static $queryBuilderInstance;
/**
* @param $method
* @param $args
*
* @return mixed
*/
public static function __callStatic($method, $args)
{
if (!static::$queryBuilderInstance) {
static::$queryBuilderInstance = new QueryBuilderHandler();
}
// Call the non-static method from the class instance
return call_user_func_array(array(static::$queryBuilderInstance, $method), $args);
}
/**
* @param QueryBuilderHandler $queryBuilderInstance
*/
public static function setQueryBuilderInstance($queryBuilderInstance)
{
static::$queryBuilderInstance = $queryBuilderInstance;
}
}

View File

@@ -0,0 +1,187 @@
<?php
namespace FluentMail\App\Services\DB;
use FluentMail\App\Services\DB\Viocon\Container;
class Connection
{
/**
* @var Container
*/
protected $container;
/**
* @var string
*/
protected $adapter;
/**
* @var array
*/
protected $adapterConfig;
/**
* @var \wpdb $wpdb
*/
protected $dbInstance;
/**
* @var \wpdb $wpdb
*/
protected $wpdb;
/**
* @var Connection
*/
protected static $storedConnection;
/**
* @var EventHandler
*/
protected $eventHandler;
/**
* @param $wpdb
* @param array $adapterConfig
* @param null|string $alias
* @param null|Container $container
*/
public function __construct($wpdb, array $config = array(), $alias = null, ?Container $container = null)
{
$container = $container ? : new Container();
$this->container = $container;
$this->wpdb = $wpdb;
$this->setAdapter()->setAdapterConfig($config)->connect();
// Create event dependency
$this->eventHandler = $this->container->build('\\FluentMail\\App\\Services\\DB\\EventHandler');
if ($alias) {
$this->createAlias($alias);
}
}
/**
* Create an easily accessible query builder alias
*
* @param $alias
*/
public function createAlias($alias)
{
class_alias('FluentMail\\App\\Services\\DB\\AliasFacade', $alias);
$builder = $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this));
AliasFacade::setQueryBuilderInstance($builder);
}
/**
* Returns an instance of Query Builder
*/
public function getQueryBuilder()
{
return $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this));
}
/**
* Create the connection adapter
*/
protected function connect()
{
$this->setDbInstance($this->wpdb);
// Preserve the first database connection with a static property
if (! static::$storedConnection) {
static::$storedConnection = $this;
}
}
/**
* @param $db
*
* @return $this
*/
public function setDbInstance($db)
{
$this->dbInstance = $db;
return $this;
}
/**
* @return \wpdb
*/
public function getDbInstance()
{
return $this->dbInstance;
}
/**
* @param $adapter
*
* @return $this
*/
public function setAdapter($adapter = 'mysql')
{
$this->adapter = $adapter;
return $this;
}
/**
* @return string
*/
public function getAdapter()
{
return $this->adapter;
}
/**
* @param array $adapterConfig
*
* @return $this
*/
public function setAdapterConfig(array $adapterConfig)
{
$this->adapterConfig = $adapterConfig;
return $this;
}
/**
* @return array
*/
public function getAdapterConfig()
{
return $this->adapterConfig;
}
/**
* @return \FluentMail\App\Services\DB\Viocon\Container
*/
public function getContainer()
{
return $this->container;
}
/**
* @return EventHandler
*/
public function getEventHandler()
{
return $this->eventHandler;
}
/**
* @return Connection
*/
public static function getStoredConnection()
{
return static::$storedConnection;
}
}

View File

@@ -0,0 +1,99 @@
<?php namespace FluentMail\App\Services\DB;
use FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler;
use FluentMail\App\Services\DB\QueryBuilder\Raw;
class EventHandler
{
/**
* @var array
*/
protected $events = array();
/**
* @var array
*/
protected $firedEvents = array();
/**
* @return array
*/
public function getEvents()
{
return $this->events;
}
/**
* @param $event
* @param $table
*
* @return callable|null
*/
public function getEvent($event, $table = ':any')
{
if ($table instanceof Raw) {
return null;
}
return isset($this->events[$table][$event]) ? $this->events[$table][$event] : null;
}
/**
* @param $event
* @param string $table
* @param callable $action
*
* @return void
*/
public function registerEvent($event, $table, \Closure $action)
{
$table = $table ?: ':any';
$this->events[$table][$event] = $action;
}
/**
* @param $event
* @param string $table
*
* @return void
*/
public function removeEvent($event, $table = ':any')
{
unset($this->events[$table][$event]);
}
/**
* @param \FluentMail\App\Services\DB\src\QueryBuilder\QueryBuilderHandler $queryBuilder
* @param $event
* @return mixed
*/
public function fireEvents($queryBuilder, $event)
{
$originalArgs = func_get_args();
$statements = $queryBuilder->getStatements();
$tables = isset($statements['tables']) ? $statements['tables'] : array();
// Events added with :any will be fired in case of any table,
// we are adding :any as a fake table at the beginning.
array_unshift($tables, ':any');
// Fire all events
foreach ($tables as $table) {
// Fire before events for :any table
if ($action = $this->getEvent($event, $table)) {
// Make an event id, with event type and table
$eventId = $event . $table;
// Fire event
$handlerParams = $originalArgs;
unset($handlerParams[1]); // we do not need $event
// Add to fired list
$this->firedEvents[] = $eventId;
$result = call_user_func_array($action, $handlerParams);
if (!is_null($result)) {
return $result;
};
}
}
}
}

View File

@@ -0,0 +1,6 @@
<?php namespace FluentMail\App\Services\DB;
class Exception extends \Exception
{
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Muhammad Usman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,545 @@
<?php namespace FluentMail\App\Services\DB\QueryBuilder\Adapters;
use FluentMail\App\Services\DB\Connection;
use FluentMail\App\Services\DB\Exception;
use FluentMail\App\Services\DB\QueryBuilder\Raw;
abstract class BaseAdapter
{
/**
* @var \FluentMail\App\Services\DB\Connection
*/
protected $connection;
/**
* @var \FluentMail\App\Services\DB\Viocon\Container
*/
protected $container;
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->container = $this->connection->getContainer();
}
/**
* Build select query string and bindings
*
* @param $statements
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
public function select($statements)
{
if (! array_key_exists('tables', $statements)) {
throw new Exception('No table specified.', 3);
} elseif (! array_key_exists('selects', $statements)) {
$statements['selects'][] = '*';
}
// From
$tables = $this->arrayStr($statements['tables'], ', ');
// Select
$selects = $this->arrayStr($statements['selects'], ', ');
// Wheres
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
// Group bys
$groupBys = '';
if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
$groupBys = 'GROUP BY ' . $groupBys;
}
// Order bys
$orderBys = '';
if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
foreach ($statements['orderBys'] as $orderBy) {
$orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', ';
}
if ($orderBys = trim($orderBys, ', ')) {
$orderBys = 'ORDER BY ' . $orderBys;
}
}
// Limit and offset
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
$offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : '';
// Having
list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
// Joins
$joinString = $this->buildJoin($statements);
$sqlArray = array(
'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
$selects,
'FROM',
$tables,
$joinString,
$whereCriteria,
$groupBys,
$havingCriteria,
$orderBys,
$limit,
$offset
);
$sql = $this->concatenateQuery($sqlArray);
$bindings = array_merge(
$whereBindings,
$havingBindings
);
return compact('sql', 'bindings');
}
/**
* Build just criteria part of the query
*
* @param $statements
* @param bool $bindValues
*
* @return array
*/
public function criteriaOnly($statements, $bindValues = true)
{
$sql = $bindings = array();
if (! isset($statements['criteria'])) {
return compact('sql', 'bindings');
}
list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
return compact('sql', 'bindings');
}
/**
* Build a generic insert/ignore/replace query
*
* @param $statements
* @param array $data
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
private function doInsert($statements, array $data, $type)
{
if (! isset($statements['tables'])) {
throw new Exception('No table specified', 3);
}
$table = end($statements['tables']);
$bindings = $keys = $values = array();
foreach ($data as $key => $value) {
$keys[] = $key;
if ($value instanceof Raw) {
$values[] = (string) $value;
} else {
$values[] = '?';
$bindings[] = $value;
}
}
$sqlArray = array(
$type . ' INTO',
$this->wrapSanitizer($table),
'(' . $this->arrayStr($keys, ',') . ')',
'VALUES',
'(' . $this->arrayStr($values, ',', false) . ')',
);
if (isset($statements['onduplicate'])) {
if (count($statements['onduplicate']) < 1) {
throw new Exception('No data given.', 4);
}
list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
$sqlArray[] = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
$bindings = array_merge($bindings, $updateBindings);
}
$sql = $this->concatenateQuery($sqlArray);
return compact('sql', 'bindings');
}
/**
* Build Insert query
*
* @param $statements
* @param array $data
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
public function insert($statements, array $data)
{
return $this->doInsert($statements, $data, 'INSERT');
}
/**
* Build Insert Ignore query
*
* @param $statements
* @param array $data
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
public function insertIgnore($statements, array $data)
{
return $this->doInsert($statements, $data, 'INSERT IGNORE');
}
/**
* Build Insert Ignore query
*
* @param $statements
* @param array $data
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
public function replace($statements, array $data)
{
return $this->doInsert($statements, $data, 'REPLACE');
}
/**
* Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
*
* @param array $data
*
* @return array
*/
private function getUpdateStatement($data)
{
$bindings = array();
$statement = '';
foreach ($data as $key => $value) {
if ($value instanceof Raw) {
$statement .= $this->wrapSanitizer($key) . '=' . $value . ',';
} else {
$statement .= $this->wrapSanitizer($key) . '=?,';
$bindings[] = $value;
}
}
$statement = trim($statement, ',');
return array($statement, $bindings);
}
/**
* Build update query
*
* @param $statements
* @param array $data
*
* @return array
* @throws \FluentMail\App\Services\DB\Exception
*/
public function update($statements, array $data)
{
if (! isset($statements['tables'])) {
throw new Exception('No table specified', 3);
} elseif (count($data) < 1) {
throw new Exception('No data given.', 4);
}
$table = end($statements['tables']);
// Update statement
list($updateStatement, $bindings) = $this->getUpdateStatement($data);
// Wheres
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
// Limit
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
$sqlArray = array(
'UPDATE',
$this->wrapSanitizer($table),
'SET ' . $updateStatement,
$whereCriteria,
$limit
);
$sql = $this->concatenateQuery($sqlArray);
$bindings = array_merge($bindings, $whereBindings);
return compact('sql', 'bindings');
}
/**
* Build delete query
*
* @param $statements
*
* @return array
* @throws \FluentMail\App\Services\DB\src\Exception
*/
public function delete($statements)
{
if (! isset($statements['tables'])) {
throw new Exception('No table specified', 3);
}
$table = end($statements['tables']);
// Wheres
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
// Limit
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
$sqlArray = array('DELETE FROM', $this->wrapSanitizer($table), $whereCriteria);
$sql = $this->concatenateQuery($sqlArray);
$bindings = $whereBindings;
return compact('sql', 'bindings');
}
/**
* Array concatenating method, like implode.
* But it does wrap sanitizer and trims last glue
*
* @param array $pieces
* @param $glue
* @param bool $wrapSanitizer
*
* @return string
*/
protected function arrayStr(array $pieces, $glue, $wrapSanitizer = true)
{
$str = '';
foreach ($pieces as $key => $piece) {
if ($wrapSanitizer) {
$piece = $this->wrapSanitizer($piece);
}
if (! is_int($key)) {
$piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece;
}
$str .= $piece . $glue;
}
return trim($str, $glue);
}
/**
* Join different part of queries with a space.
*
* @param array $pieces
*
* @return string
*/
protected function concatenateQuery(array $pieces)
{
$str = '';
foreach ($pieces as $piece) {
$str = trim($str) . ' ' . trim($piece);
}
return trim($str);
}
/**
* Build generic criteria string and bindings from statements, like "a = b and c = ?"
*
* @param $statements
* @param bool $bindValues
*
* @return array
*/
protected function buildCriteria($statements, $bindValues = true)
{
$criteria = '';
$bindings = array();
foreach ($statements as $statement) {
$key = $this->wrapSanitizer($statement['key']);
$value = $statement['value'];
if (is_null($value) && $key instanceof \Closure) {
// We have a closure, a nested criteria
// Build a new NestedCriteria class, keep it by reference so any changes made
// in the closure should reflect here
$nestedCriteria = $this->container->build(
'\\FluentMail\\App\\Services\\DB\\QueryBuilder\\NestedCriteria',
array($this->connection)
);
$nestedCriteria = & $nestedCriteria;
// Call the closure with our new nestedCriteria object
$key($nestedCriteria);
// Get the criteria only query from the nestedCriteria object
$queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
// Merge the bindings we get from nestedCriteria object
$bindings = array_merge($bindings, $queryObject->getBindings());
// Append the sql we get from the nestedCriteria object
$criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
} elseif (is_array($value)) {
// where_in or between like query
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
switch ($statement['operator']) {
case 'BETWEEN':
$bindings = array_merge($bindings, $statement['value']);
$criteria .= ' ? AND ? ';
break;
default:
$valuePlaceholder = '';
foreach ($statement['value'] as $subValue) {
$valuePlaceholder .= '?, ';
$bindings[] = $subValue;
}
$valuePlaceholder = trim($valuePlaceholder, ', ');
$criteria .= ' (' . $valuePlaceholder . ') ';
break;
}
} elseif ($value instanceof Raw) {
$criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
} else {
// Usual where like criteria
if (! $bindValues) {
// Specially for joins
// We are not binding values, lets sanitize then
$value = $this->wrapSanitizer($value);
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
} elseif ($statement['key'] instanceof Raw) {
$criteria .= $statement['joiner'] . ' ' . $key . ' ';
$bindings = array_merge($bindings, $statement['key']->getBindings());
} else {
// For wheres
$valuePlaceholder = '?';
$bindings[] = $value;
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
. $valuePlaceholder . ' ';
}
}
}
// Clear all white spaces, and, or from beginning and white spaces from ending
$criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
return array($criteria, $bindings);
}
/**
* Wrap values with adapter's sanitizer like, '`'
*
* @param $value
*
* @return string
*/
public function wrapSanitizer($value)
{
// Its a raw query, just cast as string, object has __toString()
if ($value instanceof Raw) {
return (string) $value;
} elseif ($value instanceof \Closure) {
return $value;
}
// Separate our table and fields which are joined with a ".",
// like my_table.id
$valueArr = explode('.', $value, 2);
foreach ($valueArr as $key => $subValue) {
// Don't wrap if we have *, which is not a usual field
$valueArr[$key] = trim($subValue) == '*' ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
}
// Join these back with "." and return
return implode('.', $valueArr);
}
/**
* Build criteria string and binding with various types added, like WHERE and Having
*
* @param $statements
* @param $key
* @param $type
* @param bool $bindValues
*
* @return array
*/
protected function buildCriteriaWithType($statements, $key, $type, $bindValues = true)
{
$criteria = '';
$bindings = array();
if (isset($statements[$key])) {
// Get the generic/adapter agnostic criteria string from parent
list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
if ($criteria) {
$criteria = $type . ' ' . $criteria;
}
}
return array($criteria, $bindings);
}
/**
* Build join string
*
* @param $statements
*
* @return array|string
*/
protected function buildJoin($statements)
{
$sql = '';
if (! array_key_exists('joins', $statements) || ! is_array($statements['joins'])) {
return $sql;
}
foreach ($statements['joins'] as $joinArr) {
if (is_array($joinArr['table'])) {
$mainTable = $joinArr['table'][0];
$aliasTable = $joinArr['table'][1];
$table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable);
} else {
$table = $joinArr['table'] instanceof Raw ?
(string) $joinArr['table'] :
$this->wrapSanitizer($joinArr['table']);
}
$joinBuilder = $joinArr['joinBuilder'];
$sqlArr = array(
$sql,
strtoupper($joinArr['type']),
'JOIN',
$table,
'ON',
$joinBuilder->getQuery('criteriaOnly', false)->getSql()
);
$sql = $this->concatenateQuery($sqlArr);
}
return $sql;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace FluentMail\App\Services\DB\QueryBuilder\Adapters;
class Mysql extends BaseAdapter
{
/**
* @var string
*/
protected $sanitizer = '`';
}

View File

@@ -0,0 +1,45 @@
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
class JoinBuilder extends QueryBuilderHandler
{
/**
* @param $key
* @param $operator
* @param $value
*
* @return $this
*/
public function on($key, $operator, $value)
{
return $this->joinHandler($key, $operator, $value, 'AND');
}
/**
* @param $key
* @param $operator
* @param $value
*
* @return $this
*/
public function orOn($key, $operator, $value)
{
return $this->joinHandler($key, $operator, $value, 'OR');
}
/**
* @param $key
* @param null $operator
* @param null $value
* @param string $joiner
*
* @return $this
*/
protected function joinHandler($key, $operator = null, $value = null, $joiner = 'AND')
{
$key = $this->addTablePrefix($key);
$value = $this->addTablePrefix($value);
$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
return $this;
}
}

View File

@@ -0,0 +1,20 @@
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
class NestedCriteria extends QueryBuilderHandler
{
/**
* @param $key
* @param null $operator
* @param null $value
* @param string $joiner
*
* @return $this
*/
protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
{
$key = $this->addTablePrefix($key);
$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
<?php
namespace FluentMail\App\Services\DB\QueryBuilder;
class QueryObject
{
/**
* @var string
*/
protected $sql;
/**
* @var \wpdb
*/
protected $db;
/**
* @var array
*/
protected $bindings = array();
public function __construct($sql, array $bindings)
{
$this->sql = (string) $sql;
$this->bindings = $bindings;
global $wpdb;
$this->db = $wpdb;
}
/**
* @return string
*/
public function getSql()
{
return $this->sql;
}
/**
* @return array
*/
public function getBindings()
{
return $this->bindings;
}
/**
* Get the raw/bound sql
*
* @return string
*/
public function getRawSql()
{
return $this->interpolateQuery($this->sql, $this->bindings);
}
/**
* Replaces any parameter placeholders in a query with the value of that
* parameter. Useful for debugging. Assumes anonymous parameters from
* $params are are in the same order as specified in $query
*
* Reference: http://stackoverflow.com/a/1376838/656489
*
* @param string $query The sql query with parameter placeholders
* @param array $params The array of substitution parameters
*
* @return string The interpolated query
*/
protected function interpolateQuery($query, $params)
{
$keys = $placeHolders = [];
foreach ($params as $key => $value) {
if (is_string($key)) {
$keys[] = '/:' . $key . '/';
} else {
$keys[] = '/[?]/';
}
$placeHolders[] = $this->getPlaceHolder($value);
}
$query = preg_replace($keys, $placeHolders, $query, 1, $count);
return $params ? $this->db->prepare($query, $params) : $query;
}
private function getPlaceHolder($value)
{
$placeHolder = '%s';
if (is_int($value)) {
$placeHolder = '%d';
} elseif (is_float($value)) {
$placeHolder = '%f';
}
return $placeHolder;
}
}

View File

@@ -0,0 +1,34 @@
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
class Raw
{
/**
* @var string
*/
protected $value;
/**
* @var array
*/
protected $bindings;
public function __construct($value, $bindings = array())
{
$this->value = (string)$value;
$this->bindings = (array)$bindings;
}
public function getBindings()
{
return $this->bindings;
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->value;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FluentMail\App\Services\DB\QueryBuilder;
class Transaction extends QueryBuilderHandler
{
/**
* Commit the database changes
*/
public function commit()
{
$this->db->query('COMMIT');
throw new TransactionHaltException();
}
/**
* Rollback the database changes
*/
public function rollback()
{
$this->db->query('ROLLBACK');
throw new TransactionHaltException();
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace FluentMail\App\Services\DB\QueryBuilder;
class TransactionHaltException extends \Exception
{
}

View File

@@ -0,0 +1,48 @@
<?php
namespace FluentMail\App\Services\DB\Viocon;
/**
* This class gives the ability to access non-static methods statically
*
* Class AliasFacade
*
* @package Viocon
*/
class AliasFacade {
/**
* @var Container
*/
protected static $vioconInstance;
/**
* @param $method
* @param $args
*
* @return mixed
*/
public static function __callStatic($method, $args)
{
if(!static::$vioconInstance) {
static::$vioconInstance = new Container();
}
return call_user_func_array(array(static::$vioconInstance, $method), $args);
}
/**
* @param Container $instance
*/
public static function setVioconInstance(Container $instance)
{
static::$vioconInstance = $instance;
}
/**
* @return \FluentMail\App\Services\DB\Viocon\Container $instance
*/
public static function getVioconInstance()
{
return static::$vioconInstance;
}
}

View File

@@ -0,0 +1,140 @@
<?php namespace FluentMail\App\Services\DB\Viocon;
class Container
{
/**
* @var array
*/
public $registry = array();
/**
* Singleton instances
*
* @var array
*/
public $singletons = array();
public function __construct($alias = null)
{
if ($alias) {
AliasFacade::setVioconInstance($this);
class_alias('\\FluentMail\\App\\Services\\DB\\Viocon\\AliasFacade', $alias);
}
}
/**
* Register an object with a key
*
* @param string $key
* @param mixed $object
* @param bool $singleton
*
* @return void
*/
public function set($key, $object, $singleton = false)
{
$this->registry[$key] = compact('object', 'singleton');
}
/**
* If we have a registry for the given key
*
* @param string $key
*
* @return bool
*/
public function has($key)
{
return array_key_exists($key, $this->registry);
}
/**
* Register as singleton.
*
* @param string $key
* @param mixed $object
*
* @return void
*/
public function singleton($key, $object)
{
$this->set($key, $object, true);
}
/**
* Register or replace an instance as a singleton.
* Useful for replacing with Mocked instance
*
* @param string $key
* @param mixed $instance
*
* @return void
*/
public function setInstance($key, $instance)
{
$this->singletons[$key] = $instance;
}
/**
* Build from the given key.
* If there is a class registered with Container::set() then it's instance
* will be returned. If a closure is registered, a closure's return value
* will be returned. If nothing is registered then it will try to build an
* instance with new $key(...).
*
* $parameters will be passed to closure or class constructor.
*
*
* @param string $key
* @param array $parameters
*
* @return mixed
*/
public function build($key, $parameters = array())
{
// If we have a singleton instance registered the just return it
if (array_key_exists($key, $this->singletons)) {
return $this->singletons[$key];
}
// If we don't have a registered object with the key then assume user
// is trying to build a class with the given key/name
if (!array_key_exists($key, $this->registry)) {
$object = $key;
} else {
$object = $this->registry[$key]['object'];
}
$instance = $this->instanciate($object, $parameters);
// If the key is registered as a singleton, we can save the instance as singleton
// for later use
if (isset($this->registry[$key]['singleton']) && $this->registry[$key]['singleton'] === true) {
$this->singletons[$key] = $instance;
}
return $instance;
}
/**
* Instantiate an instance of the given type.
*
* @param string $key
* @param array $parameters
*
* @throws \Exception
* @return mixed
*/
protected function instanciate($key, $parameters = null)
{
if ($key instanceof \Closure) {
return call_user_func_array($key, $parameters);
}
$reflection = new \ReflectionClass($key);
return $reflection->newInstanceArgs($parameters);
}
}

View File

@@ -0,0 +1,7 @@
<?php namespace FluentMail\App\Services\DB\Viocon;
class VioconException extends \Exception
{
}

View File

@@ -0,0 +1,22 @@
<?php defined('ABSPATH') or die;
if (! function_exists('FluentSmtpDb')) {
/**
* @return \FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler
*/
function FluentSmtpDb()
{
static $FluentSmtpDb;
if (! $FluentSmtpDb) {
global $wpdb;
$connection = new \FluentMail\App\Services\DB\Connection($wpdb, ['prefix' => $wpdb->prefix]);
$FluentSmtpDb = new \FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler($connection);
}
return $FluentSmtpDb;
}
}

View File

@@ -0,0 +1,662 @@
<?php
/*
* Copyright (c) 2005-2007 Jon Abernathy <jon@chuggnutt.com>
*
* This script is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
namespace FluentMail\App\Services;
class Html2Text
{
const ENCODING = 'UTF-8';
protected $htmlFuncFlags;
/**
* Contains the HTML content to convert.
*
* @var string $html
*/
protected $html;
/**
* Contains the converted, formatted text.
*
* @var string $text
*/
protected $text;
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $replace.
*
* @var array $search
* @see $replace
*/
protected $search = array(
"/\r/", // Non-legal carriage return
"/[\n\t]+/", // Newlines and tabs
'/<head\b[^>]*>.*?<\/head>/i', // <head>
'/<script\b[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
'/<style\b[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
'/<i\b[^>]*>(.*?)<\/i>/i', // <i>
'/<em\b[^>]*>(.*?)<\/em>/i', // <em>
'/<ins\b[^>]*>(.*?)<\/ins>/i', // <ins>
'/(<ul\b[^>]*>|<\/ul>)/i', // <ul> and </ul>
'/(<ol\b[^>]*>|<\/ol>)/i', // <ol> and </ol>
'/(<dl\b[^>]*>|<\/dl>)/i', // <dl> and </dl>
'/<li\b[^>]*>(.*?)<\/li>/i', // <li> and </li>
'/<dd\b[^>]*>(.*?)<\/dd>/i', // <dd> and </dd>
'/<dt\b[^>]*>(.*?)<\/dt>/i', // <dt> and </dt>
'/<li\b[^>]*>/i', // <li>
'/<hr\b[^>]*>/i', // <hr>
'/<div\b[^>]*>/i', // <div>
'/(<table\b[^>]*>|<\/table>)/i', // <table> and </table>
'/(<tr\b[^>]*>|<\/tr>)/i', // <tr> and </tr>
'/<td\b[^>]*>(.*?)<\/td>/i', // <td> and </td>
'/<span class="_html2text_ignore">.+?<\/span>/i', // <span class="_html2text_ignore">...</span>
'/<(img)\b[^>]*alt=\"([^>"]+)\"[^>]*>/i', // <img> with alt tag
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $replace
* @see $search
*/
protected $replace = array(
'', // Non-legal carriage return
' ', // Newlines and tabs
'', // <head>
'', // <script>s -- which strip_tags supposedly has problems with
'', // <style>s -- which strip_tags supposedly has problems with
'_\\1_', // <i>
'_\\1_', // <em>
'_\\1_', // <ins>
"\n\n", // <ul> and </ul>
"\n\n", // <ol> and </ol>
"\n\n", // <dl> and </dl>
"\t* \\1\n", // <li> and </li>
" \\1\n", // <dd> and </dd>
"\t* \\1", // <dt> and </dt>
"\n\t* ", // <li>
"\n-------------------------\n", // <hr>
"<div>\n", // <div>
"\n\n", // <table> and </table>
"\n", // <tr> and </tr>
"\t\t\\1\n", // <td> and </td>
"", // <span class="_html2text_ignore">...</span>
'[\\2]', // <img> with alt tag
);
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $entReplace.
*
* @var array $entSearch
* @see $entReplace
*/
protected $entSearch = array(
'/&#153;/i', // TM symbol in win-1252
'/&#151;/i', // m-dash in win-1252
'/&(amp|#38);/i', // Ampersand: see converter()
'/[ ]{2,}/', // Runs of spaces, post-handling
'/&#39;/i', // The apostrophe symbol
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $entReplace
* @see $entSearch
*/
protected $entReplace = array(
'™', // TM symbol
'—', // m-dash
'|+|amp|+|', // Ampersand: see converter()
' ', // Runs of spaces, post-handling
'\'', // Apostrophe
);
/**
* List of preg* regular expression patterns to search for
* and replace using callback function.
*
* @var array $callbackSearch
*/
protected $callbackSearch = array(
'/<(h)[123456]( [^>]*)?>(.*?)<\/h[123456]>/i', // h1 - h6
'/[ ]*<(p)( [^>]*)?>(.*?)<\/p>[ ]*/si', // <p> with surrounding whitespace.
'/<(br)[^>]*>[ ]*/i', // <br> with leading whitespace after the newline.
'/<(b)( [^>]*)?>(.*?)<\/b>/i', // <b>
'/<(strong)( [^>]*)?>(.*?)<\/strong>/i', // <strong>
'/<(del)( [^>]*)?>(.*?)<\/del>/i', // <del>
'/<(th)( [^>]*)?>(.*?)<\/th>/i', // <th> and </th>
'/<(a) [^>]*href=("|\')([^"\']+)\2([^>]*)>(.*?)<\/a>/i' // <a href="">
);
/**
* List of preg* regular expression patterns to search for in PRE body,
* used in conjunction with $preReplace.
*
* @var array $preSearch
* @see $preReplace
*/
protected $preSearch = array(
"/\n/",
"/\t/",
'/ /',
'/<pre[^>]*>/',
'/<\/pre>/'
);
/**
* List of pattern replacements corresponding to patterns searched for PRE body.
*
* @var array $preReplace
* @see $preSearch
*/
protected $preReplace = array(
'<br>',
'&nbsp;&nbsp;&nbsp;&nbsp;',
'&nbsp;',
'',
'',
);
/**
* Temporary workspace used during PRE processing.
*
* @var string $preContent
*/
protected $preContent = '';
/**
* Contains the base URL that relative links should resolve to.
*
* @var string $baseurl
*/
protected $baseurl = '';
/**
* Indicates whether content in the $html variable has been converted yet.
*
* @var boolean $converted
* @see $html, $text
*/
protected $converted = false;
/**
* Contains URL addresses from links to be rendered in plain text.
*
* @var array $linkList
* @see buildlinkList()
*/
protected $linkList = array();
/**
* Various configuration options (able to be set in the constructor)
*
* @var array $options
*/
protected $options = array(
'do_links' => 'inline', // 'none'
// 'inline' (show links inline)
// 'nextline' (show links on the next line)
// 'table' (if a table of link URLs should be listed after the text.
// 'bbcode' (show links as bbcode)
'width' => 70, // Maximum width of the formatted text, in columns.
// Set this value to 0 (or less) to ignore word wrapping
// and not constrain text to a fixed-width column.
);
private function legacyConstruct($html = '', $fromFile = false, array $options = array())
{
$this->set_html($html, $fromFile);
$this->options = array_merge($this->options, $options);
}
/**
* @param string $html Source HTML
* @param array $options Set configuration options
*/
public function __construct($html = '', $options = array())
{
// for backwards compatibility
if (!is_array($options)) {
return call_user_func_array(array($this, 'legacyConstruct'), func_get_args());
}
$this->html = $html;
$this->options = array_merge($this->options, $options);
$this->htmlFuncFlags = (PHP_VERSION_ID < 50400)
? ENT_QUOTES
: ENT_QUOTES | ENT_HTML5;
}
/**
* Get the source HTML
*
* @return string
*/
public function getHtml()
{
return $this->html;
}
/**
* Set the source HTML
*
* @param string $html HTML source content
*/
public function setHtml($html)
{
$this->html = $html;
$this->converted = false;
}
/**
* @deprecated
*/
public function set_html($html, $from_file = false)
{
if ($from_file) {
throw new \InvalidArgumentException("Argument from_file no longer supported");
}
return $this->setHtml($html);
}
/**
* Returns the text, converted from HTML.
*
* @return string Plain text
*/
public function getText()
{
if (!$this->converted) {
$this->convert();
}
return $this->text;
}
/**
* @deprecated
*/
public function get_text()
{
return $this->getText();
}
/**
* @deprecated
*/
public function print_text()
{
print $this->getText(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* @deprecated
*/
public function p()
{
return $this->print_text();
}
/**
* Sets a base URL to handle relative links.
*
* @param string $baseurl
*/
public function setBaseUrl($baseurl)
{
$this->baseurl = $baseurl;
}
/**
* @deprecated
*/
public function set_base_url($baseurl)
{
return $this->setBaseUrl($baseurl);
}
protected function convert()
{
if (!function_exists('\mb_internal_encoding')) {
return '';
}
$origEncoding = mb_internal_encoding();
mb_internal_encoding(self::ENCODING);
$this->doConvert();
mb_internal_encoding($origEncoding);
}
protected function doConvert()
{
$this->linkList = array();
$text = trim($this->html);
$this->converter($text);
if ($this->linkList) {
$text .= "\n\nLinks:\n------\n";
foreach ($this->linkList as $i => $url) {
$text .= '[' . ($i + 1) . '] ' . $url . "\n";
}
}
$this->text = $text;
$this->converted = true;
}
protected function converter(&$text)
{
$this->convertBlockquotes($text);
$this->convertPre($text);
$text = preg_replace($this->search, $this->replace, $text);
$text = preg_replace_callback($this->callbackSearch, array($this, 'pregCallback'), $text);
$text = strip_tags($text);
$text = preg_replace($this->entSearch, $this->entReplace, $text);
$text = html_entity_decode($text, $this->htmlFuncFlags, self::ENCODING);
// Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
$text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
// Convert "|+|amp|+|" into "&", need to be done after handling of unknown entities
// This properly handles situation of "&amp;quot;" in input string
$text = str_replace('|+|amp|+|', '&', $text);
// Normalise empty lines
$text = preg_replace("/\n\s+\n/", "\n\n", $text);
$text = preg_replace("/[\n]{3,}/", "\n\n", $text);
// remove leading empty lines (can be produced by eg. P tag on the beginning)
$text = ltrim($text, "\n");
if ($this->options['width'] > 0) {
$text = wordwrap($text, $this->options['width']);
}
}
/**
* Helper function called by preg_replace() on link replacement.
*
* Maintains an internal list of links to be displayed at the end of the
* text, with numeric indices to the original point in the text they
* appeared. Also makes an effort at identifying and handling absolute
* and relative links.
*
* @param string $link URL of the link
* @param string $display Part of the text to associate number with
* @param null $linkOverride
* @return string
*/
protected function buildlinkList($link, $display, $linkOverride = null)
{
$linkMethod = ($linkOverride) ? $linkOverride : $this->options['do_links'];
if ($linkMethod == 'none') {
return $display;
}
// Ignored link types
if (preg_match('!^(javascript:|mailto:|#)!i', html_entity_decode($link, $this->htmlFuncFlags, self::ENCODING))) {
return $display;
}
if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) {
$url = $link;
} else {
$url = $this->baseurl;
if (mb_substr($link, 0, 1) != '/') {
$url .= '/';
}
$url .= $link;
}
if ($linkMethod == 'table') {
if (($index = array_search($url, $this->linkList)) === false) {
$index = count($this->linkList);
$this->linkList[] = $url;
}
return $display . ' [' . ($index + 1) . ']';
} elseif ($linkMethod == 'nextline') {
if ($url === $display) {
return $display;
}
return $display . "\n[" . $url . ']';
} elseif ($linkMethod == 'bbcode') {
return sprintf('[url=%s]%s[/url]', $url, $display);
} else { // link_method defaults to inline
if ($url === $display) {
return $display;
}
return $display . ' [' . $url . ']';
}
}
/**
* Helper function for PRE body conversion.
*
* @param string &$text HTML content
*/
protected function convertPre(&$text)
{
// get the content of PRE element
while (preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) {
// Replace br tags with newlines to prevent the search-and-replace callback from killing whitespace
$this->preContent = preg_replace('/(<br\b[^>]*>)/i', "\n", $matches[1]);
// Run our defined tags search-and-replace with callback
$this->preContent = preg_replace_callback(
$this->callbackSearch,
array($this, 'pregCallback'),
$this->preContent
);
// convert the content
$this->preContent = sprintf(
'<div><br>%s<br></div>',
preg_replace($this->preSearch, $this->preReplace, $this->preContent)
);
// replace the content (use callback because content can contain $0 variable)
$text = preg_replace_callback(
'/<pre[^>]*>.*<\/pre>/ismU',
array($this, 'pregPreCallback'),
$text,
1
);
// free memory
$this->preContent = '';
}
}
/**
* Helper function for BLOCKQUOTE body conversion.
*
* @param string &$text HTML content
*/
protected function convertBlockquotes(&$text)
{
if (preg_match_all('/<\/*blockquote[^>]*>/i', $text, $matches, PREG_OFFSET_CAPTURE)) {
$originalText = $text;
$start = 0;
$taglen = 0;
$level = 0;
$diff = 0;
foreach ($matches[0] as $m) {
$m[1] = mb_strlen(substr($originalText, 0, $m[1]));
if ($m[0][0] == '<' && $m[0][1] == '/') {
$level--;
if ($level < 0) {
$level = 0; // malformed HTML: go to next blockquote
} elseif ($level > 0) {
// skip inner blockquote
} else {
$end = $m[1];
$len = $end - $taglen - $start;
// Get blockquote content
$body = mb_substr($text, $start + $taglen - $diff, $len);
// Set text width
$pWidth = $this->options['width'];
if ($this->options['width'] > 0) $this->options['width'] -= 2;
// Convert blockquote content
$body = trim($body);
$this->converter($body);
// Add citation markers and create PRE block
$body = preg_replace('/((^|\n)>*)/', '\\1> ', trim($body));
$body = '<pre>' . htmlspecialchars($body, $this->htmlFuncFlags, self::ENCODING) . '</pre>';
// Re-set text width
$this->options['width'] = $pWidth;
// Replace content
$text = mb_substr($text, 0, $start - $diff)
. $body
. mb_substr($text, $end + mb_strlen($m[0]) - $diff);
$diff += $len + $taglen + mb_strlen($m[0]) - mb_strlen($body);
unset($body);
}
} else {
if ($level == 0) {
$start = $m[1];
$taglen = mb_strlen($m[0]);
}
$level++;
}
}
}
}
/**
* Callback function for preg_replace_callback use.
*
* @param array $matches PREG matches
* @return string
*/
protected function pregCallback($matches)
{
switch (mb_strtolower($matches[1])) {
case 'p':
// Replace newlines with spaces.
$para = str_replace("\n", " ", $matches[3]);
// Trim trailing and leading whitespace within the tag.
$para = trim($para);
// Add trailing newlines for this para.
return "\n" . $para . "\n";
case 'br':
return "\n";
case 'b':
case 'strong':
return $this->toupper($matches[3]);
case 'del':
return $this->tostrike($matches[3]);
case 'th':
return $this->toupper("\t\t" . $matches[3] . "\n");
case 'h':
return $this->toupper("\n\n" . $matches[3] . "\n\n");
case 'a':
// override the link method
$linkOverride = null;
if (preg_match('/_html2text_link_(\w+)/', $matches[4], $linkOverrideMatch)) {
$linkOverride = $linkOverrideMatch[1];
}
// Remove spaces in URL (#1487805)
$url = str_replace(' ', '', $matches[3]);
return $this->buildlinkList($url, $matches[5], $linkOverride);
}
return '';
}
/**
* Callback function for preg_replace_callback use in PRE content handler.
*
* @param array $matches PREG matches
* @return string
*/
protected function pregPreCallback(/** @noinspection PhpUnusedParameterInspection */ $matches)
{
return $this->preContent;
}
/**
* Strtoupper function with HTML tags and entities handling.
*
* @param string $str Text to convert
* @return string Converted text
*/
protected function toupper($str)
{
// string can contain HTML tags
$chunks = preg_split('/(<[^>]*>)/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
// convert toupper only the text between HTML tags
foreach ($chunks as $i => $chunk) {
if ($chunk[0] != '<') {
$chunks[$i] = $this->strtoupper($chunk);
}
}
return implode($chunks);
}
/**
* Strtoupper multibyte wrapper function with HTML entities handling.
*
* @param string $str Text to convert
* @return string Converted text
*/
protected function strtoupper($str)
{
$str = html_entity_decode($str, $this->htmlFuncFlags, self::ENCODING);
$str = mb_strtoupper($str);
$str = htmlspecialchars($str, $this->htmlFuncFlags, self::ENCODING);
return $str;
}
/**
* Helper function for DEL conversion.
*
* @param string $text HTML content
* @return string Converted text
*/
protected function tostrike($str)
{
$rtn = '';
for ($i = 0; $i < mb_strlen($str); $i++) {
$chr = mb_substr($str, $i, 1);
$combiningChr = chr(0xC0 | 0x336 >> 6) . chr(0x80 | 0x336 & 0x3F);
$rtn .= $chr . $combiningChr;
}
return $rtn;
}
}

View File

@@ -0,0 +1,450 @@
<?php
namespace FluentMail\App\Services\Mailer;
use Exception;
use InvalidArgumentException;
use FluentMail\App\Models\Logger;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\ValidatorTrait;
class BaseHandler
{
use ValidatorTrait;
protected $app = null;
protected $params = [];
protected $manager = null;
protected $phpMailer = null;
protected $settings = [];
protected $attributes = [];
protected $response = null;
protected $existing_row_id = null;
public function __construct(?Application $app = null, ?Manager $manager = null)
{
$this->app = $app ?: fluentMail();
$this->manager = $manager ?: fluentMail(Manager::class);
}
public function setPhpMailer($phpMailer)
{
$this->phpMailer = $phpMailer;
if(!$this->phpMailer->CharSet) {
$this->phpMailer->CharSet = 'UTF-8';
}
return $this;
}
public function setSettings($settings)
{
$this->settings = $settings;
return $this;
}
protected function preSend()
{
$this->attributes = [];
if ($this->isForced('from_name')) {
$this->phpMailer->FromName = $this->getSetting('sender_name');
}
if ($this->getSetting('return_path') == 'yes') {
$this->phpMailer->Sender = $this->phpMailer->From;
}
$this->attributes = $this->setAttributes();
return true;
}
protected function isForced($key)
{
return $this->getSetting("force_{$key}") == 'yes';
}
public function isForcedEmail()
{
return $this->getSetting("force_from_email") != 'no';
}
public function isActive()
{
return $this->getSetting('is_active') == 'yes';
}
protected function getDefaultParams()
{
$timeout = (int)ini_get('max_execution_time');
return [
'timeout' => $timeout ?: 30,
'httpversion' => '1.1',
'blocking' => true,
];
}
protected function setAttributes()
{
$from = $this->setFrom();
$replyTos = $this->setRecipientsArray(array_values(
$this->phpMailer->getReplyToAddresses()
));
$contentType = $this->phpMailer->ContentType;
$customHeaders = $this->setFormattedCustomHeaders();
$recipients = [
'to' => $this->setRecipientsArray($this->phpMailer->getToAddresses()),
'cc' => $this->setRecipientsArray($this->phpMailer->getCcAddresses()),
'bcc' => $this->setRecipientsArray($this->phpMailer->getBccAddresses())
];
return array_merge($this->attributes, [
'from' => $from,
'to' => $recipients['to'],
'subject' => $this->phpMailer->Subject,
'message' => $this->phpMailer->Body,
'alt_body' => $this->phpMailer->AltBody,
'attachments' => $this->phpMailer->getAttachments(),
'custom_headers' => $customHeaders,
'headers' => [
'reply-to' => $replyTos,
'cc' => $recipients['cc'],
'bcc' => $recipients['bcc'],
'content-type' => $contentType
]
]);
}
protected function setFrom()
{
$name = $this->getSetting('sender_name');
$email = $this->getSetting('sender_email');
$overrideName = $this->getSetting('force_from_name');
if ($name && ($overrideName == 'yes' || $this->phpMailer->FromName == 'WordPress')) {
$this->attributes['sender_name'] = $name;
$this->attributes['sender_email'] = $email;
$from = $name . ' <' . $email . '>';
} elseif ($this->phpMailer->FromName) {
$this->attributes['sender_email'] = $email;
$this->attributes['sender_name'] = $this->phpMailer->FromName;
$from = $this->phpMailer->FromName . ' <' . $email . '>';
} else {
$from = $this->attributes['sender_email'] = $email;
}
return $from;
}
protected function setRecipientsArray($array)
{
$recipients = [];
foreach ($array as $key => $recipient) {
$recipient = array_filter($recipient);
if (!$recipient) continue;
$recipients[$key] = [
'email' => array_shift($recipient)
];
if ($recipient) {
$recipients[$key]['name'] = array_shift($recipient);
}
}
return $recipients;
}
protected function setFormattedCustomHeaders()
{
$headers = [];
$customHeaders = $this->phpMailer->getCustomHeaders();
foreach ($customHeaders as $key => $header) {
if ($header[0] == 'Return-Path') {
if ($this->getSetting('options.return_path') == 'no') {
if (!empty($header[1])) {
$this->phpMailer->Sender = $header[1];
}
}
unset($customHeaders[$key]);
} else {
$headers[] = [
'key' => $header[0],
'value' => $header[1]
];
}
}
$this->phpMailer->clearCustomHeaders();
foreach ($customHeaders as $customHeader) {
$this->phpMailer->addCustomHeader($customHeader[0], $customHeader[1]);
}
return $headers;
}
public function getSetting($key = null, $default = null)
{
try {
return $key ? Arr::get($this->settings, $key, $default) : $this->settings;
} catch (Exception $e) {
return $default;
}
}
protected function getParam($key = null, $default = null)
{
try {
return $key ? Arr::get($this->attributes, $key, $default) : $this->attributes;
} catch (Exception $e) {
return $default;
}
}
protected function getHeader($key, $default = null)
{
try {
return Arr::get(
$this->attributes['headers'], $key, $default
);
} catch (Exception $e) {
return $default;
}
}
public function getSubject()
{
$subject = '';
if (isset($this->attributes['subject'])) {
$subject = $this->attributes['subject'];
}
return $subject;
}
protected function getExtraParams()
{
$this->attributes['extra']['provider'] = $this->getSetting('provider');
return $this->attributes['extra'];
}
public function handleResponse($response)
{
if ( is_wp_error($response) ) {
$code = $response->get_error_code();
if (!is_numeric($code)) {
$code = 400;
}
$message = $response->get_error_message();
$errorResponse = [
'code' => $code,
'message' => $message,
'errors' => $response->get_error_data()
];
$status = $this->processResponse($errorResponse, false);
if ( !$status ) {
throw new \PHPMailer\PHPMailer\Exception($message, $code); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
} else {
return $status;
}
} else {
return $this->processResponse($response, true);
}
}
public function processResponse($response, $status)
{
if ($this->shouldBeLogged($status)) {
$data = [
'to' => $this->serialize($this->attributes['to']),
'from' => $this->attributes['from'],
'subject' => sanitize_text_field($this->attributes['subject']),
'body' => $this->attributes['message'],
'attachments' => $this->serialize($this->attributes['attachments']),
'status' => $status ? 'sent' : 'failed',
'response' => $this->serialize($response),
'headers' => $this->serialize($this->getParam('headers')),
'extra' => $this->serialize($this->getExtraParams())
];
if($this->existing_row_id) {
$row = (new Logger())->find($this->existing_row_id);
if($row) {
$row['response'] = (array) $row['response'];
if($status) {
$row['response']['fallback'] = __('Sent using fallback connection ', 'fluent-smtp') . $this->attributes['from'];
$row['response']['fallback_response'] = $response;
} else {
$row['response']['fallback'] = __('Tried to send using fallback but failed. ', 'fluent-smtp') . $this->attributes['from'];
$row['response']['fallback_response'] = $response;
}
$data['response'] = $this->serialize( $row['response']);
$data['retries'] = $row['retries'] + 1;
(new Logger())->updateLog($data, ['id' => $row['id']]);
if(!$status) {
do_action('fluentmail_email_sending_failed_no_fallback', $row['id'], $this, $data);
}
}
} else {
$logId = (new Logger)->add($data);
if(!$status) {
// We have to fire an action for this failed job
$status = apply_filters('fluentmail_email_sending_failed', $status, $logId, $this, $data);
}
}
}
return $status;
}
protected function shouldBeLogged($status)
{
if($this->existing_row_id) {
return true;
}
if (defined('FLUENTMAIL_LOG_OFF') && FLUENTMAIL_LOG_OFF) {
return false;
}
if (!$status) {
return true;
}
$miscSettings = $this->manager->getConfig('misc');
$isLogOn = $miscSettings['log_emails'] == 'yes';
return apply_filters('fluentmail_will_log_email', $isLogOn, $miscSettings, $this);
}
protected function serialize(array $data)
{
foreach ($data as $key => $item) {
if (is_array($item)) {
$this->serialize($item);
}
if (is_object($item) || is_resource($item)) {
throw new InvalidArgumentException(
"Invalid Data: Array cannot contain an object or resource."
);
}
if (is_string($item)) {
if (is_serialized($item)) {
throw new InvalidArgumentException(
"Invalid Data: Array cannot contain serialized data."
);
}
if (filter_var($item, FILTER_VALIDATE_EMAIL)) {
$data[$key] = sanitize_email($item);
} elseif (filter_var($item, FILTER_VALIDATE_URL)) {
$data[$key] = esc_url_raw($item);
} else {
$data[$key] = sanitize_text_field($item);
}
}
}
return serialize($data);
}
protected function fireWPMailFailedAction($data)
{
$code = is_numeric($data['code']) ? $data['code'] : 400;
$code = strlen($code) < 3 ? 400 : $code;
$mail_error_data['phpmailer_exception_code'] = $code;
$mail_error_data['errors'] = $data['errors'];
$error = new \WP_Error(
$code, $data['message'], $mail_error_data
);
$this->app->doAction('wp_mail_failed', $error);
}
protected function updatedLog($id, $data)
{
try {
$data['updated_at'] = current_time('mysql');
(new Logger)->updateLog($data, ['id' => $id]);
} catch (Exception $e) {
error_log($e->getMessage());
}
}
public function getValidSenders($connection)
{
return [$connection['sender_email']];
}
public function checkConnection($connection)
{
return true;
}
public function getConnectionInfo($connection)
{
return [
'info' => (string) fluentMail('view')->make('admin.general_connection_info', [
'connection' => $connection
])
];
}
public function getPhpMailer()
{
return $this->phpMailer;
}
public function setRowId($id)
{
$this->existing_row_id = $id;
}
public function addNewSenderEmail($connection, $email)
{
return new \WP_Error('not_implemented', __('Not implemented', 'fluent-smtp'));
}
public function removeSenderEmail($connection, $email)
{
return new \WP_Error('not_implemented', __('Not implemented', 'fluent-smtp'));
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FluentMail\App\Services\Mailer;
use FluentMail\App\Models\Logger;
use FluentMail\App\Services\Mailer\Providers\Factory;
use FluentMail\App\Services\Mailer\Providers\DefaultMail\Handler as PHPMailer;
class FluentPHPMailer
{
protected $app = null;
protected $phpMailer = null;
public function __construct($phpMailer)
{
$this->app = fluentMail();
$this->phpMailer = $phpMailer;
}
public function send()
{
if ($driver = fluentMailGetProvider($this->phpMailer->From)) {
if ($forceFromEmail = $driver->getSetting('force_from_email_id')) {
$this->phpMailer->From = $forceFromEmail;
}
return $driver->setPhpMailer($this->phpMailer)->send();
}
return $this->phpMailer->send();
}
public function sendViaFallback($rowId)
{
$driver = fluentMailGetProvider($this->phpMailer->From);
if($driver) {
$driver->setRowId($rowId);
return $driver->setPhpMailer($this->phpMailer)->send();
}
return false;
}
public function __get($key)
{
return $this->phpMailer->{$key};
}
public function __set($key, $value)
{
$this->phpMailer->{$key} = $value;
}
public function __call($method, $params)
{
return call_user_func_array([$this->phpMailer, $method], $params);
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace FluentMail\App\Services\Mailer;
use FluentMail\App\Models\Logger;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\Includes\Support\ValidationException;
use FluentMail\App\Services\Mailer\Providers\Factory;
class Manager
{
protected $app = null;
protected static $config = [];
protected static $settings = [];
protected static $resolved = [];
protected static $wpConfigSettings = [];
public function __construct(?Application $app = null)
{
$this->app = $app ?: fluentMail();
$this->initialize();
}
protected function initialize()
{
$this->loadConfigAndSettings();
$this->app->addCustomFilter('active_driver', [$this, 'activeDriver']);
}
protected function loadConfigAndSettings()
{
static::$config = require(__DIR__ . '/Providers/config.php');
static::$settings = (new Settings)->getSettings();
$this->mergeConfigAndSettings();
}
protected function mergeConfigAndSettings()
{
$databaseSettings = $this->getSettings();
Arr::set(static::$config, 'mappings', Arr::get($databaseSettings, 'mappings'));
Arr::set(static::$config, 'connections', Arr::get($databaseSettings, 'connections'));
if (isset($databaseSettings['misc'])) {
Arr::set(static::$config, "misc", array_merge(
static::$config['misc'], $databaseSettings['misc']
));
}
foreach (static::$config['providers'] as $key => $provider) {
try {
$optionKey = "providers.{$key}.options";
$options = array_merge(
$provider['options'],
Arr::get($databaseSettings, $optionKey, [])
);
Arr::set(static::$config, $optionKey, $options);
} catch (ValidationException $e) {
continue;
}
}
}
public function getMailerConfigAndSettings()
{
return static::$config;
}
public function getConfig($key = null, $default = null)
{
return $key ? Arr::get(static::$config, $key, $default) : static::$config;
}
public function getSettings($key = null, $default = null)
{
return $key ? Arr::get(static::$settings, $key, $default) : static::$settings;
}
public function getWPConfig($key = null, $default = null)
{
return $key ? Arr::get(
static::$wpConfigSettings, $key, $default
) : static::$wpConfigSettings;
}
public function activeDriver($phpMailer)
{
return fluentMailgetConnection($phpMailer->From);
}
}

View File

@@ -0,0 +1,356 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
use FluentMail\App\Models\Settings;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\Includes\Support\Arr;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $client = null;
const RAW_REQUEST = true;
const TRIGGER_ERROR = false;
public function send()
{
if ($this->preSend() && $this->phpMailer->preSend()) {
$this->client = new SimpleEmailServiceMessage;
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$mime = chunk_split(base64_encode($this->phpMailer->getSentMIMEMessage()), 76, "\n");
$connectionSettings = $this->filterConnectionVars($this->getSetting());
$ses = fluentMailSesConnection($connectionSettings);
$this->response = $ses->sendRawEmail($mime);
return $this->handleResponse($this->response);
}
protected function getFrom()
{
return $this->getParam('from');
}
public function getVerifiedEmails()
{
return (new Settings)->getVerifiedEmails();
}
protected function getReplyTo()
{
$replyTo = $this->getRecipients(
$this->getParam('headers.reply-to')
);
if (is_array($replyTo)) {
$replyTo = reset($replyTo);
}
return $replyTo;
}
protected function getTo()
{
return $this->getRecipients($this->getParam('to'));
}
protected function getCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.bcc'));
}
protected function getRecipients($recipients)
{
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipients);
return implode(', ', $array);
}
protected function getBody()
{
return [
$this->phpMailer->AltBody,
$this->phpMailer->Body
];
}
protected function getAttachments()
{
$attachments = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
$mimeType = mime_content_type($attachment[0]);
$filetype = str_replace(';', '', trim($mimeType));
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$attachments[] = [
'type' => $filetype,
'name' => $fileName,
'content' => $file
];
}
return $attachments;
}
protected function getCustomEmailHeaders()
{
$customHeaders = [
'X-Mailer' => 'Amazon-SES'
];
$headers = [];
foreach ($customHeaders as $key => $header) {
$headers[] = $key . ':' . $header;
}
return $headers;
}
protected function getRegion()
{
return 'email.' . $this->getSetting('region') . '.amazonaws.com';
}
public function getValidSenders($config)
{
$config = $this->filterConnectionVars($config);
$senders = $this->getSendersFromMappingsAndApi($config);
return $senders['all_senders'];
}
public function getValidSendingIdentities($config)
{
$config = $this->filterConnectionVars($config);
$region = 'email.' . $config['region'] . '.amazonaws.com';
$ses = new SimpleEmailService(
$config['access_key'],
$config['secret_key'],
$region,
static::TRIGGER_ERROR
);
$validSenders = $ses->listVerifiedEmailAddresses();
$addresses = [];
if (is_wp_error($validSenders)) {
return [
'emails' => [$config['sender_email']],
'verified_domain' => ''
];
}
if ($validSenders && isset($validSenders['Addresses'])) {
$addresses = $validSenders['Addresses'];
}
$primaryEmail = $config['sender_email'];
$domainArray = explode('@', $primaryEmail);
$domainName = $domainArray[1];
if (apply_filters('fluent_mail_ses_primary_domain_only', true)) {
$addresses = array_filter($addresses, function ($email) use ($domainName) {
return !!strpos($email, $domainName);
});
$addresses = array_values($addresses);
}
return [
'emails' => apply_filters('fluentsmtp_ses_valid_senders', $addresses, $config),
'verified_domain' => in_array($domainName, $validSenders['domains']) ? $domainName : ''
];
}
public function getConnectionInfo($connection)
{
$connection = $this->filterConnectionVars($connection);
$stats = $this->getStats($connection);
$error = '';
if (is_wp_error($stats)) {
$error = $stats->get_error_message();
$stats = [];
}
$validSenders = $this->getSendersFromMappingsAndApi($connection);
$info = (string)fluentMail('view')->make('admin.ses_connection_info', [
'connection' => $connection,
'valid_senders' => $validSenders['all_senders'],
'stats' => $stats,
'error' => $error
]);
return [
'info' => $info,
'verificationSettings' => [
'connection_name' => 'Amazon SES',
'all_senders' => $validSenders['all_senders'],
'verified_senders' => $validSenders['verified_senders'],
'verified_domain' => $validSenders['verified_domain']
]
];
}
public function addNewSenderEmail($connection, $email)
{
$connection = $this->filterConnectionVars($connection);
$validSenders = $this->getValidSendingIdentities($connection);
$emailDomain = explode('@', $email);
$emailDomain = $emailDomain[1];
if ($emailDomain != $validSenders['verified_domain']) {
return new \WP_Error(422, __('Invalid email address! Please use a verified domain.', 'fluent-smtp'));
}
$settings = fluentMailGetSettings();
$mappings = Arr::get($settings, 'mappings', []);
if (isset($mappings[$email])) {
return new \WP_Error(422, __('Email address already exists with another connection. Please choose a different email.', 'fluent-smtp'));
}
$settings = get_option('fluentmail-settings');
$settings['mappings'][$email] = md5($connection['sender_email']);
update_option('fluentmail-settings', $settings);
return true;
}
public function removeSenderEmail($connection, $email)
{
$connection = $this->filterConnectionVars($connection);
$validSenders = $this->getValidSendingIdentities($connection);
$emailDomain = explode('@', $email);
$emailDomain = $emailDomain[1];
if ($emailDomain != $validSenders['verified_domain']) {
return new \WP_Error(422, __('Invalid email address! Please use a verified domain.', 'fluent-smtp'));
}
if (in_array($email, $validSenders['emails'])) {
return new \WP_Error(422, __('Sorry! you can not remove this email from this connection', 'fluent-smtp'));
}
$settings = fluentMailGetSettings();
$mappings = Arr::get($settings, 'mappings', []);
if (!isset($mappings[$email])) {
return new \WP_Error(422, __('Email does not exists. Please try again.', 'fluent-smtp'));
}
// check if the it's the same email or not
if ($mappings[$email] != md5($connection['sender_email'])) {
return new \WP_Error(422, __('Email does not exists. Please try again.', 'fluent-smtp'));
}
$settings = get_option('fluentmail-settings');
unset($settings['mappings'][$email]);
update_option('fluentmail-settings', $settings);
return true;
}
private function getStats($config)
{
$region = 'email.' . $config['region'] . '.amazonaws.com';
$ses = new SimpleEmailService(
$config['access_key'],
$config['secret_key'],
$region,
static::TRIGGER_ERROR
);
return $ses->getSendQuota();
}
private function filterConnectionVars($connection)
{
if ($connection['key_store'] == 'wp_config') {
$connection['access_key'] = defined('FLUENTMAIL_AWS_ACCESS_KEY_ID') ? FLUENTMAIL_AWS_ACCESS_KEY_ID : '';
$connection['secret_key'] = defined('FLUENTMAIL_AWS_SECRET_ACCESS_KEY') ? FLUENTMAIL_AWS_SECRET_ACCESS_KEY : '';
}
return $connection;
}
private function getSendersFromMappingsAndApi($connection)
{
$validSenders = $this->getValidSendingIdentities($connection);
$verifiedDomain = Arr::get($validSenders, 'verified_domain', '');
if ($verifiedDomain) {
$settings = fluentMailGetSettings();
$mappings = Arr::get($settings, 'mappings', []);
$mapKey = md5($connection['sender_email']);
$mapSenders = array_filter($mappings, function ($key) use ($mapKey) {
return $key == $mapKey;
});
$mapSenders[$connection['sender_email']] = true;
foreach ($validSenders['emails'] as $email) {
$mapSenders[$email] = $email;
}
$mapSenders = array_keys($mapSenders);
} else {
$mapSenders = $validSenders['emails'];
}
return [
'all_senders' => $mapSenders,
'verified_senders' => $validSenders['emails'],
'verified_domain' => $verifiedDomain
];
}
}

View File

@@ -0,0 +1,685 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
/**
*
* Copyright (c) 2014, Daniel Zahariev.
* Copyright (c) 2011, Dan Myers.
* Parts copyright (c) 2008, Donovan Schonknecht.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This is a modified BSD license (the third clause has been removed).
* The BSD license may be found here:
* http://www.opensource.org/licenses/bsd-license.php
*
* Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates.
*
* SimpleEmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here:
* http://undesigned.org.za/2007/10/22/amazon-s3-php-class
*
* @copyright 2014 Daniel Zahariev
* @copyright 2011 Dan Myers
* @copyright 2008 Donovan Schonknecht
*/
/**
* SimpleEmailService PHP class
*
* @link https://github.com/daniel-zahariev/php-aws-ses
* @package AmazonSimpleEmailService
* @version v0.9.1
*/
class SimpleEmailService
{
/**
* @link(AWS SES regions, http://docs.aws.amazon.com/ses/latest/DeveloperGuide/regions.html)
*/
const AWS_US_EAST_1 = 'email.us-east-1.amazonaws.com';
const AWS_US_WEST_2 = 'email.us-west-2.amazonaws.com';
const AWS_EU_WEST1 = 'email.eu-west-1.amazonaws.com';
const REQUEST_SIGNATURE_V3 = 'v3';
const REQUEST_SIGNATURE_V4 = 'v4';
/**
* AWS SES Target host of region
*/
protected $__host;
/**
* AWS SES Access key
*/
protected $__accessKey;
/**
* AWS Secret key
*/
protected $__secretKey;
/**
* Enable/disable
*/
protected $__trigger_errors;
/**
* Controls the reuse of CURL hander for sending a bulk of messages
* @deprecated
*/
protected $__bulk_sending_mode = false;
/**
* Optionally reusable SimpleEmailServiceRequest instance
*/
protected $__ses_request = null;
/**
* Controls CURLOPT_SSL_VERIFYHOST setting for SimpleEmailServiceRequest's curl handler
*/
protected $__verifyHost = true;
/**
* Controls CURLOPT_SSL_VERIFYPEER setting for SimpleEmailServiceRequest's curl handler
*/
protected $__verifyPeer = true;
/**
* @var string HTTP Request signature version
*/
protected $__requestSignatureVersion;
/**
* Constructor
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @param string $host Amazon Host through which to send the emails
* @param boolean $trigger_errors Trigger PHP errors when AWS SES API returns an error
* @param string $requestSignatureVersion Version of the request signature
*/
public function __construct($accessKey = null, $secretKey = null, $host = self::AWS_US_EAST_1, $trigger_errors = true, $requestSignatureVersion = self::REQUEST_SIGNATURE_V4)
{
if ($accessKey !== null && $secretKey !== null) {
$this->setAuth($accessKey, $secretKey);
}
$this->__host = $host;
$this->__trigger_errors = $trigger_errors;
$this->__requestSignatureVersion = $requestSignatureVersion;
}
/**
* Set the request signature version
*
* @param string $requestSignatureVersion
* @return SimpleEmailService $this
*/
public function setRequestSignatureVersion($requestSignatureVersion)
{
$this->__requestSignatureVersion = $requestSignatureVersion;
return $this;
}
/**
* @return string
*/
public function getRequestSignatureVersion()
{
return $this->__requestSignatureVersion;
}
/**
* Set AWS access key and secret key
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return SimpleEmailService $this
*/
public function setAuth($accessKey, $secretKey)
{
$this->__accessKey = $accessKey;
$this->__secretKey = $secretKey;
return $this;
}
/**
* Set AWS Host
* @param string $host AWS Host
*/
public function setHost($host = self::AWS_US_EAST_1)
{
$this->__host = $host;
return $this;
}
/**
* @deprecated
*/
public function enableVerifyHost($enable = true)
{
$this->__verifyHost = (bool)$enable;
return $this;
}
/**
* @deprecated
*/
public function enableVerifyPeer($enable = true)
{
$this->__verifyPeer = (bool)$enable;
return $this;
}
/**
* @deprecated
*/
public function verifyHost()
{
return $this->__verifyHost;
}
/**
* @deprecated
*/
public function verifyPeer()
{
return $this->__verifyPeer;
}
/**
* Get AWS target host
* @return boolean
*/
public function getHost()
{
return $this->__host;
}
/**
* Get AWS SES auth access key
* @return string
*/
public function getAccessKey()
{
return $this->__accessKey;
}
/**
* Get AWS SES auth secret key
* @return string
*/
public function getSecretKey()
{
return $this->__secretKey;
}
/**
* Get the verify peer CURL mode
* @return boolean
*/
public function getVerifyPeer()
{
return $this->__verifyPeer;
}
/**
* Get the verify host CURL mode
* @return boolean
*/
public function getVerifyHost()
{
return $this->__verifyHost;
}
/**
* Get bulk email sending mode
* @return boolean
* @deprecated
*/
public function getBulkMode()
{
return $this->__bulk_sending_mode;
}
/**
* Enable/disable CURLOPT_SSL_VERIFYHOST for SimpleEmailServiceRequest's curl handler
* verifyHost and verifyPeer determine whether curl verifies ssl certificates.
* It may be necessary to disable these checks on certain systems.
* These only have an effect if SSL is enabled.
*
* @param boolean $enable New status for the mode
* @return SimpleEmailService $this
*/
public function setVerifyHost($enable = true)
{
$this->__verifyHost = (bool)$enable;
return $this;
}
/**
* Enable/disable CURLOPT_SSL_VERIFYPEER for SimpleEmailServiceRequest's curl handler
* verifyHost and verifyPeer determine whether curl verifies ssl certificates.
* It may be necessary to disable these checks on certain systems.
* These only have an effect if SSL is enabled.
*
* @param boolean $enable New status for the mode
* @return SimpleEmailService $this
*/
public function setVerifyPeer($enable = true)
{
$this->__verifyPeer = (bool)$enable;
return $this;
}
/**
* Enable/disable bulk email sending mode
*
* @param boolean $enable New status for the mode
* @return SimpleEmailService $this
* @deprecated
*/
public function setBulkMode($enable = true)
{
$this->__bulk_sending_mode = (bool)$enable;
return $this;
}
/**
* Lists the email addresses that have been verified and can be used as the 'From' address
*
* @return array|\WP_Error An array containing two items: a list of verified email addresses, and the request id.
*/
public function listVerifiedEmailAddresses()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'ListIdentities');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
return new \WP_Error($ses_response->code, $this->getErrorMessage('ListIdentities', $ses_response->error), $ses_response->error);
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$domains = array();
$addresses = array();
foreach ($ses_response->body->ListIdentitiesResult->Identities->member as $address) {
if (is_email($address)) {
$addresses[] = (string)$address;
} else {
$domains[] = (string)$address;
}
}
$response['Addresses'] = $addresses;
$response['domains'] = $domains;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Requests verification of the provided email address, so it can be used
* as the 'From' address when sending emails through SimpleEmailService.
*
* After submitting this request, you should receive a verification email
* from Amazon at the specified address containing instructions to follow.
*
* @param string $email The email address to get verified
* @return array The request id for this request.
*/
public function verifyEmailAddress($email)
{
$ses_request = $this->getRequestHandler('POST');
$ses_request->setParameter('Action', 'VerifyEmailAddress');
$ses_request->setParameter('EmailAddress', $email);
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('verifyEmailAddress', $ses_response->error);
return false;
}
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Removes the specified email address from the list of verified addresses.
*
* @param string $email The email address to remove
* @return array The request id for this request.
*/
public function deleteVerifiedEmailAddress($email)
{
$ses_request = $this->getRequestHandler('DELETE');
$ses_request->setParameter('Action', 'DeleteVerifiedEmailAddress');
$ses_request->setParameter('EmailAddress', $email);
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('deleteVerifiedEmailAddress', $ses_response->error);
return false;
}
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves information on the current activity limits for this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html
*
* @return array|\WP_Error An array containing information on this account's activity limits.
*/
public function getSendQuota()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'GetSendQuota');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
return new \WP_Error($ses_response->code, $this->getErrorMessage('getSendQuota', $ses_response->error), $ses_response->error);
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$response['Max24HourSend'] = (string)$ses_response->body->GetSendQuotaResult->Max24HourSend;
$response['MaxSendRate'] = (string)$ses_response->body->GetSendQuotaResult->MaxSendRate;
$response['SentLast24Hours'] = (string)$ses_response->body->GetSendQuotaResult->SentLast24Hours;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves statistics for the last two weeks of activity on this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html
*
* @return array An array of activity statistics. Each array item covers a 15-minute period.
*/
public function getSendStatistics()
{
$ses_request = $this->getRequestHandler('GET');
$ses_request->setParameter('Action', 'GetSendStatistics');
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$ses_response->error = array('code' => $ses_response->code, 'message' => 'Unexpected HTTP status');
}
if ($ses_response->error !== false) {
$this->__triggerError('getSendStatistics', $ses_response->error);
return false;
}
$response = array();
if (!isset($ses_response->body)) {
return $response;
}
$datapoints = array();
foreach ($ses_response->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) {
$p = array();
$p['Bounces'] = (string)$datapoint->Bounces;
$p['Complaints'] = (string)$datapoint->Complaints;
$p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts;
$p['Rejects'] = (string)$datapoint->Rejects;
$p['Timestamp'] = (string)$datapoint->Timestamp;
$datapoints[] = $p;
}
$response['SendDataPoints'] = $datapoints;
$response['RequestId'] = (string)$ses_response->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Given a SimpleEmailServiceMessage object, submits the message to the service for sending.
*
* @param SimpleEmailServiceMessage $sesMessage An instance of the message class
* @param boolean $use_raw_request If this is true or there are attachments to the email `SendRawEmail` call will be used
* @param boolean $trigger_error Optionally overwrite the class setting for triggering an error (with type check to true/false)
* @return array An array containing the unique identifier for this message and a separate request id.
* Returns false if the provided message is missing any required fields.
* @link(AWS SES Response formats, http://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-responses.html)
*/
public function sendEmail($sesMessage, $use_raw_request = false, $trigger_error = null)
{
if (!$sesMessage->validate()) {
$this->__triggerError('sendEmail', 'Message failed validation.');
return false;
}
$ses_request = $this->getRequestHandler('POST');
$action = !empty($sesMessage->attachments) || $use_raw_request ? 'SendRawEmail' : 'SendEmail';
$ses_request->setParameter('Action', $action);
// Works with both calls
if (!is_null($sesMessage->configuration_set)) {
$ses_request->setParameter('ConfigurationSetName', $sesMessage->configuration_set);
}
if ($action == 'SendRawEmail') {
// https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html
$ses_request->setParameter('RawMessage.Data', $sesMessage->mime);
} else {
$i = 1;
foreach ($sesMessage->to as $to) {
$ses_request->setParameter('Destination.ToAddresses.member.' . $i, $sesMessage->encodeRecipients($to));
$i++;
}
if (is_array($sesMessage->cc)) {
$i = 1;
foreach ($sesMessage->cc as $cc) {
$ses_request->setParameter('Destination.CcAddresses.member.' . $i, $sesMessage->encodeRecipients($cc));
$i++;
}
}
if (is_array($sesMessage->bcc)) {
$i = 1;
foreach ($sesMessage->bcc as $bcc) {
$ses_request->setParameter('Destination.BccAddresses.member.' . $i, $sesMessage->encodeRecipients($bcc));
$i++;
}
}
if (is_array($sesMessage->replyto)) {
$i = 1;
foreach ($sesMessage->replyto as $replyto) {
$ses_request->setParameter('ReplyToAddresses.member.' . $i, $sesMessage->encodeRecipients($replyto));
$i++;
}
}
$ses_request->setParameter('Source', $sesMessage->encodeRecipients($sesMessage->from));
if ($sesMessage->returnpath != null) {
$ses_request->setParameter('ReturnPath', $sesMessage->returnpath);
}
if ($sesMessage->subject != null && strlen($sesMessage->subject) > 0) {
$ses_request->setParameter('Message.Subject.Data', $sesMessage->subject);
if ($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) {
$ses_request->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset);
}
}
if ($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) {
$ses_request->setParameter('Message.Body.Text.Data', $sesMessage->messagetext);
if ($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) {
$ses_request->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset);
}
}
if ($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) {
$ses_request->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml);
if ($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) {
$ses_request->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset);
}
}
$i = 1;
foreach ($sesMessage->message_tags as $key => $value) {
$ses_request->setParameter('Tags.member.' . $i . '.Name', $key);
$ses_request->setParameter('Tags.member.' . $i . '.Value', $value);
$i++;
}
}
$ses_response = $ses_request->getResponse();
if ($ses_response->error === false && $ses_response->code !== 200) {
$response = array(
'code' => $ses_response->code,
'error' => array('Error' => array('message' => 'Unexpected HTTP status')),
);
return $response;
}
if ($ses_response->error !== false) {
if (($this->__trigger_errors && ($trigger_error !== false)) || $trigger_error === true) {
$this->__triggerError('sendEmail', $ses_response->error);
return false;
}
return $ses_response;
}
$response = array(
'MessageId' => (string)$ses_response->body->{"{$action}Result"}->MessageId,
'RequestId' => (string)$ses_response->body->ResponseMetadata->RequestId,
);
return $response;
}
public function sendRawEmail($sesMessage)
{
$ses_request = $this->getRequestHandler('POST');
$ses_request->setParameter('Action', 'SendRawEmail');
// https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html
$ses_request->setParameter('RawMessage.Data', $sesMessage);
$ses_response = $ses_request->getResponse();
if (($ses_response->error === false && $ses_response->code !== 200) || $ses_response->error !== false) {
return new \WP_Error($ses_response->code, $this->getErrorMessage('sendRawEmail', $ses_response->error), $ses_response->error);
}
return array(
'MessageId' => (string)$ses_response->body->SendRawEmailResult->MessageId,
'RequestId' => (string)$ses_response->body->ResponseMetadata->RequestId,
);
}
/**
* Trigger an error message
*
* {@internal Used by member functions to output errors}
* @param string $functionname The name of the function that failed
* @param array $error Array containing error information
* @return void
*/
public function __triggerError($functionname, $error)
{
trigger_error($this->getErrorMessage($functionname, $error), E_USER_WARNING); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public function getErrorMessage($functionname, $error)
{
if ($error == false) {
return sprintf("SimpleEmailService::%s(): Encountered an error, but no description given", $functionname);
} else if (isset($error['curl']) && $error['curl']) {
return sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'], $error['message']);
} else if (isset($error['Error'])) {
$e = $error['Error'];
return sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']);
}
return sprintf("SimpleEmailService::%s(): Encountered an error: %s", $functionname, $error);
}
/**
* Set SES Request
*
* @param SimpleEmailServiceRequest $ses_request description
* @return SimpleEmailService $this
*/
public function setRequestHandler(SimpleEmailServiceRequest $ses_request = null)
{
if (!is_null($ses_request)) {
$ses_request->setSES($this);
}
$this->__ses_request = $ses_request;
return $this;
}
/**
* Get SES Request
*
* @param string $verb HTTP Verb: GET, POST, DELETE
* @return SimpleEmailServiceRequest SES Request
*/
public function getRequestHandler($verb)
{
if (empty($this->__ses_request)) {
$this->__ses_request = new SimpleEmailServiceRequest($this, $verb);
} else {
$this->__ses_request->setVerb($verb);
}
return $this->__ses_request;
}
}

View File

@@ -0,0 +1,640 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
/**
* SimpleEmailServiceMessage PHP class
*
* @link https://github.com/daniel-zahariev/php-aws-ses
* @package AmazonSimpleEmailService
* @version v0.9.1
*/
final class SimpleEmailServiceMessage {
// these are public for convenience only
// these are not to be used outside of the SimpleEmailService class!
public $to, $cc, $bcc, $replyto, $recipientsCharset;
public $from, $returnpath;
public $subject, $messagetext, $messagehtml;
public $subjectCharset, $messageTextCharset, $messageHtmlCharset;
public $attachments, $customHeaders, $configuration_set, $message_tags;
public $is_clean, $raw_message;
public $mime;
public function __construct() {
$this->to = array();
$this->cc = array();
$this->bcc = array();
$this->replyto = array();
$this->recipientsCharset = 'UTF-8';
$this->from = null;
$this->returnpath = null;
$this->subject = null;
$this->messagetext = null;
$this->messagehtml = null;
$this->subjectCharset = 'UTF-8';
$this->messageTextCharset = 'UTF-8';
$this->messageHtmlCharset = 'UTF-8';
$this->attachments = array();
$this->customHeaders = array();
$this->configuration_set = null;
$this->message_tags = array();
$this->is_clean = true;
$this->raw_message = null;
}
/**
* addTo, addCC, addBCC, and addReplyTo have the following behavior:
* If a single address is passed, it is appended to the current list of addresses.
* If an array of addresses is passed, that array is merged into the current list.
*
* @return SimpleEmailServiceMessage $this
* @link http://docs.aws.amazon.com/ses/latest/APIReference/API_Destination.html
*/
public function addTo($to) {
if (!is_array($to)) {
$this->to[] = $to;
} else {
$this->to = array_unique(array_merge($this->to, $to));
}
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setTo($to) {
$this->to = (array) $to;
$this->is_clean = false;
return $this;
}
/**
* Clear the To: email address(es) for the message
*
* @return SimpleEmailServiceMessage $this
*/
public function clearTo() {
$this->to = array();
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
* @see addTo()
*/
public function addCC($cc) {
if (!is_array($cc)) {
$this->cc[] = $cc;
} else {
$this->cc = array_merge($this->cc, $cc);
}
$this->is_clean = false;
return $this;
}
/**
* Clear the CC: email address(es) for the message
*
* @return SimpleEmailServiceMessage $this
*/
public function clearCC() {
$this->cc = array();
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
* @see addTo()
*/
public function addBCC($bcc) {
if (!is_array($bcc)) {
$this->bcc[] = $bcc;
} else {
$this->bcc = array_merge($this->bcc, $bcc);
}
$this->is_clean = false;
return $this;
}
/**
* Clear the BCC: email address(es) for the message
*
* @return SimpleEmailServiceMessage $this
*/
public function clearBCC() {
$this->bcc = array();
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
* @see addTo()
*/
public function addReplyTo($replyto) {
if (!is_array($replyto)) {
$this->replyto[] = $replyto;
} else {
$this->replyto = array_merge($this->replyto, $replyto);
}
$this->is_clean = false;
return $this;
}
/**
* Clear the Reply-To: email address(es) for the message
*
* @return SimpleEmailServiceMessage $this
*/
public function clearReplyTo() {
$this->replyto = array();
$this->is_clean = false;
return $this;
}
/**
* Clear all of the message recipients in one go
*
* @return SimpleEmailServiceMessage $this
* @uses clearTo()
* @uses clearCC()
* @uses clearBCC()
* @uses clearReplyTo()
*/
public function clearRecipients() {
$this->clearTo();
$this->clearCC();
$this->clearBCC();
$this->clearReplyTo();
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setFrom($from) {
$this->from = $from;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setReturnPath($returnpath) {
$this->returnpath = $returnpath;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setRecipientsCharset($charset) {
$this->recipientsCharset = $charset;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setSubject($subject) {
$this->subject = $subject;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setSubjectCharset($charset) {
$this->subjectCharset = $charset;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
* @link http://docs.aws.amazon.com/ses/latest/APIReference/API_Message.html
*/
public function setMessageFromString($text, $html = null) {
$this->messagetext = $text;
$this->messagehtml = $html;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setMessageFromFile($textfile, $htmlfile = null) {
if (file_exists($textfile) && is_file($textfile) && is_readable($textfile)) {
$this->messagetext = file_get_contents($textfile);
} else {
$this->messagetext = null;
}
if (file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) {
$this->messagehtml = file_get_contents($htmlfile);
} else {
$this->messagehtml = null;
}
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setMessageFromURL($texturl, $htmlurl = null) {
if ($texturl !== null) {
$this->messagetext = file_get_contents($texturl);
} else {
$this->messagetext = null;
}
if ($htmlurl !== null) {
$this->messagehtml = file_get_contents($htmlurl);
} else {
$this->messagehtml = null;
}
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setMessageCharset($textCharset, $htmlCharset = null) {
$this->messageTextCharset = $textCharset;
$this->messageHtmlCharset = $htmlCharset;
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function setConfigurationSet($configuration_set = null) {
$this->configuration_set = $configuration_set;
$this->is_clean = false;
return $this;
}
/**
* @return array $message_tags
*/
public function getMessageTags() {
return $this->message_tags;
}
/**
* @return null|mixed $message_tag
*/
public function getMessageTag($key) {
return isset($this->message_tags[$key]) ? $this->message_tags[$key] : null;
}
/**
* Add Message tag
*
* Both key and value can contain only ASCII letters (a-z, A-Z), numbers (0-9), underscores (_), or dashes (-) and be less than 256 characters.
*
* @param string $key
* @param mixed $value
* @return SimpleEmailServiceMessage $this
* @link https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-send-email.html
* @link https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html
*/
public function setMessageTag($key, $value) {
$this->message_tags[$key] = $value;
$this->is_clean = false;
return $this;
}
/**
* @param string $key The key of the tag to be removed
* @return SimpleEmailServiceMessage $this
*/
public function removeMessageTag($key) {
unset($this->message_tags[$key]);
$this->is_clean = false;
return $this;
}
/**
* @param array $message_tags
* @return SimpleEmailServiceMessage $this
*/
public function setMessageTags($message_tags = array()) {
$this->message_tags = array_merge($this->message_tags, $message_tags);
$this->is_clean = false;
return $this;
}
/**
* @return SimpleEmailServiceMessage $this
*/
public function removeMessageTags() {
$this->message_tags = array();
$this->is_clean = false;
return $this;
}
/**
* Add custom header - this works only with SendRawEmail
*
* @param string $header Your custom header
* @return SimpleEmailServiceMessage $this
* @link( Restrictions on headers, http://docs.aws.amazon.com/ses/latest/DeveloperGuide/header-fields.html)
*/
public function addCustomHeader($header) {
$this->customHeaders[] = $header;
$this->is_clean = false;
return $this;
}
/**
* Add email attachment by directly passing the content
*
* @param string $name The name of the file attachment as it will appear in the email
* @param string $data The contents of the attachment file
* @param string $mimeType Specify custom MIME type
* @param string $contentId Content ID of the attachment for inclusion in the mail message
* @param string $attachmentType Attachment type: attachment or inline
* @return SimpleEmailServiceMessage $this
*/
public function addAttachmentFromData($name, $data, $mimeType = 'application/octet-stream', $contentId = null, $attachmentType = 'attachment') {
$this->attachments[$name] = array(
'name' => $name,
'mimeType' => $mimeType,
'data' => $data,
'contentId' => $contentId,
'attachmentType' => ($attachmentType == 'inline' ? 'inline; filename="' . $name . '"' : $attachmentType),
);
$this->is_clean = false;
return $this;
}
/**
* Add email attachment by passing file path
*
* @param string $name The name of the file attachment as it will appear in the email
* @param string $path Path to the attachment file
* @param string $mimeType Specify custom MIME type
* @param string $contentId Content ID of the attachment for inclusion in the mail message
* @param string $attachmentType Attachment type: attachment or inline
* @return boolean Status of the operation
*/
public function addAttachmentFromFile($name, $path, $mimeType = 'application/octet-stream', $contentId = null, $attachmentType = 'attachment') {
if (file_exists($path) && is_file($path) && is_readable($path)) {
$this->addAttachmentFromData($name, file_get_contents($path), $mimeType, $contentId, $attachmentType);
return true;
}
$this->is_clean = false;
return false;
}
/**
* Add email attachment by passing file path
*
* @param string $name The name of the file attachment as it will appear in the email
* @param string $url URL to the attachment file
* @param string $mimeType Specify custom MIME type
* @param string $contentId Content ID of the attachment for inclusion in the mail message
* @param string $attachmentType Attachment type: attachment or inline
* @return boolean Status of the operation
*/
public function addAttachmentFromUrl($name, $url, $mimeType = 'application/octet-stream', $contentId = null, $attachmentType = 'attachment') {
$data = file_get_contents($url);
if ($data !== false) {
$this->addAttachmentFromData($name, $data, $mimeType, $contentId, $attachmentType);
return true;
}
$this->is_clean = false;
return false;
}
/**
* Get the existence of attached inline messages
*
* @return boolean
*/
public function hasInlineAttachments() {
foreach ($this->attachments as $attachment) {
if ($attachment['attachmentType'] != 'attachment') {
return true;
}
}
return false;
}
/**
* Get the raw mail message
*
* @return string
*/
public function getRawMessage($encode = true) {
if ($this->is_clean && !is_null($this->raw_message) && $encode) {
return $this->raw_message;
}
$this->is_clean = true;
$boundary = uniqid(rand(), true);
$raw_message = count($this->customHeaders) > 0 ? join("\n", $this->customHeaders) . "\n" : '';
if (!empty($this->message_tags)) {
$message_tags = array();
foreach ($this->message_tags as $key => $value) {
$message_tags[] = "{$key}={$value}";
}
$raw_message .= 'X-SES-MESSAGE-TAGS: ' . join(', ', $message_tags) . "\n";
}
if (!is_null($this->configuration_set)) {
$raw_message .= 'X-SES-CONFIGURATION-SET: ' . $this->configuration_set . "\n";
}
$raw_message .= count($this->to) > 0 ? 'To: ' . $this->encodeRecipients($this->to) . "\n" : '';
$raw_message .= 'From: ' . $this->encodeRecipients($this->from) . "\n";
if (!empty($this->replyto)) {
$raw_message .= 'Reply-To: ' . $this->encodeRecipients($this->replyto) . "\n";
}
if (!empty($this->cc)) {
$raw_message .= 'CC: ' . $this->encodeRecipients($this->cc) . "\n";
}
if (!empty($this->bcc)) {
$raw_message .= 'BCC: ' . $this->encodeRecipients($this->bcc) . "\n";
}
if ($this->subject != null && strlen($this->subject) > 0) {
$raw_message .= 'Subject: =?' . $this->subjectCharset . '?B?' . base64_encode($this->subject) . "?=\n";
}
$raw_message .= 'MIME-Version: 1.0' . "\n";
$raw_message .= 'Content-type: ' . ($this->hasInlineAttachments() ? 'multipart/related' : 'Multipart/Mixed') . '; boundary="' . $boundary . '"' . "\n";
$raw_message .= "\n--{$boundary}\n";
$raw_message .= 'Content-type: Multipart/Alternative; boundary="alt-' . $boundary . '"' . "\n";
if ($this->messagetext != null && strlen($this->messagetext) > 0) {
$charset = empty($this->messageTextCharset) ? '' : "; charset=\"{$this->messageTextCharset}\"";
$raw_message .= "\n--alt-{$boundary}\n";
$raw_message .= 'Content-Type: text/plain' . $charset . "\n\n";
$raw_message .= $this->messagetext . "\n";
}
if ($this->messagehtml != null && strlen($this->messagehtml) > 0) {
$charset = empty($this->messageHtmlCharset) ? '' : "; charset=\"{$this->messageHtmlCharset}\"";
$raw_message .= "\n--alt-{$boundary}\n";
$raw_message .= 'Content-Type: text/html' . $charset . "\n\n";
$raw_message .= $this->messagehtml . "\n";
}
$raw_message .= "\n--alt-{$boundary}--\n";
foreach ($this->attachments as $attachment) {
$raw_message .= "\n--{$boundary}\n";
$raw_message .= 'Content-Type: ' . $attachment['mimeType'] . '; name="' . $attachment['name'] . '"' . "\n";
$raw_message .= 'Content-Disposition: ' . $attachment['attachmentType'] . "\n";
if (!empty($attachment['contentId'])) {
$raw_message .= 'Content-ID: ' . $attachment['contentId'] . '' . "\n";
}
$raw_message .= 'Content-Transfer-Encoding: base64' . "\n";
$raw_message .= "\n" . chunk_split(base64_encode($attachment['data']), 76, "\n") . "\n";
}
$raw_message .= "\n--{$boundary}--\n";
if (!$encode) {
return $raw_message;
}
$this->raw_message = base64_encode($raw_message);
return $this->raw_message;
}
/**
* Encode recipient with the specified charset in `recipientsCharset`
*
* @return string Encoded recipients joined with comma
*/
public function encodeRecipients($recipient) {
if (is_array($recipient)) {
return join(', ', array_map(array($this, 'encodeRecipients'), $recipient));
}
if (preg_match("/(.*)<(.*)>/", $recipient, $regs)) {
$recipient = '=?' . $this->recipientsCharset . '?B?' . base64_encode($regs[1]) . '?= <' . $regs[2] . '>';
}
return $recipient;
}
/**
* Validates whether the message object has sufficient information to submit a request to SES.
*
* This does not guarantee the message will arrive, nor that the request will succeed;
* instead, it makes sure that no required fields are missing.
*
* This is used internally before attempting a SendEmail or SendRawEmail request,
* but it can be used outside of this file if verification is desired.
* May be useful if e.g. the data is being populated from a form; developers can generally
* use this function to verify completeness instead of writing custom logic.
*
* @return boolean
*/
public function validate() {
// at least one destination is required
if (count($this->to) == 0 && count($this->cc) == 0 && count($this->bcc) == 0) {
return false;
}
// sender is required
if ($this->from == null || strlen($this->from) == 0) {
return false;
}
// subject is required
if (($this->subject == null || strlen($this->subject) == 0)) {
return false;
}
// message is required
if ((empty($this->messagetext) || strlen((string) $this->messagetext) == 0)
&& (empty($this->messagehtml) || strlen((string) $this->messagehtml) == 0)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,376 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
/**
* SimpleEmailServiceRequest PHP class
*
* @link https://github.com/daniel-zahariev/php-aws-ses
* @package AmazonSimpleEmailService
* @version v0.9.1
*/
class SimpleEmailServiceRequest
{
private $ses, $verb, $parameters = array();
// CURL request handler that can be reused
protected $curl_handler = null;
// Holds the response from calling AWS's API
protected $response;
//
public static $curlOptions = array();
/**
* Constructor
*
* @param SimpleEmailService $ses The SimpleEmailService object making this request
* @param string $verb HTTP verb
* @return void
*/
public function __construct(SimpleEmailService $ses = null, $verb = 'GET') {
$this->ses = $ses;
$this->verb = $verb;
$this->response = (object) array('body' => '', 'code' => 0, 'error' => false);
}
/**
* Set SES class
*
* @param SimpleEmailService $ses
* @return SimpleEmailServiceRequest $this
*/
public function setSES(SimpleEmailService $ses) {
$this->ses = $ses;
return $this;
}
/**
* Set HTTP method
*
* @param string $verb
* @return SimpleEmailServiceRequest $this
*/
public function setVerb($verb) {
$this->verb = $verb;
return $this;
}
/**
* Set request parameter
*
* @param string $key Key
* @param string $value Value
* @param boolean $replace Whether to replace the key if it already exists (default true)
* @return SimpleEmailServiceRequest $this
*/
public function setParameter($key, $value, $replace = true) {
if(!$replace && isset($this->parameters[$key])) {
$temp = (array)($this->parameters[$key]);
$temp[] = $value;
$this->parameters[$key] = $temp;
} else {
$this->parameters[$key] = $value;
}
return $this;
}
/**
* Get the params for the request
*
* @return array $params
*/
public function getParametersEncoded() {
$params = array();
foreach ($this->parameters as $var => $value) {
if(is_array($value)) {
foreach($value as $v) {
$params[] = $var.'='.$this->__customUrlEncode($v);
}
} else {
$params[] = $var.'='.$this->__customUrlEncode($value);
}
}
sort($params, SORT_STRING);
return $params;
}
/**
* Clear the request parameters
* @return SimpleEmailServiceRequest $this
*/
public function clearParameters() {
$this->parameters = array();
return $this;
}
/**
* Instantiate and setup CURL handler for sending requests.
* Instance is cashed in `$this->curl_handler`
*
* @return resource $curl_handler
*/
protected function getCurlHandler() {
if (!empty($this->curl_handler))
return $this->curl_handler;
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php');
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 2 : 0));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0));
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
foreach(self::$curlOptions as $option => $value) {
curl_setopt($curl, $option, $value);
}
$this->curl_handler = $curl;
return $this->curl_handler;
}
/**
* Get the response
*
* @return object | false
*/
public function getResponse() {
$url = 'https://'.$this->ses->getHost().'/';
$query = implode('&', $this->getParametersEncoded());
$headers = $this->getHeaders($query);
$curl_handler = $this->getCurlHandler();
curl_setopt($curl_handler, CURLOPT_CUSTOMREQUEST, $this->verb);
// Request types
switch ($this->verb) {
case 'GET':
case 'DELETE':
$url .= '?'.$query;
break;
case 'POST':
curl_setopt($curl_handler, CURLOPT_POSTFIELDS, $query);
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
break;
}
curl_setopt($curl_handler, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl_handler, CURLOPT_URL, $url);
// Execute, grab errors
if (curl_exec($curl_handler)) {
$this->response->code = curl_getinfo($curl_handler, CURLINFO_HTTP_CODE);
} else {
$this->response->error = array(
'curl' => true,
'code' => curl_errno($curl_handler),
'message' => curl_error($curl_handler),
);
}
// cleanup for reusing the current instance for multiple requests
curl_setopt($curl_handler, CURLOPT_POSTFIELDS, '');
$this->parameters = array();
// Parse body into XML
if ($this->response->error === false && !empty($this->response->body)) {
$this->response->body = simplexml_load_string($this->response->body);
// Grab SES errors
if (!in_array($this->response->code, array(200, 201, 202, 204))
&& isset($this->response->body->Error)) {
$error = $this->response->body->Error;
$output = array();
$output['curl'] = false;
$output['Error'] = array();
$output['Error']['Type'] = (string)$error->Type;
$output['Error']['Code'] = (string)$error->Code;
$output['Error']['Message'] = (string)$error->Message;
$output['RequestId'] = (string)$this->response->body->RequestId;
$this->response->error = $output;
unset($this->response->body);
}
}
$response = $this->response;
$this->response = (object) array('body' => '', 'code' => 0, 'error' => false);
return $response;
}
/**
* Get request headers
* @param string $query
* @return array
*/
protected function getHeaders($query) {
$headers = array();
if ($this->ses->getRequestSignatureVersion() == SimpleEmailService::REQUEST_SIGNATURE_V4) {
$date = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Ymd\THis\Z');
$headers[] = 'X-Amz-Date: ' . $date;
$headers[] = 'Host: ' . $this->ses->getHost();
$headers[] = 'Authorization: ' . $this->__getAuthHeaderV4($date, $query);
} else {
// must be in format 'Sun, 06 Nov 1994 08:49:37 GMT'
$date = gmdate('D, d M Y H:i:s e');
$auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey();
$auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date);
$headers[] = 'Date: ' . $date;
$headers[] = 'Host: ' . $this->ses->getHost();
$headers[] = 'X-Amzn-Authorization: ' . $auth;
}
return $headers;
}
/**
* Destroy any leftover handlers
*/
public function __destruct() {
if (!empty($this->curl_handler))
@curl_close($this->curl_handler);
}
/**
* CURL write callback
*
* @param resource $curl CURL resource
* @param string $data Data
* @return integer
*/
private function __responseWriteCallback($curl, $data) {
if (!isset($this->response->body)) {
$this->response->body = $data;
} else {
$this->response->body .= $data;
}
return strlen($data);
}
/**
* Contributed by afx114
* URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html
* PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode
* See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php
*
* @param string $var String to encode
* @return string
*/
private function __customUrlEncode($var) {
return str_replace('%7E', '~', rawurlencode($var));
}
/**
* Generate the auth string using Hmac-SHA256
*
* @internal Used by SimpleEmailServiceRequest::getResponse()
* @param string $string String to sign
* @return string
*/
private function __getSignature($string) {
return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true));
}
/**
* @param string $key
* @param string $dateStamp
* @param string $regionName
* @param string $serviceName
* @param string $algo
* @return string
*/
private function __getSigningKey($key, $dateStamp, $regionName, $serviceName, $algo) {
$kDate = hash_hmac($algo, $dateStamp, 'AWS4' . $key, true);
$kRegion = hash_hmac($algo, $regionName, $kDate, true);
$kService = hash_hmac($algo, $serviceName, $kRegion, true);
return hash_hmac($algo,'aws4_request', $kService, true);
}
/**
* Implementation of AWS Signature Version 4
* @see https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
* @param string $amz_datetime
* @param string $query
* @return string
*/
private function __getAuthHeaderV4($amz_datetime, $query) {
$amz_date = substr($amz_datetime, 0, 8);
$algo = 'sha256';
$aws_algo = 'AWS4-HMAC-' . strtoupper($algo);
$host_parts = explode('.', $this->ses->getHost());
$service = $host_parts[0];
$region = $host_parts[1];
$canonical_uri = '/';
if($this->verb === 'POST') {
$canonical_querystring = '';
$payload_data = $query;
} else {
$canonical_querystring = $query;
$payload_data = '';
}
// ************* TASK 1: CREATE A CANONICAL REQUEST *************
$canonical_headers_list = [
'host:' . $this->ses->getHost(),
'x-amz-date:' . $amz_datetime
];
$canonical_headers = implode("\n", $canonical_headers_list) . "\n";
$signed_headers = 'host;x-amz-date';
$payload_hash = hash($algo, $payload_data, false);
$canonical_request = implode("\n", array(
$this->verb,
$canonical_uri,
$canonical_querystring,
$canonical_headers,
$signed_headers,
$payload_hash
));
// ************* TASK 2: CREATE THE STRING TO SIGN*************
$credential_scope = $amz_date. '/' . $region . '/' . $service . '/' . 'aws4_request';
$string_to_sign = implode("\n", array(
$aws_algo,
$amz_datetime,
$credential_scope,
hash($algo, $canonical_request, false)
));
// ************* TASK 3: CALCULATE THE SIGNATURE *************
// Create the signing key using the function defined above.
$signing_key = $this->__getSigningKey($this->ses->getSecretKey(), $amz_date, $region, $service, $algo);
// Sign the string_to_sign using the signing_key
$signature = hash_hmac($algo, $string_to_sign, $signing_key, false);
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
return $aws_algo . ' ' . implode(', ', array(
'Credential=' . $this->ses->getAccessKey() . '/' . $credential_scope,
'SignedHeaders=' . $signed_headers ,
'Signature=' . $signature
));
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Support\ValidationException;
use FluentMail\App\Services\Mailer\Providers\AmazonSes\SimpleEmailService;
class Validator
{
protected $errors = null;
protected $provider = null;
public function __construct($provider, $errors)
{
$this->errors = $errors;
$this->provider = $provider;
}
public function validate()
{
$data = fluentMail('request')->except(['action', 'nonce']);
$inputs = Arr::only(
$data['provider']['options'], ['access_key', 'secret_key', 'region']
);
$ses = new SimpleEmailService(
$inputs['access_key'],
$inputs['secret_key'],
'email.' . $inputs['region'] . '.amazonaws.com',
false
);
$result = $ses->listVerifiedEmailAddresses();
if (is_wp_error($result)) {
throw new ValidationException(wp_kses_post($result->get_error_message()), 400);
}
if ($result) {
$senderEmail = Arr::get(
$data, 'provider.options.sender_email'
);
if (!in_array($senderEmail, $result['Addresses'])) {
throw new \Exception(esc_html__('The from email is not verified', 'fluent-smtp'), 400);
}
fluentMail(Settings::class)->saveVerifiedEmails($result['Addresses']);
}
}
public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
{
if (isset($errcontext['e'])) {
$message = $errcontext['e']['Message'];
} else {
$message = $errcontext['error']['message'];
}
$newException = new ValidationException(
'', $errno, null, array_merge(
$this->errors, [
'sender_email' => [
$errcontext['functionname'] => $message
]
]
)
);
restore_error_handler();
throw $newException;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\AmazonSes;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if ($keyStoreType == 'db') {
if (!Arr::get($connection, 'access_key')) {
$errors['access_key']['required'] = __('Access key is required.', 'fluent-smtp');
}
if (!Arr::get($connection, 'secret_key')) {
$errors['secret_key']['required'] = __('Secret key is required.', 'fluent-smtp');
}
} else if ($keyStoreType == 'wp_config') {
if (!defined('FLUENTMAIL_AWS_ACCESS_KEY_ID') || !FLUENTMAIL_AWS_ACCESS_KEY_ID) {
$errors['access_key']['required'] = __('Please define FLUENTMAIL_AWS_ACCESS_KEY_ID in wp-config.php file.', 'fluent-smtp');
}
if (!defined('FLUENTMAIL_AWS_SECRET_ACCESS_KEY') || !FLUENTMAIL_AWS_SECRET_ACCESS_KEY) {
$errors['secret_key']['required'] = __('Please define FLUENTMAIL_AWS_SECRET_ACCESS_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
public function checkConnection($connection)
{
$connection = $this->filterConnectionVars($connection);
$region = 'email.' . $connection['region'] . '.amazonaws.com';
$ses = new SimpleEmailService(
$connection['access_key'],
$connection['secret_key'],
$region,
true
);
$lists = $ses->listVerifiedEmailAddresses();
if (is_wp_error($lists)) {
$this->throwValidationException(['api_error' => $lists->get_error_message()]);
}
return true;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\DefaultMail;
use Exception;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []) );
}
protected function postSend()
{
try {
$this->phpMailer->send();
return $this->handleSuccess();
} catch(Exception $e) {
return $this->handleFailure($e);
}
}
protected function handleSuccess()
{
$data = [
'code' => 200,
'message' => 'OK'
];
return $this->processResponse($data, true);
}
protected function handleFailure($exception)
{
$error = new \WP_Error($exception->getCode(), $exception->getMessage(), []);
return $this->handleResponse($error);
}
}

View File

@@ -0,0 +1,360 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\ElasticMail;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $boundary = '';
protected $postbody = [];
protected $url = 'https://api.elasticemail.com/v2/';
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$this->boundary = hash('sha256', uniqid('', true));
$this->postbody = [];
$replyTo = $this->getReplyTo();
$postData = [
'subject' => $this->getSubject(),
'from' => $this->getParam('sender_email'),
'fromName' => $this->getParam('sender_name'),
'replyTo' => Arr::get($replyTo, 'email'),
'replyToName' => Arr::get($replyTo, 'name'),
'msgTo' => $this->getToFormatted(),
'msgCC' => $this->getCcFormatted(), // with ; separated or null
'msgBcc' => $this->getBccFormatted(), // with ; separated or null
'bodyHtml' => '',
'bodyText' => '',
'charset' => $this->phpMailer->CharSet,
'encodingType' => 0,
'isTransactional' => ($this->getSetting('mail_type') == 'transactional') ? true : false
];
if ($this->phpMailer->ContentType == 'text/html') {
$postData['bodyHtml'] = $this->getBody();
} else if ($this->phpMailer->ContentType == 'multipart/alternative') {
$postData['bodyHtml'] = $this->getBody();
$postData['bodyText'] = $this->phpMailer->AltBody;
} else {
$postData['bodyText'] = $this->getBody();
}
foreach ($this->getParam('custom_headers') as $header) {
$key = trim($header['key']);
$postData['headers_' . $key] = $key . ': ' . trim($header['value']);
}
$this->parseAllPostData($postData);
$this->setAttachments();
$this->postbody[] = '--' . $this->boundary . '--';
$actualData = implode('', $this->postbody);
try {
$response = wp_remote_post($this->url . 'email/send?apikey=' . $this->getSetting('api_key'), array(
'method' => 'POST',
'headers' => array(
'content-type' => 'multipart/form-data; boundary=' . $this->boundary
),
'body' => $actualData
)
);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseBody = \json_decode($responseBody, true);
if (!$responseBody['success']) {
$returnResponse = new \WP_Error('api_error', $responseBody['error'], $responseBody);
} else {
$returnResponse = [
'code' => 200,
'message' => $responseBody
];
}
}
} catch (\Exception $exception) {
$returnResponse = new \WP_Error($exception->getCode(), $exception->getMessage(), []);
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
protected function parseAllPostData($data)
{
foreach ($data as $key => $item) {
if (empty($item)) {
continue;
}
if (is_array($item)) {
$this->parseAllPostData($item);
} else {
$this->postbody[] = '--' . $this->boundary . "\r\n" . 'Content-Disposition: form-data; name=' . $key . "\r\n\r\n" . $item . "\r\n";
}
}
}
protected function getFrom()
{
$from = [
'email' => $this->getParam('sender_email')
];
if ($name = $this->getParam('sender_name')) {
$from['name'] = $name;
}
return $from;
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
return reset($replyTo);
}
return [
'name' => '',
'email' => ''
];
}
protected function getRecipients()
{
$recipients = [
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy(),
];
$recipients = array_filter($recipients);
foreach ($recipients as $key => $recipient) {
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipient);
$this->attributes['formatted'][$key] = implode(', ', $array);
}
return [$recipients];
}
protected function getCcFormatted()
{
$ccs = $this->getCarbonCopy();
if (!$ccs) {
return null;
}
$ccs = array_filter($ccs);
$toFormatted = [];
foreach ($ccs as $toEmail) {
if (!empty($toEmail['name'])) {
$string = $toEmail['name'] . ' <' . $toEmail['email'] . '>';
} else {
$string = $toEmail['email'];
}
$toFormatted[] = $string;
}
$toFormatted = array_filter($toFormatted);
return implode(';', $toFormatted);
}
protected function getBccFormatted()
{
$ccs = $this->getBlindCarbonCopy();
if (!$ccs) {
return null;
}
$ccs = array_filter($ccs);
$toFormatted = [];
foreach ($ccs as $toEmail) {
if (!empty($toEmail['name'])) {
$string = $toEmail['name'] . ' <' . $toEmail['email'] . '>';
} else {
$string = $toEmail['email'];
}
$toFormatted[] = $string;
}
$toFormatted = array_filter($toFormatted);
return implode(';', $toFormatted);
}
protected function getToFormatted()
{
$to = $this->getParam('to');
$toFormatted = [];
foreach ($to as $toEmail) {
if (!empty($toEmail['name'])) {
$string = $toEmail['name'] . ' <' . $toEmail['email'] . '>';
} else {
$string = $toEmail['email'];
}
$toFormatted[] = $string;
}
$toFormatted = array_filter($toFormatted);
return implode(';', $toFormatted);
}
protected function getCarbonCopy()
{
return $this->getParam('headers.cc');
}
protected function getBlindCarbonCopy()
{
return $this->getParam('headers.bcc');
}
protected function getBody()
{
return $this->getParam('message');
}
protected function setAttachments()
{
$rawAttachments = $this->getParam('attachments');
if (empty($rawAttachments) === true) {
return false;
}
foreach ($rawAttachments as $i => $attpath) {
if (empty($attpath) === true) {
continue;
}
if (!is_readable($attpath[0]) || !is_file($attpath[0])) {
continue;
}
//Extracting the file name
$filenameonly = explode(DIRECTORY_SEPARATOR, $attpath[0]);
$fname = end($filenameonly);
$mimeType = 'application/octet-stream'; // Default
if (function_exists('mime_content_type')) {
$detectedMime = mime_content_type($attpath[0]);
if ($detectedMime !== false) {
$mimeType = $detectedMime;
}
} elseif (function_exists('finfo_file')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$detectedMime = finfo_file($finfo, $attpath[0]);
finfo_close($finfo);
if ($detectedMime !== false) {
$mimeType = $detectedMime;
}
}
// Add boundary and headers for attachment
$this->postbody[] = '--' . $this->boundary . "\r\n";
$this->postbody[] = 'Content-Disposition: form-data; name="attachments' . ($i + 1) . '"; filename="' . $fname . '"' . "\r\n";
$this->postbody[] = 'Content-Type: ' . $mimeType . "\r\n";
$this->postbody[] = "\r\n";
$handle = fopen($attpath[0], "rb");
if ($handle) {
$fileContent = '';
while (($buffer = fread($handle, 8192)) !== false && $buffer !== '') {
$fileContent .= $buffer;
}
fclose($handle);
$this->postbody[] = $fileContent . "\r\n";
}
}
}
protected function getCustomEmailHeaders()
{
return [];
}
protected function getRequestHeaders()
{
return [
'Content-Type' => 'application/json',
'X-ElasticEmail-ApiKey' => $this->getSetting('api_key')
];
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_ELASTICMAIL_API_KEY') ? FLUENTMAIL_ELASTICMAIL_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
public function checkConnection($connection)
{
$this->setSettings($connection);
$request = wp_remote_get($this->url . 'account/profileoverview', [
'body' => [
'apikey' => $this->getSetting('api_key')
]
]);
if (is_wp_error($request)) {
$this->throwValidationException([
'api_key' => [
'required' => $request->get_error_message()
]
]);
}
$response = json_decode(wp_remote_retrieve_body($request), true);
if (!$response || empty($response['success'])) {
$error = 'API Key is invalid';
if (!empty($response['error'])) {
$error = $response['error'];
}
$this->throwValidationException([
'api_key' => [
'required' => $error
]
]);
}
return true;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\ElasticMail;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_ELASTICMAIL_API_KEY') || !FLUENTMAIL_ELASTICMAIL_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_ELASTICMAIL_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers;
use InvalidArgumentException;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Core\Application;
class Factory
{
protected $app = null;
protected $settings = null;
public function __construct(Application $app, Settings $settings)
{
$this->app = $app;
$this->settings = $settings;
}
public function make($provider)
{
return $this->app->make($provider);
}
public function get($email)
{
if (!($conn = $this->settings->getConnection($email))) {
$conn = $this->getDefaultProvider();
}
if ($conn) {
$settings = array_merge($conn['provider_settings'], [
'title' => $conn['title']
]);
return $this->make(
$conn['provider_settings']['provider']
)->setSettings($settings);
}
throw new InvalidArgumentException(
esc_html__('There is no matching provider found by email: ', 'fluent-smtp') . $email // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
);
}
public function getDefaultProvider()
{
return fluentMailDefaultConnection();
}
}

View File

@@ -0,0 +1,334 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Gmail;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
public function send()
{
if ($this->preSend() && $this->phpMailer->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
protected function postSend()
{
try {
$returnResponse = $this->sendViaApi();
} catch (\Exception $e) {
$returnResponse = new \WP_Error(422, $e->getMessage(), []);
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if (Arr::get($settings, 'key_store') == 'wp_config') {
$settings['client_id'] = defined('FLUENTMAIL_GMAIL_CLIENT_ID') ? FLUENTMAIL_GMAIL_CLIENT_ID : '';
$settings['client_secret'] = defined('FLUENTMAIL_GMAIL_CLIENT_SECRET') ? FLUENTMAIL_GMAIL_CLIENT_SECRET : '';
}
$this->settings = $settings;
return $this;
}
private function sendViaApi()
{
if (!class_exists('\FluentSmtpLib\Google\Service\Gmail\Message')) {
require_once FLUENTMAIL_PLUGIN_PATH . 'includes/libs/google-api-client/build/vendor/autoload.php';
}
$message = $this->phpMailer->getSentMIMEMessage();
$data = $this->getSetting();
$googleApiMessage = new \FluentSmtpLib\Google\Service\Gmail\Message();
$file_size = strlen($message);
$googleClient = $this->getApiClient($data);
if (is_wp_error($googleClient)) {
return $googleClient;
}
$googleService = new \FluentSmtpLib\Google\Service\Gmail($googleClient);
$result = array();
try {
$googleClient->setDefer(true);
$result = $googleService->users_messages->send('me', $googleApiMessage, array('uploadType' => 'resumable'));
$chunkSizeBytes = 1 * 1024 * 1024;
// create mediafile upload
$media = new \FluentSmtpLib\Google\Http\MediaFileUpload(
$googleClient,
$result,
'message/rfc822',
$message,
true,
$chunkSizeBytes
);
$media->setFileSize($file_size);
$status = false;
while (!$status) {
$status = $media->nextChunk();
}
$result = false;
// Reset to the client to execute requests immediately in the future.
$googleClient->setDefer(false);
$googleMessageId = $status->getId();
} catch (\Exception $e) {
$errorMessage = $e->getMessage();
return new \WP_Error(422, $errorMessage, []);
}
return array(
'MessageId' => $googleMessageId,
);
}
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
$clientId = Arr::get($connection, 'client_id');
$clientSecret = Arr::get($connection, 'client_secret');
if ($keyStoreType == 'db') {
if (!$clientId) {
$errors['client_id']['required'] = __('Application Client ID is required.', 'fluent-smtp');
}
if (!$clientSecret) {
$errors['client_secret']['required'] = __('Application Client Secret key is required.', 'fluent-smtp');
}
} else if ($keyStoreType == 'wp_config') {
if (!defined('FLUENTMAIL_GMAIL_CLIENT_ID') || !FLUENTMAIL_GMAIL_CLIENT_ID) {
$errors['client_id']['required'] = __('Please define FLUENTMAIL_GMAIL_CLIENT_ID in wp-config.php file.', 'fluent-smtp');
} else {
$clientId = FLUENTMAIL_GMAIL_CLIENT_ID;
}
if (!defined('FLUENTMAIL_GMAIL_CLIENT_SECRET') || !FLUENTMAIL_GMAIL_CLIENT_SECRET) {
$errors['client_secret']['required'] = __('Please define FLUENTMAIL_GMAIL_CLIENT_SECRET in wp-config.php file.', 'fluent-smtp');
} else {
$clientSecret = FLUENTMAIL_GMAIL_CLIENT_SECRET;
}
}
if ($errors) {
$this->throwValidationException($errors);
}
$accessToken = Arr::get($connection, 'access_token');
$authToken = Arr::get($connection, 'auth_token');
if (!$accessToken && $authToken) {
// this is new, We have to generate the tokens
$body = [
'code' => $authToken,
'grant_type' => 'authorization_code',
'redirect_uri' => apply_filters('fluentsmtp_gapi_callback', 'https://fluentsmtp.com/gapi/'), // 'urn:ietf:wg:oauth:2.0:oob'
'client_id' => $clientId,
'client_secret' => $clientSecret
];
$tokens = $this->makeRequest('https://accounts.google.com/o/oauth2/token', $body, 'POST');
if (is_wp_error($tokens)) {
$errors['auth_token']['required'] = $tokens->get_error_message();
} else {
add_filter('fluentmail_saving_connection_data', function ($con, $provider) use ($connection, $tokens) {
if ($provider != 'gmail') {
return $con;
}
if (Arr::get($con, 'connection.sender_email') != $connection['sender_email']) {
return $con;
}
$con['connection']['refresh_token'] = $tokens['refresh_token'];
$con['connection']['access_token'] = $tokens['access_token'];
$con['connection']['auth_token'] = '';
$con['connection']['expire_stamp'] = time() + $tokens['expires_in'];
$con['connection']['expires_in'] = $tokens['expires_in'];
$con['connection']['version'] = 2;
return $con;
}, 10, 2);
}
} else if (!$authToken && !$accessToken) {
$errors['auth_token']['required'] = __('Please Provide Auth Token.', 'fluent-smtp');
}
if ($errors) {
$this->throwValidationException($errors);
}
}
private function makeRequest($url, $bodyArgs, $type = 'GET', $headers = false)
{
if (!$headers) {
$headers = array(
'Content-Type' => 'application/http',
'Content-Transfer-Encoding' => 'binary',
'MIME-Version' => '1.0',
);
}
$args = [
'headers' => $headers
];
if ($bodyArgs) {
$args['body'] = json_encode($bodyArgs);
}
$args['method'] = $type;
$request = wp_remote_request($url, $args);
if (is_wp_error($request)) {
$message = $request->get_error_message();
return new \WP_Error(422, $message);
}
$body = json_decode(wp_remote_retrieve_body($request), true);
if (!empty($body['error'])) {
$error = 'Unknown Error';
if (isset($body['error_description'])) {
$error = $body['error_description'];
} else if (!empty($body['error']['message'])) {
$error = $body['error']['message'];
}
return new \WP_Error(422, $error);
}
return $body;
}
private function saveNewTokens($existingData, $tokens)
{
if (empty($tokens['access_token']) || empty($tokens['refresh_token'])) {
return false;
}
$senderEmail = $existingData['sender_email'];
$existingData['access_token'] = $tokens['access_token'];
$existingData['refresh_token'] = $tokens['refresh_token'];
$existingData['expire_stamp'] = $tokens['expires_in'] + time();
$existingData['expires_in'] = $tokens['expires_in'];
(new Settings())->updateConnection($senderEmail, $existingData);
fluentMailGetProvider($senderEmail, true); // we are clearing the static cache here
wp_schedule_single_event($existingData['expire_stamp'] - 360, 'fluentsmtp_renew_gmail_token');
return true;
}
private function getApiClient($data)
{
$senderEmail = $data['sender_email'];
static $cachedServices = [];
if (isset($cachedServices[$senderEmail])) {
return $cachedServices[$senderEmail];
}
if (!class_exists('\FluentSmtpLib\Google\Client')) {
require_once FLUENTMAIL_PLUGIN_PATH . 'includes/libs/google-api-client/build/vendor/autoload.php';
}
$client = new \FluentSmtpLib\Google\Client();
$client->setClientId($data['client_id']);
$client->setClientSecret($data['client_secret']);
$client->addScope("https://www.googleapis.com/auth/gmail.compose");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$tokens = [
'access_token' => $data['access_token'],
'refresh_token' => $data['refresh_token'],
'expires_in' => $data['expire_stamp'] - time()
];
$client->setAccessToken($tokens);
// check if expired or will be expired in 5 minutes
if (($data['expire_stamp'] - 300) < time()) {
$newTokens = $client->refreshToken($data['refresh_token']);
$result = $this->saveNewTokens($data, $newTokens);
if (!$result) {
$errorDescription = Arr::get($newTokens, 'error_description');
if (!$errorDescription) {
$errorDescription = __('Failed to renew token with Gmail Api', 'fluent-smtp');
}
return new \WP_Error('api_error', $errorDescription);
}
$client->setAccessToken($newTokens);
}
$cachedServices[$senderEmail] = $client;
return $cachedServices[$senderEmail];
}
public function getConnectionInfo($connection)
{
if (Arr::get($connection, 'key_store') == 'wp_config') {
$connection['client_id'] = defined('FLUENTMAIL_GMAIL_CLIENT_ID') ? FLUENTMAIL_GMAIL_CLIENT_ID : '';
$connection['client_secret'] = defined('FLUENTMAIL_GMAIL_CLIENT_SECRET') ? FLUENTMAIL_GMAIL_CLIENT_SECRET : '';
}
if (!class_exists('\FluentSmtpLib\Google\Client')) {
require_once FLUENTMAIL_PLUGIN_PATH . 'includes/libs/google-api-client/build/vendor/autoload.php';
}
$client = $this->getApiClient($connection);
if (is_wp_error($client)) {
return '<p style="color: red; text-align: center; font-size: 18px;">ERROR: ' . $connection->get_error_message() . '</p>';
}
$info = fluentMailgetConnection($connection['sender_email']);
$connection = $info->getSetting();
$extraRow = [
'title' => __('Token Validity', 'fluent-smtp'),
'content' => 'Valid (' . (int)(($connection['expire_stamp'] - time()) / 60) . 'minutes)'
];
if (($connection['expire_stamp']) < time()) {
$extraRow['content'] = __('Invalid. Please re-authenticate', 'fluent-smtp');
}
$connection['extra_rows'] = [$extraRow];
return [
'info' => (string)fluentMail('view')->make('admin.general_connection_info', [
'connection' => $connection
])
];
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Mailgun;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 200;
protected $url = null;
const API_BASE_US = 'https://api.mailgun.net/v3/';
const API_BASE_EU = 'https://api.eu.mailgun.net/v3/';
public function send()
{
if ($this->preSend()) {
$this->setUrl();
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
protected function setUrl()
{
$url = $this->getSetting('region') == 'eu' ? self::API_BASE_EU : self::API_BASE_US;
$url .= sanitize_text_field($this->getSetting('domain_name') . '/messages');
return $this->url = $url;
}
public function postSend()
{
$content_type = $this->getHeader('content-type');
$body = [
'from' => $this->getFrom(),
'subject' => $this->getSubject(),
'h:X-Mailer' => 'FluentMail - Mailgun',
'h:Content-Type' => $content_type
];
if (stripos($content_type, 'html') === false) {
$body['text'] = $this->getBody();
} else if ($content_type == 'multipart/alternative') {
$body['html'] = $this->getBody();
$body['text'] = $this->phpMailer->AltBody;
} else {
$body['html'] = $this->getBody();
}
if ($replyTo = $this->getReplyTo()) {
$body['h:Reply-To'] = $replyTo;
}
$recipients = [
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy()
];
if ($recipients = array_filter($recipients)) {
$body = array_merge($body, $recipients);
}
foreach ($this->getParam('custom_headers') as $header) {
$key = trim($header['key']);
$body['h:' . $key] = trim($header['value']);
}
$params = [
'body' => $body,
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
if (!empty($this->attributes['attachments'])) {
$params = $this->getAttachments($params);
}
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
if ($isOKCode) {
$responseBody = \json_decode($responseBody, true);
}
if ($isOKCode && isset($responseBody['id'])) {
$returnResponse = [
'id' => Arr::get($responseBody, 'id'),
'message' => Arr::get($responseBody, 'message')
];
} else {
$returnResponse = new \WP_Error($responseCode, __('Mailgun API Error', 'fluent-smtp'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_MAILGUN_API_KEY') ? FLUENTMAIL_MAILGUN_API_KEY : '';
$settings['domain_name'] = defined('FLUENTMAIL_MAILGUN_DOMAIN') ? FLUENTMAIL_MAILGUN_DOMAIN : '';
}
$this->settings = $settings;
return $this;
}
protected function getFrom()
{
return $this->getParam('from');
}
protected function getReplyTo()
{
return $this->getRecipients(
$this->getParam('headers.reply-to')
);
}
protected function getTo()
{
return $this->getRecipients($this->getParam('to'));
}
protected function getCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.bcc'));
}
protected function getRecipients($recipients)
{
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipients);
return implode(', ', $array);
}
protected function getBody()
{
return $this->getParam('message');
}
protected function getAttachments($params)
{
$data = [];
$payload = '';
$attachments = $this->attributes['attachments'];
foreach ($attachments as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'content' => $file,
'name' => $fileName,
];
}
if (!empty($data)) {
$boundary = hash('sha256', uniqid('', true));
foreach ($params['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";
}
}
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 . '--';
$params['body'] = $payload;
$params['headers']['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
$this->attributes['headers']['content-type'] = 'multipart/form-data';
}
return $params;
}
protected function getRequestHeaders()
{
$apiKey = $this->getSetting('api_key');
return [
'Authorization' => 'Basic ' . base64_encode('api:' . $apiKey)
];
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Mailgun;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
if (! Arr::get($connection, 'domain_name')) {
$errors['domain_name']['required'] = __('Domain name is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_MAILGUN_API_KEY') || !FLUENTMAIL_MAILGUN_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_MAILGUN_API_KEY in wp-config.php file.', 'fluent-smtp');
}
if(!defined('FLUENTMAIL_MAILGUN_DOMAIN') || !FLUENTMAIL_MAILGUN_DOMAIN) {
$errors['domain_name']['required'] = __('Please define FLUENTMAIL_MAILGUN_DOMAIN in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Outlook;
use FluentMail\Includes\Support\Arr;
class API
{
private $clientId;
private $clientSecret;
public function __construct($clientId = '', $clientSecret = '')
{
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
}
public function getAuthUrl()
{
$fluentClient = new \FluentMail\Includes\OAuth2Provider($this->getConfig());
return $fluentClient->getAuthorizationUrl();
}
public function generateToken($authCode)
{
return $this->sendTokenRequest('authorization_code', [
'code' => $authCode
]);
}
/**
* @return mixed|string
*/
public function sendTokenRequest($type, $params)
{
$fluentClient = new \FluentMail\Includes\OAuth2Provider($this->getConfig());
try {
$tokens = $fluentClient->getAccessToken($type, $params);
return $tokens;
} catch (\Exception$exception) {
return new \WP_Error(422, $exception->getMessage());
}
}
/**
* @return array | \WP_Error
*/
public function sendMime($mime, $accessToken)
{
$response = wp_remote_request('https://graph.microsoft.com/v1.0/me/sendMail', [
'method' => 'POST',
'headers' => [
'Authorization' => 'Bearer ' . $accessToken,
'Content-Type' => 'text/plain'
],
'body' => $mime
]);
if (is_wp_error($response)) {
return $response;
}
$responseCode = wp_remote_retrieve_response_code($response);
if ($responseCode >= 300) {
$error = Arr::get($response, 'response.message');
if (!$error) {
$responseBody = json_decode(wp_remote_retrieve_body($response), true);
$error = Arr::get($responseBody, 'error.message');
if (!$error) {
$error = __('Something with wrong with Outlook API. Please check your API Settings', 'fluent-smtp');
}
}
return new \WP_Error($responseCode, $error);
}
$header = wp_remote_retrieve_headers($response);
return $header->getAll();
}
public function getRedirectUrl()
{
return rest_url('fluent-smtp/outlook_callback');
}
private function getConfig()
{
return [
'clientId' => $this->clientId,
'clientSecret' => $this->clientSecret,
'redirectUri' => $this->getRedirectUrl(),
'urlAuthorize' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
'urlAccessToken' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
'urlResourceOwnerDetails' => '',
'scopes' => 'https://graph.microsoft.com/user.read https://graph.microsoft.com/mail.readwrite https://graph.microsoft.com/mail.send https://graph.microsoft.com/mail.send.shared offline_access'
];
}
}

View File

@@ -0,0 +1,208 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Outlook;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
public function send()
{
$this->phpMailer->Encoding = 'base64';
if ($this->preSend() && $this->phpMailer->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
protected function postSend()
{
try {
$returnResponse = $this->sendViaApi();
} catch (\Exception $e) {
$returnResponse = new \WP_Error(422, $e->getMessage(), []);
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if (Arr::get($settings, 'key_store') == 'wp_config') {
$settings['client_id'] = defined('FLUENTMAIL_OUTLOOK_CLIENT_ID') ? FLUENTMAIL_OUTLOOK_CLIENT_ID : '';
$settings['client_secret'] = defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET') ? FLUENTMAIL_OUTLOOK_CLIENT_SECRET : '';
}
$this->settings = $settings;
return $this;
}
private function sendViaApi()
{
$mime = chunk_split(base64_encode($this->phpMailer->getSentMIMEMessage()), 76, "\n");
$data = $this->getSetting();
$accessToken = $this->getAccessToken($data);
$api = (new API($data['client_id'], $data['client_secret']));
$result = $api->sendMime($mime, $accessToken);
if(is_wp_error($result)) {
$errorMessage = $result->get_error_message();
return new \WP_Error(422, $errorMessage, []);
} else {
return array(
'RequestId' => $result['request-id'],
);
}
}
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
$clientId = Arr::get($connection, 'client_id');
$clientSecret = Arr::get($connection, 'client_secret');
if ($keyStoreType == 'db') {
if (!$clientId) {
$errors['client_id']['required'] = __('Application Client ID is required.', 'fluent-smtp');
}
if (!$clientSecret) {
$errors['client_secret']['required'] = __('Application Client Secret key is required.', 'fluent-smtp');
}
} else if ($keyStoreType == 'wp_config') {
if (!defined('FLUENTMAIL_OUTLOOK_CLIENT_ID') || !FLUENTMAIL_OUTLOOK_CLIENT_ID) {
$errors['client_id']['required'] = __('Please define FLUENTMAIL_OUTLOOK_CLIENT_ID in wp-config.php file.', 'fluent-smtp');
} else {
$clientId = FLUENTMAIL_OUTLOOK_CLIENT_ID;
}
if (!defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET') || !FLUENTMAIL_OUTLOOK_CLIENT_SECRET) {
$errors['client_secret']['required'] = __('Please define FLUENTMAIL_OUTLOOK_CLIENT_SECRET in wp-config.php file.', 'fluent-smtp');
} else {
$clientSecret = FLUENTMAIL_OUTLOOK_CLIENT_SECRET;
}
}
if ($errors) {
$this->throwValidationException($errors);
}
$accessToken = Arr::get($connection, 'access_token');
$authToken = Arr::get($connection, 'auth_token');
if (!$accessToken && $authToken) {
$tokens = (new API($clientId, $clientSecret))->generateToken($authToken);
if (is_wp_error($tokens)) {
$errors['auth_token']['required'] = $tokens->get_error_message();
} else {
add_filter('fluentmail_saving_connection_data', function ($con, $provider) use ($connection, $tokens) {
if ($provider != 'outlook') {
return $con;
}
if (Arr::get($con, 'connection.sender_email') != $connection['sender_email']) {
return $con;
}
$con['connection']['refresh_token'] = $tokens['refresh_token'];
$con['connection']['access_token'] = $tokens['access_token'];
$con['connection']['auth_token'] = '';
$con['connection']['expire_stamp'] = time() + $tokens['expires_in'];
return $con;
}, 10, 2);
}
} else if (!$authToken && !$accessToken) {
$errors['auth_token']['required'] = __('Please Provide Auth Token.', 'fluent-smtp');
}
if ($errors) {
$this->throwValidationException($errors);
}
}
private function saveNewTokens($existingData, $tokens)
{
if (empty($tokens['access_token']) || empty($tokens['refresh_token'])) {
return false;
}
$senderEmail = $existingData['sender_email'];
$existingData['access_token'] = $tokens['access_token'];
$existingData['refresh_token'] = $tokens['refresh_token'];
$existingData['expire_stamp'] = $tokens['expires_in'] + time();
(new Settings())->updateConnection($senderEmail, $existingData);
return fluentMailGetProvider($senderEmail, true); // we are clearing the static cache here
}
private function getAccessToken($config)
{
$accessToken = $config['access_token'];
// check if expired or will be expired in 300 seconds
if ( ($config['expire_stamp'] - 300) < time()) {
$fluentAPi = (new API($config['client_id'], $config['client_secret']));
$tokens = $fluentAPi->sendTokenRequest('refresh_token', [
'refresh_token' => $config['refresh_token']
]);
if(is_wp_error($tokens)) {
return false;
}
$this->saveNewTokens($config, $tokens);
$accessToken = $tokens['access_token'];
}
return $accessToken;
}
public function getConnectionInfo($connection)
{
if (Arr::get($connection, 'key_store') == 'wp_config') {
$connection['client_id'] = defined('FLUENTMAIL_OUTLOOK_CLIENT_ID') ? FLUENTMAIL_OUTLOOK_CLIENT_ID : '';
$connection['client_secret'] = defined('FLUENTMAIL_OUTLOOK_CLIENT_SECRET') ? FLUENTMAIL_OUTLOOK_CLIENT_SECRET : '';
}
$this->getAccessToken($connection);
$info = fluentMailgetConnection($connection['sender_email']);
$connection = $info->getSetting();
$extraRow = [
'title' => __('Token Validity', 'fluent-smtp'),
'content' => 'Valid (' . intval((($connection['expire_stamp'] - time()) / 60)) . 'm)'
];
if (($connection['expire_stamp']) < time()) {
$extraRow['content'] = 'Invalid. Please re-authenticate';
}
$connection['extra_rows'] = [$extraRow];
return [
'info' => (string)fluentMail('view')->make('admin.general_connection_info', [
'connection' => $connection
])
];
}
}

View File

@@ -0,0 +1,211 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\PepiPost;
use WP_Error as WPError;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\PepiPost\ValidatorTrait;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 202;
protected $url = 'https://api.pepipost.com/v5/mail/send';
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$body = [
'from' => $this->getFrom(),
'personalizations' => $this->getRecipients(),
'subject' => $this->getSubject(),
'content' => $this->getBody(),
'headers' => $this->getCustomEmailHeaders()
];
if ($replyTo = $this->getReplyTo()) {
$body['reply_to'] = $replyTo;
}
if (!empty($this->getParam('attachments'))) {
$body['attachments'] = $this->getAttachments();
}
$params = [
'body' => json_encode($body),
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
$responseBody = \json_decode($responseBody, true);
if ($isOKCode) {
$returnResponse = [
'id' => Arr::get($responseBody, 'data.message_id'),
'message' => Arr::get($responseBody, 'message_id')
];
} else {
$returnResponse = new \WP_Error($responseCode, Arr::get($responseBody, 'error', 'Unknown Error'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_PEPIPOST_API_KEY') ? FLUENTMAIL_PEPIPOST_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
protected function getFrom()
{
return [
'name' => $this->getParam('sender_name'),
'email' => $this->getParam('sender_email')
];
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
$replyTo = reset($replyTo);
return $replyTo['email'];
}
}
protected function getRecipients()
{
$recipients = [
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy(),
];
$recipients = array_filter($recipients);
foreach ($recipients as $key => $recipient) {
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipient);
$this->attributes['formatted'][$key] = implode(', ', $array);
}
return [$recipients];
}
protected function getTo()
{
return $this->getParam('to');
}
protected function getCarbonCopy()
{
return array_map(function ($cc) {
return ['email' => $cc['email']];
}, $this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy()
{
return array_map(function ($bcc) {
return ['email' => $bcc['email']];
}, $this->getParam('headers.bcc'));
}
protected function getBody()
{
$return = [
[
'type' => 'html',
'value' => $this->getParam('message')
]
];
if ($this->phpMailer->contentType == 'multipart/alternative') {
$return[] = [
'type' => 'amp',
'value' => $this->phpMailer->AltBody
];
}
return $return;
}
protected function getAttachments()
{
$data = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'name' => $fileName,
'content' => base64_encode($file)
];
}
return $data;
}
protected function getCustomEmailHeaders()
{
return [];
}
protected function getRequestHeaders()
{
return [
'content-type' => 'application/json',
'api_key' => $this->getSetting('api_key')
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\PepiPost;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_PEPIPOST_API_KEY') || !FLUENTMAIL_PEPIPOST_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_PEPIPOST_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Postmark;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 200;
protected $url = 'https://api.postmarkapp.com/email';
public function send()
{
if ($this->preSend() && $this->phpMailer->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$body = [
'From' => $this->getParam('from'),
'To' => $this->getTo(),
'Subject' => $this->getSubject(),
'MessageStream' => $this->getSetting('message_stream', 'outbound')
];
if ($replyTo = $this->getReplyTo()) {
$body['ReplyTo'] = $replyTo;
}
if ($bcc = $this->getBlindCarbonCopy()) {
$body['Bcc'] = $bcc;
}
if ($cc = $this->getCarbonCopy()) {
$body['Cc'] = $cc;
}
$contentType = $this->getHeader('content-type');
if ($contentType == 'text/html') {
$body['HtmlBody'] = $this->getParam('message');
if ($this->getSetting('track_opens') == 'yes') {
$body['TrackOpens'] = true;
}
if ($this->getSetting('track_links') == 'yes') {
$body['TrackLinks'] = 'HtmlOnly';
}
} else if ($contentType == 'multipart/alternative') {
$body['HtmlBody'] = $this->getParam('message');
$body['TextBody'] = $this->phpMailer->AltBody;
} else {
$body['TextBody'] = $this->getParam('message');
}
if (!empty($this->getParam('attachments'))) {
$body['Attachments'] = $this->getAttachments();
}
// Add any custom headers
$customHeaders = $this->phpMailer->getCustomHeaders();
if (!empty($customHeaders)) {
foreach ($customHeaders as $header) {
$body['Headers'][] = [
'Name' => $header[0],
'Value' => $header[1]
];
}
}
// Handle apostrophes in email address From names by escaping them for the Postmark API.
$from_regex = "/(\"From\": \"[a-zA-Z\\d]+)*[\\\\]{2,}'/";
$args = array(
'headers' => $this->getRequestHeaders(),
'body' => preg_replace($from_regex, "'", wp_json_encode($body), 1),
);
$response = wp_remote_post($this->url, $args);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
$responseBody = \json_decode($responseBody, true);
if ($isOKCode) {
$returnResponse = [
'id' => Arr::get($responseBody, 'MessageID'),
'message' => Arr::get($responseBody, 'Message')
];
} else {
$returnResponse = new \WP_Error($responseCode, Arr::get($responseBody, 'Message', 'Unknown Error'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_POSTMARK_API_KEY') ? FLUENTMAIL_POSTMARK_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
$replyTo = reset($replyTo);
return $replyTo['email'];
}
}
protected function getTo()
{
return $this->getRecipients($this->getParam('to'));
}
protected function getCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.bcc'));
}
protected function getRecipients($recipients)
{
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipients);
return implode(', ', $array);
}
protected function getAttachments()
{
$data = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'Name' => $fileName,
'Content' => base64_encode($file),
'ContentType' => $this->determineMimeContentRype($attachment[0])
];
}
return $data;
}
protected function getRequestHeaders()
{
return [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Postmark-Server-Token' => $this->getSetting('api_key'),
];
}
protected function determineMimeContentRype($filename)
{
if (function_exists('mime_content_type')) {
return mime_content_type($filename);
} elseif (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $filename);
finfo_close($finfo);
return $mime_type;
} else {
return 'application/octet-stream';
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Postmark;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_POSTMARK_API_KEY') || !FLUENTMAIL_POSTMARK_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_POSTMARK_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,219 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SendGrid;
use WP_Error as WPError;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\SendGrid\ValidatorTrait;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 202;
protected $url = 'https://api.sendgrid.com/v3/mail/send';
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$body = [
'from' => $this->getFrom(),
'personalizations' => $this->getRecipients(),
'subject' => $this->getSubject(),
'content' => $this->getBody()
];
if ($replyTo = $this->getReplyTo()) {
$body['reply_to'] = $replyTo;
}
if (!empty($this->getParam('attachments'))) {
$body['attachments'] = $this->getAttachments();
}
$params = [
'body' => json_encode($body),
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
$responseBody = \json_decode($responseBody, true);
if ($isOKCode) {
$returnResponse = [
'code' => 202,
'message' => Arr::get($responseBody, 'message')
];
} else {
$returnResponse = new \WP_Error($responseCode, Arr::get($responseBody, 'errors.0.message', 'Unknown Error'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
protected function getFrom()
{
$from = [
'email' => $this->getParam('sender_email')
];
if ($name = $this->getParam('sender_name')) {
$from['name'] = $name;
}
return $from;
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
return reset($replyTo);
}
}
protected function getRecipients()
{
$recipients = [
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy(),
];
$recipients = array_filter($recipients);
foreach ($recipients as $key => $recipient) {
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipient);
$this->attributes['formatted'][$key] = implode(', ', $array);
}
return [$recipients];
}
protected function getTo()
{
return $this->getParam('to');
}
protected function getCarbonCopy()
{
return $this->getParam('headers.cc');
}
protected function getBlindCarbonCopy()
{
return $this->getParam('headers.bcc');
}
protected function getBody()
{
$contentType = $this->getParam('headers.content-type');
if ($contentType == 'multipart/alternative') {
return [
[
'value' => $this->phpMailer->AltBody,
'type' => 'text/plain'
],
[
'value' => $this->getParam('message'),
'type' => 'text/html'
]
];
}
return [
[
'value' => $this->getParam('message'),
'type' => $contentType
]
];
}
protected function getAttachments()
{
$data = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$contentId = wp_hash($attachment[0]);
$file = file_get_contents($attachment[0]);
$mimeType = mime_content_type($attachment[0]);
$filetype = str_replace(';', '', trim($mimeType));
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'type' => $filetype,
'filename' => $fileName,
'disposition' => 'attachment',
'content_id' => $contentId,
'content' => base64_encode($file)
];
}
return $data;
}
protected function getCustomEmailHeaders()
{
return [];
}
protected function getRequestHeaders()
{
return [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->getSetting('api_key')
];
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_SENDGRID_API_KEY') ? FLUENTMAIL_SENDGRID_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SendGrid;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_SENDGRID_API_KEY') || !FLUENTMAIL_SENDGRID_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_SENDGRID_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
public function checkConnection($connection)
{
return true;
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SendInBlue;
use WP_Error as WPError;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\SendInBlue\ValidatorTrait;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 201;
protected $url = 'https://api.brevo.com/v3/smtp/email';
protected $allowedAttachmentExts = [
'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'
];
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend()
{
$body = [
'sender' => $this->getFrom(),
'subject' => $this->getSubject(),
'htmlContent' => $this->getBody()
];
$contentType = $this->getParam('headers.content-type');
if ($contentType == 'multipart/alternative') {
$body['textContent'] = $this->phpMailer->AltBody;
} else if ($contentType == 'text/plain') {
$body['textContent'] = $this->getBody();
unset($body['htmlContent']);
}
if ($replyTo = $this->getReplyTo()) {
$body['replyTo'] = $replyTo;
}
$recipients = $this->setRecipients();
$body = array_merge($body, $recipients);
if (!empty($this->getParam('attachments'))) {
$body['attachment'] = $this->getAttachments();
}
$params = [
'body' => json_encode($body),
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
$responseBody = \json_decode($responseBody, true);
if ($isOKCode) {
$returnResponse = [
'messageId' => Arr::get($responseBody, 'messageId')
];
} else {
$returnResponse = new \WP_Error($responseCode, Arr::get($responseBody, 'message', __('SendInBlueError API Error', 'fluent-smtp')), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_SENDINBLUE_API_KEY') ? FLUENTMAIL_SENDINBLUE_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
protected function getFrom()
{
return [
'name' => $this->getParam('sender_name'),
'email' => $this->getParam('sender_email')
];
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
return reset($replyTo);
}
}
protected function setRecipients()
{
$recipients = [
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy()
];
$recipients = array_filter($recipients);
foreach ($recipients as $key => $recipient) {
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipient);
$this->attributes['formatted'][$key] = implode(', ', $array);
}
return $recipients;
}
protected function getTo()
{
return $this->getParam('to');
}
protected function getCarbonCopy()
{
return $this->getParam('headers.cc');
}
protected function getBlindCarbonCopy()
{
return $this->getParam('headers.bcc');
}
protected function getBody()
{
return $this->getParam('message');
}
protected function getAttachments()
{
$files = [];
foreach ($this->getParam('attachments') as $attachment) {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$ext = pathinfo($attachment[0], PATHINFO_EXTENSION);
if (in_array($ext, $this->allowedAttachmentExts, true)) {
$files[] = [
'name' => basename($attachment[0]),
'content' => base64_encode(file_get_contents($attachment[0]))
];
}
}
}
return $files;
}
protected function getCustomEmailHeaders()
{
return [];
}
protected function getRequestHeaders()
{
return [
'Api-Key' => $this->getSetting('api_key'),
'Content-Type' => 'application/json',
'Accept' => 'application/json'
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SendInBlue;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_SENDINBLUE_API_KEY') || !FLUENTMAIL_SENDINBLUE_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_SENDINBLUE_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Simulator;
use FluentMail\App\Models\Logger;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
public function send()
{
if($this->shouldBeLogged(true)) {
$atts = $this->setAttributes();
$customHeaders = $atts['custom_headers'];
$headers = $atts['headers'];
foreach ($customHeaders as $customHeader) {
$headers[$customHeader['key']] = $customHeader['value'];
}
$headers = $this->phpMailer->getCustomHeaders();
$headers['content-type'] = $this->phpMailer->ContentType;
$logData = [
'to' => maybe_serialize($this->setRecipientsArray($this->phpMailer->getToAddresses())),
'from' => maybe_serialize($this->phpMailer->From),
'subject' => $this->phpMailer->Subject,
'body' => $this->phpMailer->Body,
'attachments' => maybe_serialize($this->phpMailer->getAttachments()),
'status' => 'sent',
'response' => maybe_serialize(['status' => __('Email sending was simulated, No Email was sent originally', 'fluent-smtp')]),
'headers' => maybe_serialize($headers),
'extra' => maybe_serialize(['provider' => 'Simulator'])
];
(new Logger)->add($logData);
}
return true;
}
protected function postSend()
{
$returnResponse = [
'response' => 'OK'
];
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
$this->settings = $settings;
return $this;
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Smtp;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\Smtp\ValidatorTrait;
class Handler extends BaseHandler
{
use ValidatorTrait;
public function send()
{
if ($this->preSend()) {
if ($this->getSetting('auto_tls') == 'no') {
$this->phpMailer->SMTPAutoTLS = false;
}
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
protected function postSend()
{
try {
$this->phpMailer->isSMTP();
$this->phpMailer->Host = $this->getSetting('host');
$this->phpMailer->Port = $this->getSetting('port');
if ($this->getSetting('auth') == 'yes') {
$this->phpMailer->SMTPAuth = true;
$this->phpMailer->Username = $this->getSetting('username');
$this->phpMailer->Password = $this->getSetting('password');
}
if (($encryption = $this->getSetting('encryption')) != 'none') {
$this->phpMailer->SMTPSecure = $encryption;
}
$fromEmail = $this->phpMailer->From;
if ($this->isForcedEmail() && !fluentMailIsListedSenderEmail($fromEmail)) {
$fromEmail = $this->getSetting('sender_email');
}
if (isset($this->phpMailer->FromName)) {
$fromName = $this->phpMailer->FromName;
if (
$this->getSetting('force_from_name') == 'yes' &&
$customFrom = $this->getSetting('sender_name')
) {
$fromName = $customFrom;
}
$this->phpMailer->setFrom($fromEmail, $fromName);
} else {
$this->phpMailer->setFrom($fromEmail);
}
foreach ($this->getParam('to') as $to) {
if (isset($to['name'])) {
$this->phpMailer->addAddress($to['email'], $to['name']);
} else {
$this->phpMailer->addAddress($to['email']);
}
}
foreach ($this->getParam('headers.reply-to') as $replyTo) {
if (isset($replyTo['name'])) {
$this->phpMailer->addReplyTo($replyTo['email'], $replyTo['name']);
} else {
$this->phpMailer->addReplyTo($replyTo['email']);
}
}
foreach ($this->getParam('headers.cc') as $cc) {
if (isset($cc['name'])) {
$this->phpMailer->addCC($cc['email'], $cc['name']);
} else {
$this->phpMailer->addCC($cc['email']);
}
}
foreach ($this->getParam('headers.bcc') as $bcc) {
if (isset($bcc['name'])) {
$this->phpMailer->addBCC($bcc['email'], $bcc['name']);
} else {
$this->phpMailer->addBCC($bcc['email']);
}
}
if ($attachments = $this->getParam('attachments')) {
foreach ($attachments as $attachment) {
$this->phpMailer->addAttachment($attachment[0], $attachment[7]);
}
}
if ($this->getParam('headers.content-type') == 'text/html' || $this->getParam('headers.content-type') == 'multipart/alternative') {
$this->phpMailer->isHTML(true);
}
$this->phpMailer->Subject = $this->getSubject();
$this->phpMailer->Body = $this->getParam('message');
if ($this->getParam('headers.content-type') == 'multipart/alternative') {
$this->phpMailer->AltBody = $this->getParam('alt_body');
$this->phpMailer->ContentType = 'multipart/alternative';
}
$this->phpMailer->send();
$returnResponse = [
'response' => 'OK'
];
} catch (\Exception $e) {
$returnResponse = new \WP_Error(422, $e->getMessage(), []);
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if (Arr::get($settings, 'key_store') == 'wp_config') {
$settings['username'] = defined('FLUENTMAIL_SMTP_USERNAME') ? FLUENTMAIL_SMTP_USERNAME : '';
$settings['password'] = defined('FLUENTMAIL_SMTP_PASSWORD') ? FLUENTMAIL_SMTP_PASSWORD : '';
}
$this->settings = $settings;
return $this;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Smtp;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = Arr::get($connection, 'key_store', 'db');
if (!Arr::get($connection, 'host')) {
$errors['host']['required'] = __('SMTP host is required.', 'fluent-smtp');
}
if (!Arr::get($connection, 'port')) {
$errors['port']['required'] = __('SMTP port is required.', 'fluent-smtp');
}
if (Arr::get($connection, 'auth') == 'yes') {
if ($keyStoreType == 'wp_config') {
if (!defined('FLUENTMAIL_SMTP_USERNAME') || !FLUENTMAIL_SMTP_USERNAME) {
$errors['username']['required'] = __('Please define FLUENTMAIL_SMTP_USERNAME in wp-config.php file.', 'fluent-smtp');
}
if (!defined('FLUENTMAIL_SMTP_PASSWORD') || !FLUENTMAIL_SMTP_PASSWORD) {
$errors['password']['required'] = __('Please define FLUENTMAIL_SMTP_PASSWORD in wp-config.php file.', 'fluent-smtp');
}
} else {
if (!Arr::get($connection, 'username')) {
$errors['username']['required'] = __('SMTP username is required.', 'fluent-smtp');
}
if (!Arr::get($connection, 'password')) {
$errors['password']['required'] = __('SMTP password is required.', 'fluent-smtp');
}
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Smtp2Go;
use WP_Error as WPError;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\Smtp2Go\ValidatorTrait;
class Handler extends BaseHandler {
use ValidatorTrait;
protected $emailSentCode = 200;
protected $url = 'https://api.smtp2go.com/v3/email/send';
public function send() {
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
public function postSend() {
$body = [
'sender' => $this->getFrom(),
'to' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy(),
'subject' => $this->getSubject(),
'html_body' => $this->getBody(),
'text_body' => $this->phpMailer->AltBody
];
if ($replyTo = $this->getReplyTo()) {
$body['custom_headers'][] = [
'header' => 'Reply-To',
'value' => $replyTo
];
}
if (!empty($this->getParam('attachments'))) {
$body['attachments'] = $this->getAttachments();
}
$params = [
'body' => json_encode($body),
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
$responseBody = \json_decode($responseBody, true);
if ($isOKCode) {
$returnResponse = [
'email_id' => Arr::get($responseBody, 'data.email_id'),
'succeeded' => Arr::get($responseBody, 'data.succeeded'),
];
} else {
$returnResponse = new \WP_Error($responseCode, Arr::get($responseBody, 'data.error', 'Unknown Error'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
protected function getFrom() {
$from = $this->getParam('sender_email');
if ($name = $this->getParam('sender_name')) {
$from = $name . ' <' . $from . '>';
}
return $from;
}
protected function getReplyTo() {
if ($replyTo = $this->getParam('headers.reply-to')) {
$replyTo = reset($replyTo);
return $replyTo['email'];
}
}
protected function getRecipients($recipients) {
return array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipients);
}
protected function getTo() {
return $this->getRecipients($this->getParam('to'));
}
protected function getCarbonCopy() {
return $this->getRecipients($this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy() {
return $this->getRecipients($this->getParam('headers.bcc'));
}
protected function getBody() {
return $this->getParam('message');
}
protected function getAttachments() {
$data = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
$mimeType = mime_content_type($attachment[0]);
$filetype = str_replace(';', '', trim($mimeType));
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'mimetype' => $filetype,
'filename' => $fileName,
'fileblob' => base64_encode($file)
];
}
return $data;
}
protected function getCustomEmailHeaders() {
return [];
}
protected function getRequestHeaders() {
return [
'Content-Type' => 'application/json',
'X-Smtp2go-Api-Key' => $this->getSetting('api_key')
];
}
public function setSettings($settings) {
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_SMTP2GO_API_KEY') ? FLUENTMAIL_SMTP2GO_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\Smtp2Go;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_SMTP2GO_API_KEY') || !FLUENTMAIL_SMTP2GO_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_SMTP2GO_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
public function checkConnection($connection)
{
return true;
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SparkPost;
use WP_Error as WPError;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Core\Application;
use FluentMail\App\Services\Mailer\Manager;
use FluentMail\App\Services\Mailer\BaseHandler;
use FluentMail\App\Services\Mailer\Providers\SparkPost\ValidatorTrait;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 202;
protected $url = 'https://api.sparkpost.com/api/v1/transmissions';
public function send()
{
if ($this->preSend()) {
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []) );
}
public function postSend()
{
$body = [
'options' => [
'sandbox' => false
],
'content' => [
'from' => $this->getFrom(),
'subject' => $this->getSubject(),
'html' => $this->phpMailer->Body,
'text' => $this->phpMailer->AltBody,
'headers' => (object) []
],
'recipients' => $this->getTo(),
'cc' => $this->getCarbonCopy(),
'bcc' => $this->getBlindCarbonCopy()
];
if ($replyTo = $this->getReplyTo()) {
$body['content']['reply_to'] = $replyTo;
}
if (!empty($this->getParam('attachments'))) {
$body['content']['attachments'] = $this->getAttachments();
}
$params = [
'body' => json_encode($body),
'headers' => $this->getRequestHeaders()
];
$params = array_merge($params, $this->getDefaultParams());
$response = wp_safe_remote_post($this->url, $params);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode < 300;
$responseBody = \json_decode($responseBody, true);
if($isOKCode) {
$returnResponse = [
'response' => $responseBody
];
} else {
$returnResponse = new \WP_Error($responseCode, __('SparkPost API Error', 'fluent-smtp'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_SPARKPOST_API_KEY') ? FLUENTMAIL_SPARKPOST_API_KEY : '';
}
$this->settings = $settings;
return $this;
}
protected function getFrom()
{
$email = $this->getParam('sender_email');
if ($name = $this->getParam('sender_name')) {
$from = $name . ' <' . $email . '>';
} else {
$from = $email;
}
return $from;
}
protected function getTo()
{
$address = [];
foreach ($this->getParam('to') as $to) {
$address[] = [
'address' => [
'email' => $to['email']
]
];
}
return $address;
}
protected function getReplyTo()
{
if ($replyTo = $this->getParam('headers.reply-to')) {
$replyTo = reset($replyTo);
return $replyTo['email'];
}
}
protected function getCarbonCopy()
{
$address = [];
foreach ($this->getParam('headers.cc') as $cc) {
$address[] = [
'address' => [
'email' => $cc['email']
]
];
}
return $address;
}
protected function getBlindCarbonCopy()
{
$address = [];
foreach ($this->getParam('headers.bcc') as $bcc) {
$address[] = [
'address' => [
'email' => $bcc['email']
]
];
}
return $address;
}
protected function getAttachments()
{
$data = [];
foreach ($this->getParam('attachments') as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
$mimeType = mime_content_type($attachment[0]);
$filetype = str_replace(';', '', trim($mimeType));
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'name' => $fileName,
'type' => $filetype,
'content' => base64_encode($file)
];
}
return $data;
}
protected function getCustomEmailHeaders()
{
return [
'X-Mailer' => 'FluentMail - SparkPost'
];
}
protected function getRequestHeaders()
{
return [
'Content-Type' => 'application/json',
'Authorization' => $this->getSetting('api_key')
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\SparkPost;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_SPARKPOST_API_KEY') || !FLUENTMAIL_SPARKPOST_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_SPARKPOST_API_KEY in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,253 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\TransMail;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\BaseHandler;
class Handler extends BaseHandler
{
use ValidatorTrait;
protected $emailSentCode = 200;
protected $url = null;
public function send()
{
if ($this->preSend()) {
$this->setUrl();
return $this->postSend();
}
return $this->handleResponse(new \WP_Error(422, __('Something went wrong!', 'fluent-smtp'), []));
}
protected function setUrl()
{
return $this->url = 'https://transmail.zoho.' . $this->getSetting('domain_name') . '/v1.1/email';
}
public function postSend()
{
$body = [
'bounce_address' => $this->getSetting('bounce_address'),
'from' => json_encode([
'name' => $this->phpMailer->FromName,
'address' => $this->phpMailer->From,
]),
'to' => json_encode([
[
'email_address' => [
'address' => '',
'name' => ''
]
]
]),
'cc' => json_encode([
[
'email_address' => [
'address' => '',
'name' => ''
]
]
]),
'bcc' => json_encode([
[
'email_address' => [
'address' => '',
'name' => ''
]
]
]),
'reply_to' => json_encode([
'address' => '',
'name' => ''
]),
'subject' => $this->getSubject()
];
if ($this->phpMailer->ContentType == 'text/html') {
$body['htmlbody'] = $this->getBody();
} else if ($this->phpMailer->ContentType == 'multipart/alternative') {
$body['htmlbody'] = $this->getBody();
$body['textbody'] = $this->getParam('alt_body');
} else {
$body['textbody'] = $this->getBody();
}
$headers1 = array(
'Authorization' => 'Zoho-enczapikey ' . $this->getSetting('token'),
'Content-Type' => 'application/json',
'Accept' => 'application/json'
);
$data_string = json_encode($body);
$args = array(
'body' => $data_string,
'headers' => $headers1,
'method' => 'POST'
);
$response = wp_safe_remote_post($this->url, $args);
if (is_wp_error($response)) {
$returnResponse = new \WP_Error($response->get_error_code(), $response->get_error_message(), $response->get_error_messages());
} else {
$responseBody = wp_remote_retrieve_body($response);
$responseCode = wp_remote_retrieve_response_code($response);
$isOKCode = $responseCode == $this->emailSentCode;
if ($isOKCode) {
$responseBody = \json_decode($responseBody, true);
}
if ($isOKCode && isset($responseBody['id'])) {
$returnResponse = [
'id' => Arr::get($responseBody, 'id'),
'message' => Arr::get($responseBody, 'message')
];
} else {
$returnResponse = new \WP_Error($responseCode, __('Mailgun API Error', 'fluent-smtp'), $responseBody);
}
}
$this->response = $returnResponse;
return $this->handleResponse($this->response);
}
public function setSettings($settings)
{
if ($settings['key_store'] == 'wp_config') {
$settings['api_key'] = defined('FLUENTMAIL_MAILGUN_API_KEY') ? FLUENTMAIL_MAILGUN_API_KEY : '';
$settings['domain_name'] = defined('FLUENTMAIL_MAILGUN_DOMAIN') ? FLUENTMAIL_MAILGUN_DOMAIN : '';
}
$this->settings = $settings;
return $this;
}
protected function getFrom()
{
return $this->getParam('from');
}
protected function getReplyTo()
{
return $this->getRecipients(
$this->getParam('headers.reply-to')
);
}
protected function getTo()
{
return $this->getRecipients($this->getParam('to'));
}
protected function getCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.cc'));
}
protected function getBlindCarbonCopy()
{
return $this->getRecipients($this->getParam('headers.bcc'));
}
protected function getRecipients($recipients)
{
$array = array_map(function ($recipient) {
return isset($recipient['name'])
? $recipient['name'] . ' <' . $recipient['email'] . '>'
: $recipient['email'];
}, $recipients);
return implode(', ', $array);
}
protected function getBody()
{
return $this->getParam('message');
}
protected function getAttachments($params)
{
$data = [];
$payload = '';
$attachments = $this->attributes['attachments'];
foreach ($attachments as $attachment) {
$file = false;
try {
if (is_file($attachment[0]) && is_readable($attachment[0])) {
$fileName = basename($attachment[0]);
$file = file_get_contents($attachment[0]);
}
} catch (\Exception $e) {
$file = false;
}
if ($file === false) {
continue;
}
$data[] = [
'content' => $file,
'name' => $fileName,
];
}
if (!empty($data)) {
$boundary = hash('sha256', uniqid('', true));
foreach ($params['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";
}
}
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 . '--';
$params['body'] = $payload;
$params['headers']['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
$this->attributes['headers']['content-type'] = 'multipart/form-data';
}
return $params;
}
protected function getRequestHeaders()
{
$apiKey = $this->getSetting('api_key');
return [
'Authorization' => 'Basic ' . base64_encode('api:' . $apiKey)
];
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace FluentMail\App\Services\Mailer\Providers\TransMail;
use FluentMail\Includes\Support\Arr;
use FluentMail\App\Services\Mailer\ValidatorTrait as BaseValidatorTrait;
trait ValidatorTrait
{
use BaseValidatorTrait;
public function validateProviderInformation($connection)
{
$errors = [];
$keyStoreType = $connection['key_store'];
if($keyStoreType == 'db') {
if (! Arr::get($connection, 'api_key')) {
$errors['api_key']['required'] = __('Api key is required.', 'fluent-smtp');
}
if (! Arr::get($connection, 'domain_name')) {
$errors['domain_name']['required'] = __('Domain name is required.', 'fluent-smtp');
}
} else if($keyStoreType == 'wp_config') {
if(!defined('FLUENTMAIL_MAILGUN_API_KEY') || !FLUENTMAIL_MAILGUN_API_KEY) {
$errors['api_key']['required'] = __('Please define FLUENTMAIL_MAILGUN_API_KEY in wp-config.php file.', 'fluent-smtp');
}
if(!defined('FLUENTMAIL_MAILGUN_DOMAIN') || !FLUENTMAIL_MAILGUN_DOMAIN) {
$errors['domain_name']['required'] = __('Please define FLUENTMAIL_MAILGUN_DOMAIN in wp-config.php file.', 'fluent-smtp');
}
}
if ($errors) {
$this->throwValidationException($errors);
}
}
}

View File

@@ -0,0 +1,252 @@
<?php
return [
'connections' => [],
'mappings' => [],
'providers' => [
'smtp' => [
'key' => 'smtp',
'title' => __('SMTP server', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-smtp.svg'),
'provider' => 'Smtp',
'need_pro' => 'no',
'is_smtp' => true,
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'force_from_email' => 'yes',
'return_path' => 'yes',
'host' => '',
'port' => '',
'auth' => 'yes',
'username' => '',
'password' => '',
'auto_tls' => 'yes',
'encryption' => 'none',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/set-up-fluent-smtp-with-any-host-or-mailer/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure any SMTP with FluentSMTP.', 'fluent-smtp')
],
'ses' => [
'key' => 'ses',
'title' => __('Amazon SES', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-aws-ses.svg'),
'provider' => 'AmazonSes',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'force_from_email' => 'yes',
'return_path' => 'yes',
'access_key' => '',
'secret_key' => '',
'region' => 'us-east-1',
'key_store' => 'db'
],
'regions' => [
'us-east-1' => __('US East (N. Virginia)', 'fluent-smtp'),
'us-east-2' => __('US East (Ohio)', 'fluent-smtp'),
'us-west-1' => __('US West (N. California)', 'fluent-smtp'),
'us-west-2' => __('US West (Oregon)', 'fluent-smtp'),
'ca-central-1' => __('Canada (Central)', 'fluent-smtp'),
'eu-west-1' => __('EU (Ireland)', 'fluent-smtp'),
'eu-west-2' => __('EU (London)', 'fluent-smtp'),
'eu-west-3' => __('Europe (Paris)', 'fluent-smtp'),
'eu-central-1' => __('EU (Frankfurt)', 'fluent-smtp'),
'eu-south-1' => __('Europe (Milan)', 'fluent-smtp'),
'eu-north-1' => __('Europe (Stockholm)', 'fluent-smtp'),
'ap-south-1' => __('Asia Pacific (Mumbai)', 'fluent-smtp'),
'ap-northeast-2' => __('Asia Pacific (Seoul)', 'fluent-smtp'),
'ap-southeast-1' => __('Asia Pacific (Singapore)', 'fluent-smtp'),
'ap-southeast-2' => __('Asia Pacific (Sydney)', 'fluent-smtp'),
'ap-northeast-1' => __('Asia Pacific (Tokyo)', 'fluent-smtp'),
'ap-northeast-3' => __('Asia Pacific (Osaka)', 'fluent-smtp'),
'sa-east-1' => __('South America (São Paulo)', 'fluent-smtp'),
'me-south-1' => __('Middle East (Bahrain)', 'fluent-smtp'),
'us-gov-west-1' => __('AWS GovCloud (US)', 'fluent-smtp'),
'af-south-1' => __('Africa (Cape Town)', 'fluent-smtp'),
'cn-northwest-1' => __('China (Ningxia)', 'fluent-smtp')
],
'note' => '<a href="https://fluentsmtp.com/docs/set-up-amazon-ses-in-fluent-smtp/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Amazon SES with FluentSMTP.', 'fluent-smtp')
],
'mailgun' => [
'key' => 'mailgun',
'title' => __('Mailgun', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-mailgun.svg'),
'provider' => 'Mailgun',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'return_path' => 'yes',
'api_key' => '',
'domain_name' => '',
'key_store' => 'db',
'region' => 'us'
],
'note' => '<a href="https://fluentsmtp.com/docs/configure-mailgun-in-fluent-smtp-to-send-emails/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Mailgun with FluentSMTP.', 'fluent-smtp')
],
'sendgrid' => [
'key' => 'sendgrid',
'title' => __('SendGrid', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-sendgrid.svg'),
'provider' => 'SendGrid',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/set-up-the-sendgrid-driver-in-fluent-smtp/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure SendGrid with FluentSMTP.', 'fluent-smtp')
],
'sendinblue' => [
'key' => 'sendinblue',
'title' => __('Sendinblue', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-sendinblue.svg'),
'provider' => 'SendInBlue',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/setting-up-sendinblue-mailer-in-fluent-smtp/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Sendinblue with FluentSMTP.', 'fluent-smtp')
],
'sparkpost' => [
'key' => 'sparkpost',
'title' => __('SparkPost', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-sparkpost.svg'),
'provider' => 'SparkPost',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/configure-sparkpost-in-fluent-smtp-to-send-emails/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure SparkPost with FluentSMTP.', 'fluent-smtp')
],
'pepipost' => [
'key' => 'pepipost',
'title' => __('Netcore Email API, formerly Pepipost', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-netcore.svg'),
'provider' => 'PepiPost',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/set-up-the-pepipost-mailer-in-fluent-smtp/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Netcore (formerly Pepipost) with FluentSMTP.', 'fluent-smtp')
],
'postmark' => [
'key' => 'postmark',
'title' => __('Postmark', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-postmark.svg'),
'provider' => 'Postmark',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'track_opens' => 'no',
'track_links' => 'no',
'api_key' => '',
'message_stream' => 'outbound',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/configure-postmark-in-fluent-smtp-to-send-emails/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Postmark with FluentSMTP.', 'fluent-smtp')
],
'elasticmail' => [
'key' => 'elasticmail',
'title' => __('Elastic Email', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-elastic-email.svg'),
'provider' => 'ElasticMail',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'mail_type' => 'transactional',
'key_store' => 'db'
],
'note' => '<a href="https://fluentsmtp.com/docs/configure-elastic-email-in-fluent-smtp/" target="_blank" rel="noopener">' . __('Read the documentation', 'fluent-smtp') . '</a>' . __(' for how to configure Elastic Email with FluentSMTP.', 'fluent-smtp')
],
'smtp2go' => [
'key' => 'smtp2go',
'title' => __('SMTP2GO', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-smtp2go.svg'),
'provider' => 'Smtp2Go',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'api_key' => '',
'key_store' => 'db'
]
],
'gmail' => [
'key' => 'gmail',
'title' => __('Gmail or Google Workspace', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-gmail-google-workspace.svg'),
'provider' => 'Gmail',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'return_path' => 'yes',
'key_store' => 'db',
'client_id' => '',
'client_secret' => '',
'auth_token' => '',
'access_token' => '',
'refresh_token' => ''
],
'note' => __('Gmail/Google Workspace is not recommended for sending mass marketing emails.', 'fluent-smtp')
],
'outlook' => [
'key' => 'outlook',
'title' => __('Outlook or Office 365', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-microsoft.svg'),
'provider' => 'Outlook',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'return_path' => 'yes',
'key_store' => 'db',
'client_id' => '',
'client_secret' => '',
'auth_token' => '',
'access_token' => '',
'refresh_token' => ''
],
'note' => __('Outlook/Office365 is not recommended for sending mass marketing emails.', 'fluent-smtp')
],
'default' => [
'key' => 'default',
'title' => __('PHP mail()', 'fluent-smtp'),
'image' => fluentMailAssetUrl('images/provider-php.svg'),
'provider' => 'DefaultMail',
'options' => [
'sender_name' => '',
'sender_email' => '',
'force_from_name' => 'no',
'force_from_email' => 'yes',
'return_path' => 'yes',
'key_store' => 'db'
],
'note' => __('The Default option does not use SMTP or any Email Service Providers so it will not improve email delivery on your site.', 'fluent-smtp')
],
],
'misc' => [
'log_emails' => 'yes',
'log_saved_interval_days' => '14',
'disable_fluentcrm_logs' => 'no',
'default_connection' => '',
'fallback_connection' => ''
]
];

View File

@@ -0,0 +1,39 @@
<?php
namespace FluentMail\App\Services\Mailer;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
use FluentMail\Includes\Support\ValidationException;
trait ValidatorTrait
{
public function validateBasicInformation($connection)
{
$errors = [];
if (!($email = Arr::get($connection, 'sender_email'))) {
$errors['sender_email']['required'] = __('Sender email is required.', 'fluent-smtp');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['sender_email']['email'] = __('Invalid email address.', 'fluent-smtp');
}
if ($errors) {
$this->throwValidationException($errors);
}
}
public function validateProviderInformation($inputs)
{
// Required Method
}
public function throwValidationException($errors)
{
throw new ValidationException(
esc_html__('Unprocessable Entity', 'fluent-smtp'), 422, null, $errors // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
);
}
}

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1,425 @@
<?php
namespace FluentMail\App\Services;
use InvalidArgumentException;
use FluentMail\App\Models\Settings;
use FluentMail\Includes\Support\Arr;
class NotificationHelper
{
public static function getRemoteServerUrl()
{
if (defined('FLUENTSMTP_SERVER_REMOTE_SERVER_URL')) {
return FLUENTSMTP_SERVER_REMOTE_SERVER_URL;
}
return 'https://fluentsmtp.com/wp-json/fluentsmtp_notify/v1/';
}
public static function issueTelegramPinCode($data)
{
return self::sendTeleRequest('register-site', $data, 'POST');
}
public static function registerSlackSite($data)
{
return self::sendSlackRequest('register-site', $data, 'POST');
}
public static function getTelegramConnectionInfo($token)
{
return self::sendTeleRequest('get-site-info', [], 'GET', $token);
}
public static function sendTestTelegramMessage($token = '')
{
if (!$token) {
$settings = (new Settings())->notificationSettings();
$token = Arr::get($settings, 'telegram.token');
}
return self::sendTeleRequest('send-test', [], 'POST', $token);
}
public static function disconnectTelegram($token)
{
self::sendTeleRequest('disconnect', [], 'POST', $token);
$settings = (new Settings())->notificationSettings();
$settings['telegram'] = [
'status' => 'no',
'token' => ''
];
$settings['active_channel'] = '';
update_option('_fluent_smtp_notify_settings', $settings, false);
return true;
}
public static function getTelegramBotTokenId()
{
static $token = null;
if ($token !== null) {
return $token;
}
$settings = (new Settings())->notificationSettings();
$token = Arr::get($settings, 'telegram.token', false);
if (!$token) {
$token = false;
}
return $token;
}
public static function getSlackWebhookUrl()
{
static $url = null;
if ($url !== null) {
return $url;
}
$settings = (new Settings())->notificationSettings();
$url = Arr::get($settings, 'slack.webhook_url');
if (!$url) {
$url = false;
}
return $url;
}
public static function sendFailedNotificationTele($data)
{
wp_remote_post(self::getRemoteServerUrl() . 'telegram/send-failed-notification', array(
'timeout' => 0.01,
'blocking' => false,
'body' => $data,
'cookies' => false,
'sslverify' => false,
));
return true;
}
private static function sendTeleRequest($route, $data = [], $method = 'POST', $token = '')
{
$url = self::getRemoteServerUrl() . 'telegram/' . $route;
if ($token) {
$url .= '?site_token=' . $token;
}
if ($method == 'POST') {
$response = wp_remote_post($url, [
'body' => $data,
'sslverify' => false,
'timeout' => 50
]);
} else {
$response = wp_remote_get($url, [
'sslverify' => false,
'timeout' => 50
]);
}
if (is_wp_error($response)) {
return $response;
}
$responseCode = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
$responseData = json_decode($body, true);
if (!$responseData || empty($responseData['success']) || $responseCode !== 200) {
return new \WP_Error('invalid_data', 'Something went wrong', $responseData);
}
return $responseData;
}
private static function sendSlackRequest($route, $data = [], $method = 'POST', $token = '')
{
$url = self::getRemoteServerUrl() . 'slack/' . $route;
if ($token) {
$url .= '?site_token=' . $token;
}
if ($method == 'POST') {
$response = wp_remote_post($url, [
'body' => $data,
'sslverify' => false,
'timeout' => 50
]);
} else {
$response = wp_remote_get($url, [
'sslverify' => false,
'timeout' => 50
]);
}
if (is_wp_error($response)) {
return $response;
}
$responseCode = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
$responseData = json_decode($body, true);
if (!$responseData || empty($responseData['success']) || $responseCode !== 200) {
return new \WP_Error('invalid_data', 'Something went wrong', $responseData);
}
return $responseData;
}
public static function sendSlackMessage($message, $webhookUrl, $blocking = false)
{
if (is_array($message)) {
$body = wp_json_encode($message);
} else {
$body = wp_json_encode(array('text' => $message));
}
$args = array(
'body' => $body,
'headers' => array(
'Content-Type' => 'application/json',
),
'cookies' => null,
'timeout' => 60,
'redirection' => 5,
'blocking' => true,
'httpversion' => '1.0',
'sslverify' => false,
'data_format' => 'body',
);
if (!$blocking) {
$args['blocking'] = false;
$args['timeout'] = 0.01;
}
$response = wp_remote_post($webhookUrl, $args);
if (!$blocking) {
return true;
}
if (is_wp_error($response)) {
return $response;
}
$body = wp_remote_retrieve_body($response);
return json_decode($body, true);
}
public static function sendDiscordMessage($message, $webhookUrl, $blocking = false)
{
$body = wp_json_encode(array(
'content' => $message,
'username' => 'FluentSMTP'
));
$args = array(
'body' => $body,
'headers' => array(
'Content-Type' => 'application/json',
),
'timeout' => 60,
'redirection' => 5,
'blocking' => true,
'httpversion' => '1.0',
'sslverify' => false,
'data_format' => 'body',
);
if (!$blocking) {
$args['blocking'] = false;
$args['timeout'] = 0.01;
}
$response = wp_remote_post($webhookUrl, $args);
if (!$blocking) {
return true;
}
if (is_wp_error($response)) {
return $response;
}
$body = wp_remote_retrieve_body($response);
return json_decode($body, true);
}
public static function getActiveChannelSettings()
{
static $channel = null;
if ($channel !== null) {
return $channel;
}
$settings = (new Settings())->notificationSettings();
$activeChannel = Arr::get($settings, 'active_channel', '');
if (!$activeChannel) {
$channel = false;
return $channel;
}
$channelSettings = Arr::get($settings, $activeChannel, []);
if (!$channelSettings || empty($channelSettings['status']) || $channelSettings['status'] != 'yes') {
$channel = false;
return $channel;
}
$channel = $channelSettings;
$channel['driver'] = $activeChannel;
return $channel;
}
public static function formatSlackMessageBlock($handler, $logData = [])
{
$sendingTo = self::unserialize(Arr::get($logData, 'to'));
if (is_array($sendingTo)) {
$sendingTo = Arr::get($sendingTo, '0.email', '');
}
if (is_array($sendingTo) || !$sendingTo) {
$sendingTo = Arr::get($logData, 'to');
}
$heading = sprintf(__('[%s] Failed to send email', 'fluent-smtp'), get_bloginfo('name'));
return [
'text' => $heading,
'blocks' => [
[
'type' => 'header',
'text' => [
'type' => 'plain_text',
'text' => $heading,
"emoji" => true
]
],
[
'type' => 'section',
'fields' => [
[
'type' => "mrkdwn",
'text' => "*Website URL:*\n " . site_url()
],
[
'type' => "mrkdwn",
'text' => "*Sending Driver:*\n " . strtoupper($handler->getSetting('provider'))
],
[
'type' => "mrkdwn",
'text' => "*To Email Address:*\n " . $sendingTo
],
[
'type' => "mrkdwn",
'text' => "*Email Subject:*\n " . Arr::get($logData, 'subject')
]
]
],
[
'type' => 'section',
'text' => [
'type' => "mrkdwn",
'text' => "*Error Message:*\n ```" . self::getErrorMessageFromResponse(self::unserialize(Arr::get($logData, 'response'))) . "```"
]
],
[
'type' => 'section',
'text' => [
'type' => "mrkdwn",
'text' => "<" . admin_url('options-general.php?page=fluent-mail#/logs?per_page=10&page=1&status=failed&search=') . "|View Failed Email(s)>"
]
]
]
];
}
public static function formatDiscordMessageBlock($handler, $logData = [])
{
$sendingTo = self::unserialize(Arr::get($logData, 'to'));
if (is_array($sendingTo)) {
$sendingTo = Arr::get($sendingTo, '0.email', '');
}
if (is_array($sendingTo) || !$sendingTo) {
$sendingTo = Arr::get($logData, 'to');
}
$heading = sprintf(__('[%s] Failed to send email', 'fluent-smtp'), get_bloginfo('name'));
$content = '## ' . $heading . "\n";
$content .= __('**Website URL:** ', 'fluent-smtp') . site_url() . "\n";
$content .= __('**Sending Driver:** ', 'fluent-smtp') . strtoupper($handler->getSetting('provider')) . "\n";
$content .= __('**To Email Address:** ', 'fluent-smtp') . $sendingTo . "\n";
$content .= __('**Email Subject:** ', 'fluent-smtp') . Arr::get($logData, 'subject') . "\n";
$content .= __('**Error Message:** ```', 'fluent-smtp') . self::getErrorMessageFromResponse(self::unserialize(Arr::get($logData, 'response'))) . "```\n";
$content .= __('[View Failed Email(s)](', 'fluent-smtp') . admin_url('options-general.php?page=fluent-mail#/logs?per_page=10&page=1&status=failed&search=') . ')';
return $content;
}
public static function getErrorMessageFromResponse($response)
{
if (!$response || !is_array($response)) {
return '';
}
if (!empty($response['fallback_response']['message'])) {
$message = $response['fallback_response']['message'];
} else {
$message = Arr::get($response, 'message');
}
if (!$message) {
return '';
}
if (!is_string($message)) {
$message = json_encode($message);
}
return $message;
}
protected static function unserialize($data)
{
if (is_serialized($data)) {
if (preg_match('/(^|;)O:[0-9]+:/', $data)) {
return '';
}
return unserialize(trim($data), ['allowed_classes' => false]);
}
return $data;
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace FluentMail\App\Services;
class Reporting
{
protected static $daily = 'P1D';
protected static $weekly = 'P1W';
protected static $monthly = 'P1M';
public function getSendingStats($from, $to)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
global $wpdb;
$tableName = $wpdb->prefix.FLUENT_MAIL_DB_PREFIX.'email_logs';
$items = $wpdb->get_results($wpdb->prepare(
'SELECT COUNT(id) AS count, DATE(created_at) AS date FROM `%1$s` WHERE `created_at` BETWEEN \'%2$s\' AND \'%3$s\' GROUP BY `%4$s` ORDER BY `%5$s` ASC',
$tableName,
$from->format('Y-m-d'),
$to->format('Y-m-d'),
$groupBy,
$orderBy
));
return $this->getResult($period, $items);
}
protected function makeDatePeriod($from, $to, $interval = null)
{
$interval = $interval ?: static::$daily;
return new \DatePeriod($from, new \DateInterval($interval), $to);
}
protected function makeFromDate($from)
{
$from = $from ?: '-7 days';
return new \DateTime($from);
}
protected function makeToDate($to)
{
$to = $to ? gmdate('Y-m-d', strtotime( $to . " +1 days")) : '+1 days';
return new \DateTime($to);
}
protected function getFrequency($from, $to)
{
$numDays = $to->diff($from)->format("%a");
if ($numDays > 62 && $numDays <= 181) {
return static::$weekly;
} else if ($numDays > 181) {
return static::$monthly;
}
return static::$daily;
}
protected function getGroupAndOrder($frequency)
{
$orderBy = $groupBy = 'date';
if ($frequency == static::$weekly) {
$orderBy = $groupBy = 'week';
} else if ($frequency == static::$monthly) {
$orderBy = $groupBy = 'month';
}
return [$groupBy, $orderBy];
}
protected function prepareSelect($frequency, $dateField = 'created_at')
{
$select = [
fluentMailDb()->raw('COUNT(id) AS count'),
fluentMailDb()->raw('DATE('.$dateField.') AS date')
];
if ($frequency == static::$weekly) {
$select[] = fluentMailDb()->raw('WEEK(created_at) week');
} else if ($frequency == static::$monthly) {
$select[] = fluentMailDb()->raw('MONTH(created_at) month');
}
return $select;
}
protected function getResult($period, $items)
{
$range = $this->getDateRangeArray($period);
$formatter = 'basicFormatter';
if ($this->isMonthly($period)) {
$formatter = 'monYearFormatter';
}
foreach ($items as $item) {
$date = $this->{$formatter}($item->date);
$range[$date] = (int) $item->count;
}
return $range;
}
protected function getDateRangeArray($period)
{
$range = [];
$formatter = 'basicFormatter';
if ($this->isMonthly($period)) {
$formatter = 'monYearFormatter';
}
foreach ($period as $date) {
$date = $this->{$formatter}($date);
$range[$date] = 0;
}
return $range;
}
protected function basicFormatter($date)
{
if (is_string($date)) {
$date = new \DateTime($date);
}
return $date->format('Y-m-d');
}
protected function monYearFormatter($date)
{
if (is_string($date)) {
$date = new \DateTime($date);
}
return $date->format('M Y');
}
protected function isMonthly($period)
{
return !!$period->getDateInterval()->m;
}
}

View File

@@ -0,0 +1,361 @@
<?php
namespace FluentMail\App\Services;
//This is a auto-generated file. Please do not modify
class TransStrings
{
public static function getStrings()
{
return [
' - Simulated' => __(' - Simulated', 'fluent-smtp'),
' connection.' => __(' connection.', 'fluent-smtp'),
' in the ' => __(' in the ', 'fluent-smtp'),
' option in the Google Cloud Project.' => __(' option in the Google Cloud Project.', 'fluent-smtp'),
'(Default: US East(N.Virginia) / us - east - 1)' => __('(Default: US East(N.Virginia) / us - east - 1)', 'fluent-smtp'),
'(Optional) Share Non - Sensitive Data.It will help us to improve the integrations' => __('(Optional) Share Non - Sensitive Data.It will help us to improve the integrations', 'fluent-smtp'),
'(Re Authentication Required)' => __('(Re Authentication Required)', 'fluent-smtp'),
'*** It is very important to put ' => __('*** It is very important to put ', 'fluent-smtp'),
'A name for the connection.' => __('A name for the connection.', 'fluent-smtp'),
'API Key' => __('API Key', 'fluent-smtp'),
'About' => __('About', 'fluent-smtp'),
'Access Data: Active SMTP Connection Provider, installed plugin names, php & mysql version' => __('Access Data: Active SMTP Connection Provider, installed plugin names, php & mysql version', 'fluent-smtp'),
'Access Key' => __('Access Key', 'fluent-smtp'),
'Access Keys in Config File' => __('Access Keys in Config File', 'fluent-smtp'),
'Access Token' => __('Access Token', 'fluent-smtp'),
'Actions' => __('Actions', 'fluent-smtp'),
'Activation Pin' => __('Activation Pin', 'fluent-smtp'),
'Active Connections:' => __('Active Connections:', 'fluent-smtp'),
'Active Email Connections' => __('Active Email Connections', 'fluent-smtp'),
'Active Senders:' => __('Active Senders:', 'fluent-smtp'),
'Add' => __('Add', 'fluent-smtp'),
'Add Additional Senders' => __('Add Additional Senders', 'fluent-smtp'),
'Add Another Connection' => __('Add Another Connection', 'fluent-smtp'),
'Add Multi-Part Plain Text for HTML Emails (beta)' => __('Add Multi-Part Plain Text for HTML Emails (beta)', 'fluent-smtp'),
'After 1 Year' => __('After 1 Year', 'fluent-smtp'),
'After 14 Days' => __('After 14 Days', 'fluent-smtp'),
'After 2 Years' => __('After 2 Years', 'fluent-smtp'),
'After 30 Days' => __('After 30 Days', 'fluent-smtp'),
'After 6 Months' => __('After 6 Months', 'fluent-smtp'),
'After 60 Days' => __('After 60 Days', 'fluent-smtp'),
'After 7 Days' => __('After 7 Days', 'fluent-smtp'),
'After 90 Days' => __('After 90 Days', 'fluent-smtp'),
'Alerts' => __('Alerts', 'fluent-smtp'),
'All Statuses' => __('All Statuses', 'fluent-smtp'),
'All Time' => __('All Time', 'fluent-smtp'),
'App Callback URL(Use this URL to your APP)' => __('App Callback URL(Use this URL to your APP)', 'fluent-smtp'),
'Application Client ID' => __('Application Client ID', 'fluent-smtp'),
'Application Client Secret' => __('Application Client Secret', 'fluent-smtp'),
'Application Keys in Config File' => __('Application Keys in Config File', 'fluent-smtp'),
'Apply' => __('Apply', 'fluent-smtp'),
'Are you sure you want to disconnect Discord notifications?' => __('Are you sure you want to disconnect Discord notifications?', 'fluent-smtp'),
'Are you sure you want to disconnect Slack notifications?' => __('Are you sure you want to disconnect Slack notifications?', 'fluent-smtp'),
'Are you sure you want to disconnect Telegram notifications?' => __('Are you sure you want to disconnect Telegram notifications?', 'fluent-smtp'),
'Are you sure you want to remove this email address?' => __('Are you sure you want to remove this email address?', 'fluent-smtp'),
'Are you sure, you want to delete all the logs?' => __('Are you sure, you want to delete all the logs?', 'fluent-smtp'),
'Attachments' => __('Attachments', 'fluent-smtp'),
'Authenticate with Google & Get Access Token' => __('Authenticate with Google & Get Access Token', 'fluent-smtp'),
'Authenticate with Office365 & Get Access Token' => __('Authenticate with Office365 & Get Access Token', 'fluent-smtp'),
'Authentication' => __('Authentication', 'fluent-smtp'),
'Authorized Redirect URI' => __('Authorized Redirect URI', 'fluent-smtp'),
'Authorized Redirect URIs' => __('Authorized Redirect URIs', 'fluent-smtp'),
'Awesome! Please check your email inbox and confirm your subscription.' => __('Awesome! Please check your email inbox and confirm your subscription.', 'fluent-smtp'),
'Best WP DataTables Plugin for WordPress' => __('Best WP DataTables Plugin for WordPress', 'fluent-smtp'),
'Bulk Action' => __('Bulk Action', 'fluent-smtp'),
'By Date' => __('By Date', 'fluent-smtp'),
'Cancel' => __('Cancel', 'fluent-smtp'),
'Close' => __('Close', 'fluent-smtp'),
'Community' => __('Community', 'fluent-smtp'),
'Configure Discord Notification' => __('Configure Discord Notification', 'fluent-smtp'),
'Connect With Your Email Providers' => __('Connect With Your Email Providers', 'fluent-smtp'),
'Connection Details' => __('Connection Details', 'fluent-smtp'),
'Connection Name ' => __('Connection Name ', 'fluent-smtp'),
'Connection Provider' => __('Connection Provider', 'fluent-smtp'),
'Connection deleted Successfully.' => __('Connection deleted Successfully.', 'fluent-smtp'),
'Continue' => __('Continue', 'fluent-smtp'),
'Continue to Slack' => __('Continue to Slack', 'fluent-smtp'),
'Contributors' => __('Contributors', 'fluent-smtp'),
'Create API Key.' => __('Create API Key.', 'fluent-smtp'),
'Cumulative' => __('Cumulative', 'fluent-smtp'),
'Current verified senders:' => __('Current verified senders:', 'fluent-smtp'),
'Date-Time' => __('Date-Time', 'fluent-smtp'),
'Days' => __('Days', 'fluent-smtp'),
'Default Connection' => __('Default Connection', 'fluent-smtp'),
'Delete All Logs' => __('Delete All Logs', 'fluent-smtp'),
'Delete Logs' => __('Delete Logs', 'fluent-smtp'),
'Delete Logs:' => __('Delete Logs:', 'fluent-smtp'),
'Delete Selected' => __('Delete Selected', 'fluent-smtp'),
'Disable Logging for FluentCRM Emails' => __('Disable Logging for FluentCRM Emails', 'fluent-smtp'),
'Disconnect' => __('Disconnect', 'fluent-smtp'),
'Disconnect & Reconnect' => __('Disconnect & Reconnect', 'fluent-smtp'),
'Discord Channel Details: ' => __('Discord Channel Details: ', 'fluent-smtp'),
'Discord Notifications Enabled' => __('Discord Notifications Enabled', 'fluent-smtp'),
'Discord Webhook URL' => __('Discord Webhook URL', 'fluent-smtp'),
'Documentation' => __('Documentation', 'fluent-smtp'),
'Domain Name' => __('Domain Name', 'fluent-smtp'),
'Edit Connection' => __('Edit Connection', 'fluent-smtp'),
'ElasticEmail API Settings' => __('ElasticEmail API Settings', 'fluent-smtp'),
'Email Address' => __('Email Address', 'fluent-smtp'),
'Email Body' => __('Email Body', 'fluent-smtp'),
'Email Failed:' => __('Email Failed:', 'fluent-smtp'),
'Email Headers' => __('Email Headers', 'fluent-smtp'),
'Email Log' => __('Email Log', 'fluent-smtp'),
'Email Logs' => __('Email Logs', 'fluent-smtp'),
'Email Marketing Automation and CRM Plugin for WordPress' => __('Email Marketing Automation and CRM Plugin for WordPress', 'fluent-smtp'),
'Email Sending Error Notifications' => __('Email Sending Error Notifications', 'fluent-smtp'),
'Email Simulation' => __('Email Simulation', 'fluent-smtp'),
'Email Test' => __('Email Test', 'fluent-smtp'),
'Email Type' => __('Email Type', 'fluent-smtp'),
'Enable Email Summary' => __('Enable Email Summary', 'fluent-smtp'),
'Enable email opens tracking on postmark(For HTML Emails only).' => __('Enable email opens tracking on postmark(For HTML Emails only).', 'fluent-smtp'),
'Enable link tracking on postmark (For HTML Emails only).' => __('Enable link tracking on postmark (For HTML Emails only).', 'fluent-smtp'),
'Encryption' => __('Encryption', 'fluent-smtp'),
'End date' => __('End date', 'fluent-smtp'),
'Enter Full Screen' => __('Enter Full Screen', 'fluent-smtp'),
'Enter new email address ex: new_sender@' => __('Enter new email address ex: new_sender@', 'fluent-smtp'),
'Enter the sender email address(optional).' => __('Enter the sender email address(optional).', 'fluent-smtp'),
'Failed' => __('Failed', 'fluent-smtp'),
'Fallback Connection' => __('Fallback Connection', 'fluent-smtp'),
'Fastest Contact Form Builder Plugin for WordPress' => __('Fastest Contact Form Builder Plugin for WordPress', 'fluent-smtp'),
'Filter' => __('Filter', 'fluent-smtp'),
'FluentCRM Email Logging' => __('FluentCRM Email Logging', 'fluent-smtp'),
'FluentSMTP does not store your email notifications data.' => __('FluentSMTP does not store your email notifications data.', 'fluent-smtp'),
'FluentSMTP does not store your email notifications data. ' => __('FluentSMTP does not store your email notifications data. ', 'fluent-smtp'),
'FluentSMTP is built using the following open-source libraries and software' => __('FluentSMTP is built using the following open-source libraries and software', 'fluent-smtp'),
'Follow this link to get a Domain Name from Mailgun:' => __('Follow this link to get a Domain Name from Mailgun:', 'fluent-smtp'),
'Follow this link to get an API Key from ElasticEmail: ' => __('Follow this link to get an API Key from ElasticEmail: ', 'fluent-smtp'),
'Follow this link to get an API Key from Mailgun:' => __('Follow this link to get an API Key from Mailgun:', 'fluent-smtp'),
'Follow this link to get an API Key from SMTP2GO:' => __('Follow this link to get an API Key from SMTP2GO:', 'fluent-smtp'),
'Follow this link to get an API Key from SendGrid:' => __('Follow this link to get an API Key from SendGrid:', 'fluent-smtp'),
'Follow this link to get an API Key:' => __('Follow this link to get an API Key:', 'fluent-smtp'),
'Force From Email (Recommended Settings: Enable)' => __('Force From Email (Recommended Settings: Enable)', 'fluent-smtp'),
'Force Sender Name' => __('Force Sender Name', 'fluent-smtp'),
'Friday' => __('Friday', 'fluent-smtp'),
'From' => __('From', 'fluent-smtp'),
'From Email' => __('From Email', 'fluent-smtp'),
'From Name' => __('From Name', 'fluent-smtp'),
'Functionalities' => __('Functionalities', 'fluent-smtp'),
'General Settings' => __('General Settings', 'fluent-smtp'),
'Get API Key.' => __('Get API Key.', 'fluent-smtp'),
'Get a Domain Name.' => __('Get a Domain Name.', 'fluent-smtp'),
'Get a Private API Key.' => __('Get a Private API Key.', 'fluent-smtp'),
'Get v3 API Key.' => __('Get v3 API Key.', 'fluent-smtp'),
'Getting Started' => __('Getting Started', 'fluent-smtp'),
'Gmail / Google Workspace API Settings' => __('Gmail / Google Workspace API Settings', 'fluent-smtp'),
'Great!' => __('Great!', 'fluent-smtp'),
'How can we help you?' => __('How can we help you?', 'fluent-smtp'),
'I have sent the code' => __('I have sent the code', 'fluent-smtp'),
'If you find an issue or have a suggestion please ' => __('If you find an issue or have a suggestion please ', 'fluent-smtp'),
'If you have a minute, consider ' => __('If you have a minute, consider ', 'fluent-smtp'),
'Install Fluent Forms (Free)' => __('Install Fluent Forms (Free)', 'fluent-smtp'),
'Install FluentCRM (Free)' => __('Install FluentCRM (Free)', 'fluent-smtp'),
'Install Ninja Tables (Free)' => __('Install Ninja Tables (Free)', 'fluent-smtp'),
'Installing... Please wait' => __('Installing... Please wait', 'fluent-smtp'),
'Join FluentCRM Facebook Community' => __('Join FluentCRM Facebook Community', 'fluent-smtp'),
'Join FluentForms Facebook Community' => __('Join FluentForms Facebook Community', 'fluent-smtp'),
'Last 3 months' => __('Last 3 months', 'fluent-smtp'),
'Last 30 Days' => __('Last 30 Days', 'fluent-smtp'),
'Last 7 Days' => __('Last 7 Days', 'fluent-smtp'),
'Last month' => __('Last month', 'fluent-smtp'),
'Last step!' => __('Last step!', 'fluent-smtp'),
'Last week' => __('Last week', 'fluent-smtp'),
'Less' => __('Less', 'fluent-smtp'),
'Log All Emails for Reporting' => __('Log All Emails for Reporting', 'fluent-smtp'),
'Log Emails' => __('Log Emails', 'fluent-smtp'),
'Mailer' => __('Mailer', 'fluent-smtp'),
'Mailgun API Settings' => __('Mailgun API Settings', 'fluent-smtp'),
'Manage Additional Senders' => __('Manage Additional Senders', 'fluent-smtp'),
'Marketing' => __('Marketing', 'fluent-smtp'),
'Meet ' => __('Meet ', 'fluent-smtp'),
'Message Stream' => __('Message Stream', 'fluent-smtp'),
'Monday' => __('Monday', 'fluent-smtp'),
'More' => __('More', 'fluent-smtp'),
'Next' => __('Next', 'fluent-smtp'),
'None' => __('None', 'fluent-smtp'),
'Notification Days' => __('Notification Days', 'fluent-smtp'),
'Notification Email Addresses' => __('Notification Email Addresses', 'fluent-smtp'),
'Off' => __('Off', 'fluent-smtp'),
'On' => __('On', 'fluent-smtp'),
'Oops!' => __('Oops!', 'fluent-smtp'),
'Outlook / Office365 API Settings' => __('Outlook / Office365 API Settings', 'fluent-smtp'),
'Pepipost API Settings' => __('Pepipost API Settings', 'fluent-smtp'),
'Pin copied to clipboard' => __('Pin copied to clipboard', 'fluent-smtp'),
'Please ' => __('Please ', 'fluent-smtp'),
'Please Provide an email' => __('Please Provide an email', 'fluent-smtp'),
'Please add another connection to use fallback feature' => __('Please add another connection to use fallback feature', 'fluent-smtp'),
'Please authenticate with Google to get ' => __('Please authenticate with Google to get ', 'fluent-smtp'),
'Please authenticate with Office365 to get ' => __('Please authenticate with Office365 to get ', 'fluent-smtp'),
'Please enter a valid email address' => __('Please enter a valid email address', 'fluent-smtp'),
'Please send test email to confirm if the connection is working or not.' => __('Please send test email to confirm if the connection is working or not.', 'fluent-smtp'),
'Postmark API Settings' => __('Postmark API Settings', 'fluent-smtp'),
'Prev' => __('Prev', 'fluent-smtp'),
'Private API Key' => __('Private API Key', 'fluent-smtp'),
'Provider' => __('Provider', 'fluent-smtp'),
'Quick Overview' => __('Quick Overview', 'fluent-smtp'),
'Read the documentation' => __('Read the documentation', 'fluent-smtp'),
'Real-Time Email Failure Notifications' => __('Real-Time Email Failure Notifications', 'fluent-smtp'),
'Receiver\'s Telegram Username: ' => __('Receiver\'s Telegram Username: ', 'fluent-smtp'),
'Recommended Plugin' => __('Recommended Plugin', 'fluent-smtp'),
'Region ' => __('Region ', 'fluent-smtp'),
'Remove' => __('Remove', 'fluent-smtp'),
'Resend' => __('Resend', 'fluent-smtp'),
'Resend Selected Emails' => __('Resend Selected Emails', 'fluent-smtp'),
'Resent Count' => __('Resent Count', 'fluent-smtp'),
'Retry' => __('Retry', 'fluent-smtp'),
'Run Another Test Email' => __('Run Another Test Email', 'fluent-smtp'),
'SMTP Host' => __('SMTP Host', 'fluent-smtp'),
'SMTP Password' => __('SMTP Password', 'fluent-smtp'),
'SMTP Port' => __('SMTP Port', 'fluent-smtp'),
'SMTP Username' => __('SMTP Username', 'fluent-smtp'),
'SMTP2GO API Settings' => __('SMTP2GO API Settings', 'fluent-smtp'),
'SSL' => __('SSL', 'fluent-smtp'),
'Saturday' => __('Saturday', 'fluent-smtp'),
'Save Connection Settings' => __('Save Connection Settings', 'fluent-smtp'),
'Save Email Logs:' => __('Save Email Logs:', 'fluent-smtp'),
'Save Settings' => __('Save Settings', 'fluent-smtp'),
'Search Results for' => __('Search Results for', 'fluent-smtp'),
'Search Type and Enter...' => __('Search Type and Enter...', 'fluent-smtp'),
'Secret Key' => __('Secret Key', 'fluent-smtp'),
'Select' => __('Select', 'fluent-smtp'),
'Select Email or Type' => __('Select Email or Type', 'fluent-smtp'),
'Select Provider' => __('Select Provider', 'fluent-smtp'),
'Select Region' => __('Select Region', 'fluent-smtp'),
'Select date and time' => __('Select date and time', 'fluent-smtp'),
'Send Test Email' => __('Send Test Email', 'fluent-smtp'),
'Send Test Message' => __('Send Test Message', 'fluent-smtp'),
'Send To' => __('Send To', 'fluent-smtp'),
'Send this email in HTML or in plain text format.' => __('Send this email in HTML or in plain text format.', 'fluent-smtp'),
'SendGrid API Settings' => __('SendGrid API Settings', 'fluent-smtp'),
'Sender Email ' => __('Sender Email ', 'fluent-smtp'),
'Sender Email Address' => __('Sender Email Address', 'fluent-smtp'),
'Sender Name' => __('Sender Name', 'fluent-smtp'),
'Sender Settings' => __('Sender Settings', 'fluent-smtp'),
'Sendinblue API Settings' => __('Sendinblue API Settings', 'fluent-smtp'),
'Sending Stats' => __('Sending Stats', 'fluent-smtp'),
'Sending by time of day' => __('Sending by time of day', 'fluent-smtp'),
'Server Response' => __('Server Response', 'fluent-smtp'),
'Set the return-path to match the From Email' => __('Set the return-path to match the From Email', 'fluent-smtp'),
'Settings' => __('Settings', 'fluent-smtp'),
'Slack Channel Details: ' => __('Slack Channel Details: ', 'fluent-smtp'),
'Slack Notifications Enabled' => __('Slack Notifications Enabled', 'fluent-smtp'),
'Sorry! No docs found' => __('Sorry! No docs found', 'fluent-smtp'),
'SparkPost API Settings' => __('SparkPost API Settings', 'fluent-smtp'),
'Start date' => __('Start date', 'fluent-smtp'),
'Status' => __('Status', 'fluent-smtp'),
'Status:' => __('Status:', 'fluent-smtp'),
'Store API Keys in Config File' => __('Store API Keys in Config File', 'fluent-smtp'),
'Store API Keys in DB' => __('Store API Keys in DB', 'fluent-smtp'),
'Store Access Keys in DB' => __('Store Access Keys in DB', 'fluent-smtp'),
'Store Application Keys in DB' => __('Store Application Keys in DB', 'fluent-smtp'),
'Subject' => __('Subject', 'fluent-smtp'),
'Subscribe To Updates' => __('Subscribe To Updates', 'fluent-smtp'),
'Successful' => __('Successful', 'fluent-smtp'),
'Summary Email' => __('Summary Email', 'fluent-smtp'),
'Sunday' => __('Sunday', 'fluent-smtp'),
'TLS' => __('TLS', 'fluent-smtp'),
'Telegram Connection Status: ' => __('Telegram Connection Status: ', 'fluent-smtp'),
'Telegram Notifications Enable' => __('Telegram Notifications Enable', 'fluent-smtp'),
'Test Email Has been successfully sent' => __('Test Email Has been successfully sent', 'fluent-smtp'),
'The email address already exists in the list' => __('The email address already exists in the list', 'fluent-smtp'),
'The email address must match the domain: ' => __('The email address must match the domain: ', 'fluent-smtp'),
'The email address which emails are sent from.' => __('The email address which emails are sent from.', 'fluent-smtp'),
'The name which emails are sent from.' => __('The name which emails are sent from.', 'fluent-smtp'),
'Thursday' => __('Thursday', 'fluent-smtp'),
'To' => __('To', 'fluent-smtp'),
'To send emails you will need only a Mail Send access level for this API key.' => __('To send emails you will need only a Mail Send access level for this API key.', 'fluent-smtp'),
'Today' => __('Today', 'fluent-smtp'),
'Total Email Sent (Logged):' => __('Total Email Sent (Logged):', 'fluent-smtp'),
'Track Opens' => __('Track Opens', 'fluent-smtp'),
'Transactional' => __('Transactional', 'fluent-smtp'),
'Try Again' => __('Try Again', 'fluent-smtp'),
'Tuesday' => __('Tuesday', 'fluent-smtp'),
'Turn On' => __('Turn On', 'fluent-smtp'),
'Type & press enter...' => __('Type & press enter...', 'fluent-smtp'),
'Use Auto TLS' => __('Use Auto TLS', 'fluent-smtp'),
'Validating Data. Please wait...' => __('Validating Data. Please wait...', 'fluent-smtp'),
'Wednesday' => __('Wednesday', 'fluent-smtp'),
'Write a review (really appreciated 😊)' => __('Write a review (really appreciated 😊)', 'fluent-smtp'),
'Yes, Disconnect' => __('Yes, Disconnect', 'fluent-smtp'),
'You may add additional sending emails in this' => __('You may add additional sending emails in this', 'fluent-smtp'),
'Your Discord Channel Name (For Internal Use)' => __('Your Discord Channel Name (For Internal Use)', 'fluent-smtp'),
'Your Discord Channel Webhook URL' => __('Your Discord Channel Webhook URL', 'fluent-smtp'),
'Your Email' => __('Your Email', 'fluent-smtp'),
'Your Email Address' => __('Your Email Address', 'fluent-smtp'),
'Your Name' => __('Your Name', 'fluent-smtp'),
'Your SMTP Username' => __('Your SMTP Username', 'fluent-smtp'),
'__ABOUT_BY' => __('FluentSMTP is free and will be always free. This is our pledge to WordPress community from WPManageNinja LLC.', 'fluent-smtp'),
'__ABOUT_COMMUNITY' => __('FluentSMTP is powered by community. We listen to our community users and build products that add value to businesses and save time.', 'fluent-smtp'),
'__ABOUT_INTRO' => __('is a free and open-source WordPress Plugin. Our mission is to provide the ultimate email delivery solution with your favorite Email sending service. FluentSMTP is built for performance and speed.', 'fluent-smtp'),
'__ABOUT_JOIN' => __('Join our communities and participate in great conversations.', 'fluent-smtp'),
'__ABOUT_POWERED' => __('FluentSMTP is powered by its users like you. Feel free to contribute on Github. Thanks to all of our contributors.', 'fluent-smtp'),
'__ANOTHER_CONNECTION_NOTICE' => __('Another connection with same email address exists. This connection will replace that connection', 'fluent-smtp'),
'__DEFAULT_CONNECTION_CONFLICT' => __('Default and Fallback connection cannot be the same. Please select different connections.', 'fluent-smtp'),
'__DEFAULT_MAIL_WARNING' => __('The Default(none) option does not use SMTP and will not improve email delivery on your site.', 'fluent-smtp'),
'__DEFAULT_MAIl_WARNING' => __('__DEFAULT_MAIl_WARNING', 'fluent-smtp'),
'__DISCORD_INTRO' => __('Get real-time notification on your Discord Channel on any email sending failure. Configure notification with Discord to start getting real time notifications.', 'fluent-smtp'),
'__DISCORD_NOTIFICATION_ENABLED' => __('Your FluentSMTP plugin is currently integrated with your Discord Channel. Receive timely notifications on Discord for any email sending issues from your website. This ongoing connection ensures you\'re always informed about your email delivery status.', 'fluent-smtp'),
'__EMAIL_LOGGING_OFF' => __('Email Logging is currently turned off. Only Failed and resent emails will be shown here', 'fluent-smtp'),
'__EMAIL_SUMMARY_INTRO' => __('Email summary is useful for getting weekly or daily emails about all the email sending stats for this site.', 'fluent-smtp'),
'__Email_Simulation_Label' => __('Disable sending all emails. If you enable this, no email will be sent.', 'fluent-smtp'),
'__Email_Simulation_Yes' => __('No Emails will be sent from your WordPress.', 'fluent-smtp'),
'__Email_TEXT_PART_Label' => __('Enable Multi-Part Plain Text version of your HTML Emails. This feature is in beta', 'fluent-smtp'),
'__FC_DESC' => __('is the best and complete feature-rich Email Marketing & CRM solution. It is also the simplest and fastest CRM and Marketing Plugin on WordPress. Manage your customer relationships, build your email lists, send email campaigns, build funnels, and make more profit and increase your conversion rates. (Yes, Its Free!)', 'fluent-smtp'),
'__FF_DESC' => __('is the ultimate user-friendly, fast, customizable drag-and-drop WordPress Contact Form Plugin that offers you all the premium features, plus many more completely unique additional features.', 'fluent-smtp'),
'__FORCE_SENDER_NAME_TIP' => __('When checked, the From Name setting above will be used for all emails, ignoring values set by other plugins.', 'fluent-smtp'),
'__GCP_API_INST' => sprintf(__('Please %s to create API keys on the Google Cloud Platform.', 'fluent-smtp'), '<a target="_blank" rel="nofollow" href="https://fluentsmtp.com/docs/connect-gmail-or-google-workspace-emails-with-fluentsmtp/">' . __('check the documentation', 'fluent-smtp') . '</a>'),
'__GCP_INTRO' => sprintf(__('Google API version has been upgraded. Please %s.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://fluentsmtp.com/docs/connect-gmail-or-google-workspace-emails-with-fluentsmtp/">' . __('read the doc and upgrade your API connection', 'fluent-smtp') . '</a>'),
'__GIT_CONTRIBUTE' => sprintf(__('If you are a developer and would like to contribute to the project, please %s', 'fluent-smtp'), '<a target="_blank" rel="nofollow" href="https://github.com/WPManageNinja/fluent-smtp/">' . __('contribute on GitHub', 'fluent-smtp') . '</a>'),
'__GMAIL_CODE_INSTRUCTION' => __('Simply copy the following snippet and replace the stars with the corresponding credential. Then simply paste to wp-config.php file of your WordPress installation', 'fluent-smtp'),
'__GMAIL_SUCCESS' => __('Your Gmail / Google Workspace Authentication has been enabled. No further action is needed. If you want to re-authenticate,', 'fluent-smtp'),
'__MAILGUN_REGION' => sprintf(__('If you are operating under EU laws, you may be required to use the EU region. %s.', 'fluent-smtp'), '<a target="_blank" href="https://www.mailgun.com/regions">' . __('More information on Mailgun.com', 'fluent-smtp') . '</a>'),
'__MAILGUN_URL_TIP' => __('Define which endpoint you want to use for sending messages.', 'fluent-smtp'),
'__NT_DESC' => __('Looking for a WordPress table plugin for your website? Then youre in the right place.', 'fluent-smtp'),
'__NT_DESC_EXT' => __('the best WP table plugin that comes with all the solutions to the problems you face while creating tables on your posts/pages.', 'fluent-smtp'),
'__PASSWORD_ENCRYPT_HELP' => __('This input will be securely encrypted using WP SALTS as encryption keys before saving.', 'fluent-smtp'),
'__PASSWORD_ENCRYPT_TIP' => __('If you change your WordPress SALT Keys, this credential will become invalid. Please update this credential whenever the WP SALTS are modified.', 'fluent-smtp'),
'__PEPIPOST_HELP' => __('Follow this link to get an API Key from Pepipost (Click Show button on Settings Page):', 'fluent-smtp'),
'__POSTMARK_CLICK' => __('If you enable this then link tracking header will be added to the email for Postmark.', 'fluent-smtp'),
'__POSTMARK_HELP' => __('Follow this link to get an API Key from Postmark (Your API key is in the API Tokens tab of your):', 'fluent-smtp'),
'__POSTMARK_OPEN' => __('If you enable this then open tracking header will be added to the email for Postmark.', 'fluent-smtp'),
'__REAL_NOTIFCATION_DESC' => __('Get real-time notification on your favorite messaging channel on any email sending failure. Configure any of the following channel to start getting real time notifications.', 'fluent-smtp'),
'__RETURN_PATH_ALERT' => __('Return Path indicates where non-delivery receipts - or bounce messages - are to be sent. If unchecked, bounce messages may be lost. With this enabled, you\'ll be emailed using "From Email" if any messages bounce as a result of issues with the recipients email.', 'fluent-smtp'),
'__RETURN_PATH_TOOLTIP' => sprintf(__('Return Path indicates where non-delivery receipts—or bounce messages—%1$s are to be sent. If unchecked, bounce messages may be lost. With this enabled, %2$s you\'ll be emailed using "From Email" if any messages bounce due to issues with the recipient\'s email.', 'fluent-smtp'), '<br />', '<br />'),
'__SLACK_INTRO' => __('Get real-time notification on your Slack Channel on any email sending failure. Configure notification with Slack Bot to start getting real time notifications.', 'fluent-smtp'),
'__SLACK_NOTIFICATION_ENABLED' => __('Your FluentSMTP plugin is currently integrated with your Slack Channel. Receive timely notifications on Slack for any email sending issues from your website. This ongoing connection ensures you\'re always informed about your email delivery status.', 'fluent-smtp'),
'__SLACK_TERMS' => sprintf(__('I agree to the %1$s of this Slack integration.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://fluentsmtp.com/terms-and-conditions/">' . __('terms and conditions', 'fluent-smtp') . '</a>'),
'__SMTP_CRED_HELP' => __('(If you need to provide your SMTP server\'s credentials (username and password) enable the authentication, in most cases this is required.)', 'fluent-smtp'),
'__SUBSCRIBE_INTRO' => __('Subscribe with your email to know about this plugin updates, releases and useful tips.', 'fluent-smtp'),
'__SUPPORT_INTRO' => sprintf(__('Please view the %1$s first. If you still can\'t find the answer, %2$s and we will be happy to assist you with any problems.', 'fluent-smtp'), '<a href="https://fluentsmtp.com/docs" target="_blank" rel="noopener">' . __('documentation', 'fluent-smtp') . '</a>', '<a href="https://github.com/WPManageNinja/fluent-smtp" target="_blank" rel="noopener">' . __('open a GitHub issue', 'fluent-smtp') . '</a>'),
'__TELEGRAM_NOTIFICATION_ENABLED' => sprintf(__('Your FluentSMTP plugin is currently integrated with Telegram. Receive timely notifications from %s on Telegram for any email sending issues from your website. This ongoing connection ensures you\'re always informed about your email delivery status.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://t.me/fluentsmtp_bot">@fluentsmtp_bot</a>'),
'__TELE_INTRO' => sprintf(__('Get real-time notifications on your %1$s for any email sending failures. Configure notifications with FluentSMTP\'s official %2$s to start receiving real-time alerts.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://telegram.org/">Telegram Messenger</a>', '<a target="_blank" rel="noopener" href="https://t.me/fluentsmtp_bot">telegram bot</a>'),
'__TELE_LAST_STEP' => sprintf(__('Please find %s on Telegram and send the following text to activate this connection.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://t.me/fluentsmtp_bot"><span class="tele_bot">@fluentsmtp_bot</span></a>'),
'__TELE_RESPONSE_ERROR' => __('We could not fetch the Telegram notification status. Here is the server response: ', 'fluent-smtp'),
'__TELE_TERMS' => sprintf(__('I agree to the %s of this Telegram integration.', 'fluent-smtp'), '<a target="_blank" rel="noopener" href="https://fluentsmtp.com/terms-and-conditions/">' . __('terms and conditions', 'fluent-smtp') . '</a>'),
'__TEST_EMAIL_INST' => __('Enter email address where test email will be sent (By default, logged in user email will be used if email address is not provided).', 'fluent-smtp'),
'__TLS_HELP' => __('(By default, the TLS encryption would be used if the server supports it. On some servers, it could be a problem and may need to be disabled.)', 'fluent-smtp'),
'__WP_CONFIG_INSTRUCTION' => __('Simply copy the following snippet and replace the stars with the corresponding credential. Then simply paste to wp-config.php file of your WordPress installation', 'fluent-smtp'),
'__default_connection_popover' => __('Select which connection will be used for sending transactional emails from your WordPress. If you use multiple connection then email will be routed based on source from email address', 'fluent-smtp'),
'__fallback_connection_popover' => __('Fallback Connection will be used if an email is failed to send in one connection. Please select a different connection than the default connection', 'fluent-smtp'),
'__from_email_tooltip' => __('If checked, the From Email setting above will be used for all emails (It will check if the from email is listed to available connections).', 'fluent-smtp'),
'__routing_info' => __('Your emails will be routed automatically based on From email address. No additional configuration is required.', 'fluent-smtp'),
'__wizard_instruction' => __('Please configure your first email service provider connection', 'fluent-smtp'),
'__wizard_sub' => __('Thank you for installing FluentSMTP - The ultimate SMTP & Email Service Connection Plugin for WordPress', 'fluent-smtp'),
'__wizard_title' => __('Welcome to FluentSMTP', 'fluent-smtp'),
'activate ' => __('activate ', 'fluent-smtp'),
'cancel' => __('cancel', 'fluent-smtp'),
'change' => __('change', 'fluent-smtp'),
'check the documentation first to create API keys at Microsoft' => __('check the documentation first to create API keys at Microsoft', 'fluent-smtp'),
'click here' => __('click here', 'fluent-smtp'),
'confirm' => __('confirm', 'fluent-smtp'),
'copy' => __('copy', 'fluent-smtp'),
'delete_logs_info' => __('delete_logs_info', 'fluent-smtp'),
'force_sender_tooltip' => __('force_sender_tooltip', 'fluent-smtp'),
'open an issue on GitHub' => __('open an issue on GitHub', 'fluent-smtp'),
'provider for the connection.' => __('provider for the connection.', 'fluent-smtp'),
'read the documentation here' => __('read the documentation here', 'fluent-smtp'),
'save_connection_error_1' => __('Please select your email service provider', 'fluent-smtp'),
'save_connection_error_2' => __('Credential Verification Failed. Please check your inputs', 'fluent-smtp'),
'write a review for FluentSMTP' => __('write a review for FluentSMTP', 'fluent-smtp')
];
}
}

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1,216 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title><?php esc_html_e('FluentSMTP Email Health Report', 'fluent-smtp') ?></title>
<style type="text/css">@media only screen and (max-width: 599px) {
table.body .container {
width: 95% !important;
}
.header {
padding: 15px 15px 12px 15px !important;
}
.header img {
width: 200px !important;
height: auto !important;
}
.content, .aside {
padding: 30px 40px 20px 40px !important;
}
}</style>
<style type="text/css">
body {
font-family: 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
background: #eeeeee;
}
/* GENERAL STYLE RESETS */
body, #bodyTable {
height: 100% !important;
width: 100% !important;
margin: 0;
padding: 0;
}
img, a img {
border: 0;
outline: none;
text-decoration: none;
}
table, td {
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
border-collapse: collapse;
}
img {
-ms-interpolation-mode: bicubic;
}
body, table, td, p, a, li, blockquote {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
</style>
</head>
<body
style="height: 100% !important; width: 100% !important; min-width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; background-color: #f1f1f1; text-align: center;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="body"
style="border-collapse: collapse; border-spacing: 0; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; height: 100% !important; width: 100% !important; min-width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; background-color: #f1f1f1; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%;">
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="center" valign="top" class="body-inner fluent-smtp"
style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: center;">
<!-- Container -->
<table border="0" cellpadding="0" cellspacing="0" class="container"
style="border-collapse: collapse; border-spacing: 0; padding: 0; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; width: 600px; margin: 0 auto 30px auto; Margin: 0 auto 30px auto; text-align: inherit;">
<!-- Header -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td valign="middle" class="header"
style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: white; border: 1px solid #7D3492; background: #7D3492; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: left; padding: 10px 20px 10px 20px;">
<table width="100%">
<tr>
<td>
<h3 style="margin: 5px 0; color: white;"><?php esc_html_e('Email Sending Health', 'fluent-smtp') ?></h3>
<p style="margin: 0;color: white;font-size: 12px;"><?php echo esc_html($date_range); ?></p>
</td>
<td style="text-align: right;">
<img src="<?php echo esc_url(fluentMailMix('images/fluentsmtp-white.png')); ?>"
width="164px" alt="Fluent SMTP Logo"
style="outline: none; text-decoration: none; max-width: 100%; clear: both; -ms-interpolation-mode: bicubic; display: inline-block !important; width: 164px;">
</td>
</tr>
</table>
</td>
</tr>
<!-- Content -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="left" valign="top" class="content"
style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; background-color: #ffffff; padding: 20px 20px 30px 20px; border-right: 1px solid #ddd; border-left: 1px solid #ddd;">
<table width="100%">
<tr>
<td>
<h3 style="font-size: 18px; font-weight: normal; margin: 0;"><?php echo wp_kses_post($sent['title']); ?></h3>
<?php if ($sent['subject_items']): ?>
<p style="margin: 4px 0 0 0;font-size: 12px;"><?php echo wp_kses_post($sent['subtitle']); ?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<td style="padding: 10px 0">
<?php if ($sent['subject_items']): ?>
<table width="100%"
style="border: 1px solid #ccd0d4; border-collapse: collapse;">
<tr style="background-color: #f9f9f9;">
<th style="padding: 8px 10px;"
align="left"><?php esc_html_e('Subject', 'fluent-smtp'); ?></th>
<th style="width:85px; padding: 8px 10px;"><?php esc_html_e('Emails Sent', 'fluent-smtp'); ?></th>
</tr>
<?php foreach ($sent['subject_items'] as $index => $item): ?>
<tr <?php if($index % 2 == 1) { echo 'style="background-color: #f9f9f9;"'; }?>>
<td style="padding: 8px 10px;"><?php echo wp_kses_post($item['subject']); ?></td>
<td style="padding: 8px 10px;"
align="center"><?php echo esc_html(number_format_i18n($item['emails_sent'])); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php else: ?>
<table width="100%" cellpadding="20">
<tr>
<td bgcolor="#eeeeee"
style="background-color: #eeeeee;border-radius: 5px;"
align="center">
<h3 style="font-size: 24px; line-height: 30px; font-weight: normal;"><?php esc_html_e('Looks like no email has been sent to the time period', 'fluent-smtp'); ?></h3>
<p>
<?php esc_html_e('If this is unusual you should probably check if your site is broken.', 'fluent-smtp');
?>
</p>
</td>
</tr>
</table>
<?php endif; ?>
</td>
</tr>
</table>
</td>
</tr>
<!-- Fails -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="left" valign="top" class="content"
style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; background-color: #ffffff; padding: 20px 20px 30px 20px; border-right: 1px solid #ddd; border-bottom: 1px solid #ddd; border-left: 1px solid #ddd;">
<table width="100%">
<tr>
<td>
<h3 style="font-size: 18px; font-weight: normal; margin: 0;"><?php echo wp_kses_post($fail['title']); ?></h3>
<?php if ($fail['subject_items']): ?>
<p style="margin: 4px 0 0 0;"><?php echo wp_kses_post($fail['subtitle']); ?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<td style="padding: 10px 0">
<?php if ($fail['subject_items']): ?>
<table width="100%" style="border: 1px solid #ccd0d4; border-collapse: collapse;">
<tr style="background-color: #f9f9f9;">
<th style="padding: 8px 10px;" align="left"><?php esc_html_e('Subject', 'fluent-smtp'); ?></th>
<th style="width:85px; padding: 8px 10px;"><?php esc_html_e('Failed Count', 'fluent-smtp'); ?></th>
</tr>
<?php foreach ($fail['subject_items'] as $index => $item): ?>
<tr <?php if($index % 2 == 1) { echo 'style="background-color: #f9f9f9;"'; }?>>
<td style="padding: 8px 10px;"><?php echo wp_kses_post($item['subject']); ?></td>
<td style="padding: 8px 10px;"
align="center"><?php echo esc_html(number_format_i18n($item['emails_sent'])); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php else: ?>
<table width="100%" cellpadding="20">
<tr>
<td bgcolor="#eeeeee"
style="background-color: #eeeeee;border-radius: 5px;" align="center">
<h3 style="font-size: 24px; line-height: 30px; font-weight: normal;"><?php esc_html_e('Awesome! no failures! 🎉', 'fluent-smtp'); ?></h3>
<p>
<?php esc_html_e('Your email sending health is perfect', 'fluent-smtp');
?>
</p>
</td>
</tr>
</table>
<?php endif; ?>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td valign="middle" class="header" style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: white; border: 1px solid #7D3492; background: #7D3492; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: left; padding: 10px 20px 10px 20px;">
<table width="100%">
<tr>
<td>
<p style="font-size: 10px; line-height: 12px; color: white;"><?php esc_html_e('You received this email because the Email Sending Health Report is enabled in your FluentSMTP settings. Simply turn it off to stop these emails at ', 'fluent-smtp') ?><?php echo esc_html($domain_name); ?>.</p>
</td>
<td style="text-align: right;width: 100px; padding-left: 15px;">
<img src="<?php echo esc_url(fluentMailMix('images/fluentsmtp-white.png')); ?>"
width="164px" alt="Fluent SMTP Logo"
style="outline: none; text-decoration: none; max-width: 100%; clear: both; -ms-interpolation-mode: bicubic; display: inline-block !important; width: 164px;">
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title><?php esc_html_e('FluentSMTP Test Email', 'fluent-smtp') ?></title>
<style type="text/css">@media only screen and (max-width: 599px) {table.body .container {width: 95% !important;}.header {padding: 15px 15px 12px 15px !important;}.header img {width: 200px !important;height: auto !important;}.content, .aside {padding: 30px 40px 20px 40px !important;}}</style>
</head>
<body style="height: 100% !important; width: 100% !important; min-width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; background-color: #f1f1f1; text-align: center;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="body" style="border-collapse: collapse; border-spacing: 0; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; height: 100% !important; width: 100% !important; min-width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-font-smoothing: antialiased !important; -moz-osx-font-smoothing: grayscale !important; background-color: #f1f1f1; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%;">
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="center" valign="top" class="body-inner fluent-smtp" style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: center;">
<!-- Container -->
<table border="0" cellpadding="0" cellspacing="0" class="container" style="border-collapse: collapse; border-spacing: 0; padding: 0; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; width: 600px; margin: 0 auto 30px auto; Margin: 0 auto 30px auto; text-align: inherit;">
<!-- Header -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="center" valign="middle" class="header" style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: center; padding: 30px 30px 22px 30px;">
<img src="<?php echo esc_url( fluentMailMix('images/fluentsmtp.png')); ?>" width="164px" alt="Fluent SMTP Logo" style="outline: none; text-decoration: none; max-width: 100%; clear: both; -ms-interpolation-mode: bicubic; display: inline-block !important; width: 164px;">
</td>
</tr>
<!-- Content -->
<tr style="padding: 0; vertical-align: top; text-align: left;">
<td align="left" valign="top" class="content" style="word-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; margin: 0; Margin: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; background-color: #ffffff; padding: 60px 75px 45px 75px; border-right: 1px solid #ddd; border-bottom: 1px solid #ddd; border-left: 1px solid #ddd; border-top: 4px solid #c716c1;">
<div class="success" style="text-align: center;">
<p class="text-extra-large text-center congrats" style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; mso-line-height-rule: exactly; line-height: 140%; font-size: 20px; text-align: center; margin: 0 0 20px 0; Margin: 0 0 20px 0;">
<?php esc_html_e('Congrats, test email was sent successfully!', 'fluent-smtp') ?>
</p>
<p class="text-large" style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; text-align: left; mso-line-height-rule: exactly; line-height: 140%; margin: 0 0 15px 0; Margin: 0 0 15px 0; font-size: 16px;">
<?php esc_html_e('Thank you for using Fluent SMTP Plugin. The ultimate SMTP plugin you need for making sure your emails are delivered.', 'fluent-smtp') ?><br /><br /><?php esc_html_e('FluentSMTP is a free opensource plugin and it will be always free ', 'fluent-smtp') ?>(<a href="https://fluentsmtp.com/why-we-built-fluentsmtp-plugin/"><?php esc_html_e('Learn why it\'s free', 'fluent-smtp') ?></a>).
</p>
<p class="signature" style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; text-align: left; margin: 20px 0 0 0; Margin: 20px 0 0 0;">
<img src="<?php echo esc_url( fluentMailMix('images/mail_signature.png')); ?>" width="250" alt="Fluent SMTP Logo" style="outline: none; text-decoration: none; max-width: 100%; clear: both; -ms-interpolation-mode: bicubic; display: inline-block !important; width: 150px;">
</p>
<p style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; margin: 0 0 15px 0; Margin: 0 0 15px 0;">
Shahjahan Jewel<br>
CEO, WPManageNinja LLC
</p>
<p style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; text-align: left; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; margin: 20px 0 15px 0; Margin: 20px 0 15px 0;">
<?php esc_html_e('This email was sent from ', 'fluent-smtp') ?><b><?php echo esc_html(get_bloginfo('name')); ?> at <?php echo esc_html(current_time('mysql')); ?></b>
</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<p style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #444; font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; font-weight: normal; padding: 0; text-align: center; font-size: 14px; mso-line-height-rule: exactly; line-height: 140%; margin: 0 0 15px 0; margin: 0 0 15px 0;">
<?php esc_html_e('PS: if you have a minute please ', 'fluent-smtp') ?><a href="https://wordpress.org/support/plugin/fluent-smtp/reviews/?filter=5"><?php esc_html_e('write a review for FluentSMTP', 'fluent-smtp') ?></a>
</p>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,5 @@
Hi There,
Are you seeing this email? You are? Well awesome - that means you're all set to start sending emails from your site.
Thank you for using FluentSMTP 🎉
This Email sent at (server time): <?php echo esc_html(date('Y-m-d H:i:s')); ?>

View File

@@ -0,0 +1,28 @@
<div class="fss_connection_info">
<table class="wp-list-table widefat striped">
<tr>
<th><?php esc_html_e('Connection Type', 'fluent-smtp') ?></th>
<td><?php echo esc_html($connection['provider']); ?></td>
</tr>
<tr>
<th><?php esc_html_e('Sender Email', 'fluent-smtp') ?></th>
<td><?php echo esc_html($connection['sender_email']); ?></td>
</tr>
<tr>
<th><?php esc_html_e('Sender Name', 'fluent-smtp') ?></th>
<td><?php echo esc_html($connection['sender_name']); ?></td>
</tr>
<tr>
<th><?php esc_html_e('Force Sender Name', 'fluent-smtp') ?></th>
<td><?php echo esc_html(ucfirst($connection['force_from_name'])); ?></td>
</tr>
<?php if(isset($connection['extra_rows'])) : ?>
<?php foreach ($connection['extra_rows'] as $row): ?>
<tr>
<th><?php echo esc_html($row['title']); ?></th>
<td><?php echo wp_kses_post($row['content']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</table>
</div>

View File

@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>FluentSMTP</title>
<style>
.fsmtp_wrap {
margin: 200px auto 0;
width: 500px;
text-align: center;
border: 1px solid gray;
}
.fsmtp_title {
padding: 10px 20px;
border-bottom: 1px solid gray;
background: hsl(240deg 8% 93%);
}
.fsmtp_body {
padding: 20px;
background: white;
}
body {
margin: 0;
padding: 0;
min-height: 100vh;
background: hsl(240deg 6% 87%);
}
textarea {
width: 100%;
padding: 10px;
}
.fsmtp_wrap * {
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="fsmtp_wrap">
<div class="fsmtp_title"><?php echo esc_html($title); ?></div>
<div class="fsmtp_body">
<?php echo $body; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
<?php // silence is golden

View File

@@ -0,0 +1 @@
<div id='fluent_mail_app'></div>

View File

@@ -0,0 +1,60 @@
<div class="fss_connection_info">
<?php if($error): ?>
<p style="color: red;" class="connection_info_error"><?php esc_html_e('Connection Error: ', 'fluent-smtp') ?><?php echo wp_kses_post($error); ?></p>
<?php endif; ?>
<table class="wp-list-table widefat striped">
<tr>
<th><?php esc_html_e('Connection Type', 'fluent-smtp') ?></th>
<td>Amazon SES</td>
</tr>
<?php if(isset($stats['Max24HourSend'])): ?>
<tr>
<th><?php esc_html_e('Max Send in 24 hours', 'fluent-smtp') ?></th>
<td><?php echo (int) $stats['Max24HourSend']; ?></td>
</tr>
<?php endif; ?>
<?php if(isset($stats['SentLast24Hours'])): ?>
<tr>
<th><?php esc_html_e('Sent in last 24 hours', 'fluent-smtp') ?></th>
<td><?php echo (int) $stats['SentLast24Hours']; ?></td>
</tr>
<?php endif; ?>
<?php if(isset($stats['MaxSendRate'])): ?>
<tr>
<th><?php esc_html_e('Max Sending Rate', 'fluent-smtp') ?></th>
<td><?php echo (int) $stats['MaxSendRate']; ?>/sec</td>
</tr>
<?php endif; ?>
<tr>
<th><?php esc_html_e('Sender Email', 'fluent-smtp') ?></th>
<td><?php echo $connection['sender_email']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
</tr>
<tr>
<th><?php esc_html_e('Sender Name', 'fluent-smtp') ?></th>
<td><?php echo esc_html($connection['sender_name']); ?></td>
</tr>
<tr>
<th><?php esc_html_e('Force Sender Name', 'fluent-smtp') ?></th>
<td><?php echo esc_html(ucfirst($connection['force_from_name'])); ?></td>
</tr>
<tr>
<th><?php esc_html_e('Valid Sending Emails', 'fluent-smtp') ?></th>
<td>
<ul>
<?php foreach ($valid_senders as $sender): ?>
<li><?php echo esc_html($sender); ?></li>
<?php endforeach; ?>
</ul>
</td>
</tr>
</table>
<?php if(!$error && empty($stats['Max24HourSend'])): ?>
<p style="color: red;" class="connection_info_error">
<?php esc_html_e('Looks like you are in sandbox mode. Please apply to Amazon AWS to approve your account. ', 'fluent-smtp') ?><a href="https://fluentcrm.com/set-up-amazon-ses-with-fluentcrm/#4-moving-out-of-sandbox-mode" target="_blank" rel="nofollow"><?php esc_html_e('Read More here.', 'fluent-smtp') ?></a>
</p>
<?php endif; ?>
<p><a href="https://aws.amazon.com/ses/extendedaccessrequest/" target="_blank" rel="nofollow"><?php esc_html_e('Increase Sending Limits', 'fluent-smtp') ?></a></p>
</div>

View File

@@ -0,0 +1 @@
<?php // silence is golden