Commit 5a9a23ae by Manzar Hussain

add another smtp

parent bb4790b6
SMTP Authentication Support module for Drupal 8.x.
This module adds SMTP functionality to Drupal.
REQUIREMENTS
------------
* Access to an SMTP server that will accept mail from you.
* The following PHP extensions need to be installed: hash, date & pcre.
* The PHPMailer class has been unbundled, and must be installed with composer.
Refer to https://github.com/PHPMailer/PHPMailer for details.
* Optional: To connect to an SMTP server using SSL, you need to have the
openssl package installed on your server, and your webserver and PHP
installation need to have additional components installed and configured.
INSTALLATION INSTRUCTIONS
-------------------------
1. Copy the files included in the tarball into a directory named "smtp" in
your Drupal /modules/ or /modules/contrib/ directory.
2. Enable the module:
a. Login as site administrator, visit the Extend page, and enable SMTP.
b. Run "drush pm-enable smtp" on the command line.
3. Enable the SMTP Authentication Support module on the Manage -> Extend
page.
4. Fill in required settings on the Manage -> Configuration -> System ->
SMTP Authentication Support page.
5. Enjoy.
CONFIGURATION
------------
* Configure the user permissions in Administration -> People -> Permissions:
- Administer SMTP Authentication Support module.
RELATED MODULES
---------------
You may find the following modules helpful:
- mailsystem: controls which mail-related modules are used by other actions.
- mimemail: Makes HTML email easier to send.
- pet: Previewable Templating module
- rules: can send emails when "events" happen, such as publishing a node.
NOTES
-----
Valid SMTP Server
This module sends email by connecting to an SMTP server. Therefore, you need
to have access to an SMTP server, that is configured to accept email from you,
for this module to work.
Selecting Connection Port
Mail servers listen for new mail on TCP/IP numbered "ports", and while these
can be whatever the administrator determines, the standard values are:
+ 25 : Internet-wide email transmission by default in cleartext but can
upgrade to TLS when both sides support it;
+ 465 : A version of port 25 for which SSL is enabled from the beginning.
Now deprecated in favour of port 587 and the use of "STARTTLS";
+ 587 : "Mail submission" - used for clients talking to a "smarthost"
server (e.g. the ISP server) for queueing and/or forwarding.
The recommendation is for SMTP module to use port 587 with STARTTLS for
secure (especially cross-internet) connections to the ISP email server,
and port 25 for insecure and/or local connections. However, the actual port
and server to use will depend on the email server you will be using, and
if the above does not work for you, you should refer to that server's
documentation for more help.
Finally, it is worth mentioning that the popular development tool "mailhog"
uses port "8025".
Setting the From email address:
Drupal normally uses the From email address (see Manage -> Configuration ->
Site information -> E-mail address) as the Mail From address. It is important
for this to be the correct address and many ISPs will block email that comes
from an address they do not recognise as "theirs".
Sending mail to GMail requires SSL or TLS. Connecting to an SMTP server using
SSL is possible only if PHP's openssl extension is working. If the SMTP
module detects openssl is available it will display the options in the SMTP
settings page. Alternatively, run on the web host:
php -i | grep "Stream Socket Transports"
and look for at least one of:
ssl, tls, tlsv1.1, tlsv1.2
in the output.
Google Mail Authentication:
GMail currently supports using OAuth2 to authenticate senders of email, to
ensure that only the valid owners can use it. OAuth2 will be required from
early 2021 and new accounts will require it from mid-2020.
This restriction means that GMail and GSuite email addresses cannot be used
without also enabling OAuth2, which is not currently supported by the module.
----
Note for Office365 users:
The 'E-mail from name' field will most likley be ignored by Office365-hosted
Exchange. Please see this Microsoft KB article for more details:
https://support.microsoft.com/en-us/help/4458479/improvements-in-smtp-authenticated-submission-client-protocol
{
"name": "drupal/smtp",
"description": "Allow for site emails to be sent through an SMTP server of your choice.",
"type": "drupal-module",
"license": "GPL-2.0-or-later",
"homepage": "https://www.drupal.org/project/smtp",
"support": {
"issues": "https://www.drupal.org/project/issues/smtp"
},
"require": {
"drupal/core": "^8.8 || ^9",
"phpmailer/phpmailer": "^6.1.7"
},
"extra": {
"branch-alias": {
"dev-8.x-1.x": "1.x-dev"
}
},
"suggest": {
"drupal/mailsystem": "Allows using SMTP alongside other mail modules."
}
}
smtp_on: false
smtp_host: ''
smtp_hostbackup: ''
smtp_port: '25'
smtp_protocol: 'standard'
smtp_autotls: true
smtp_timeout: 30
smtp_username: ''
smtp_password: ''
smtp_from: ''
smtp_fromname: ''
smtp_client_hostname: ''
smtp_client_helo: ''
smtp_allowhtml: ''
smtp_test_address: ''
smtp_debugging: false
prev_mail_system: 'php_mail'
smtp_keepalive: false
smtp.settings:
type: config_object
label: 'SMTP config.'
mapping:
smtp_on:
type: boolean
label: 'Turn this module on or off'
smtp_host:
type: string
label: 'SMTP server'
smtp_hostbackup:
type: string
label: 'SMTP backup server'
smtp_port:
type: string
label: 'SMTP port'
smtp_protocol:
type: string
label: 'Use encrypted protocol'
smtp_autotls:
type: boolean
label: 'Enable TLS encryption automatically when supported by the remote host'
smtp_timeout:
type: integer
label: 'Amount of seconds for the SMTP command to timeout'
smtp_username:
type: string
label: 'Username'
smtp_password:
type: string
label: 'Password'
smtp_from:
type: email
label: 'E-mail from address'
smtp_fromname:
type: text
label: 'E-mail from name'
smtp_client_hostname:
type: string
label: 'Hostname'
smtp_client_helo:
type: string
label: 'HELO'
smtp_allowhtml:
type: string
label: 'Allow to send e-mails formated as HTML'
smtp_test_address:
type: email
label: 'E-mail address to send a test e-mail to'
smtp_debugging:
type: boolean
label: 'Enable debugging'
prev_mail_system:
type: string
label: 'Previous mail system'
smtp_keepalive:
type: boolean
label: 'Enable the connection keep alive function'
{
"require": {
"phpmailer/phpmailer": {
"version": "^6.1.7",
"url": "https://github.com/PHPMailer/PHPMailer/archive/v6.1.7.zip"
}
}
}
name: SMTP Authentication Support
description: "Allow for site emails to be sent through an SMTP server of your choice."
package: Mail
type: module
core_version_requirement: ^8.8 || ^9
configure: smtp.config
# Information added by Drupal.org packaging script on 2020-09-25
version: '8.x-1.0'
project: 'smtp'
datestamp: 1601070987
<?php
/**
* @file
* The installation instructions for the SMTP Authentication Support.
*/
use Drupal\Core\Url;
use PHPMailer\PHPMailer\PHPMailer;
/**
* Implements hook_uninstall().
*/
function smtp_uninstall() {
// Restore previous mail system.
_disable_smtp();
// Cleaning garbage.
$config = \Drupal::service('config.factory');
$smtp_config = $config->getEditable('smtp.settings');
$smtp_config->delete();
}
/**
* Add SMTP timeout configuration and change default to 30.
*/
function smtp_update_8001() {
\Drupal::configFactory()->getEditable('smtp.settings')
->set('smtp_timeout', 30)
->save(TRUE);
}
/**
* Add SMTP keepalive configuration and set default to FALSE.
*/
function smtp_update_8002() {
\Drupal::configFactory()->getEditable('smtp.settings')
->set('smtp_keepalive', FALSE)
->save(TRUE);
}
/**
* If mailsystem exists, disable smtp mailsystem automatically.
*/
function smtp_update_8004() {
$mailsystem_enabled = \Drupal::moduleHandler()->moduleExists('mailsystem');
if ($mailsystem_enabled) {
_disable_smtp();
}
}
/**
* Implements hook_install().
*/
function smtp_install() {
$messenger = \Drupal::messenger();
// @var \Drupal\Core\Routing\RouteBuilderInterface $routeBuilder $route_builder.
$route_builder = \Drupal::service('router.builder');
// Makes the 'smtp.config' route available here, see hook_install doc.
$route_builder->rebuild();
$messenger->addMessage(t('Thanks for installing SMTP Authentication Support'));
$messenger->addMessage(t('Server settings on <a href="@url_settings">SMTP Authentication Support</a>', [
'@url_settings' => Url::fromRoute('smtp.config')->toString(),
]));
}
/**
* Disable the SMTP mailsystem if the mailsystem module is installed.
*
* @param $modules
* @param $is_syncing
*/
function smtp_modules_installed($modules, $is_syncing = FALSE) {
if (in_array('mailsystem', $modules) && !$is_syncing) {
// If mailsystem module is enabled, make sure SMTP is disabled.
_disable_smtp();
}
}
/**
* Implements hook_requirements().
*/
function smtp_requirements(string $phase) {
$requirements = [];
if ($phase == 'runtime') {
// Ensure PHPMailer exists.
if (class_exists(PHPMailer::class)) {
$mail = new PHPMailer();
}
if (empty($mail)) {
$requirements['smtp_phpmailer'] = [
'title' => (string) t('SMTP: PHPMailer Library'),
'value' => (string) t('Missing'),
'severity' => REQUIREMENT_ERROR,
'description' => t('PHPMailer is Required for SMTP to function.'),
];
// If PHPMailer is not found, SMTP should not be set as the mail system.
_disable_smtp();
return $requirements;
}
else {
$required_version = '6.1.7';
$installed_version = $mail::VERSION;
$reflector = new \ReflectionClass('\PHPMailer\PHPMailer\PHPMailer');
$requirements['smtp_phpmailer'] = [
'title' => (string) t('SMTP: PHPMailer library'),
'value' => $installed_version,
'description' => t('PHPMailer is located at %path', ['%path' => $reflector->getFileName()]),
];
if (!version_compare($installed_version, $required_version, '>=')) {
$requirements['smtp_phpmailer']['severity'] = REQUIREMENT_ERROR;
$requirements['smtp_phpmailer']['description'] = (string) t("PHPMailer library @version or higher is required. Please install a newer version by executing 'composer update' in your site's root directory.", [
'@version' => $required_version,
]);
// If incorrect version, SMTP should not be set as the mail system.
_disable_smtp();
}
else {
$requirements['smtp_phpmailer']['severity'] = REQUIREMENT_INFO;
/** @var \Drupal\smtp\ConnectionTester\ConnectionTester $tester */
$tester = \Drupal::service('smtp.connection_tester');
$tester->testConnection();
$requirements = array_merge($requirements, $tester->hookRequirements($phase));
}
}
}
return $requirements;
}
/**
* Helper function to disable SMTP and restore the default mailsystem.
*/
function _disable_smtp() {
$config = \Drupal::service('config.factory');
// Always make sure SMTP is disabled.
$smtp_config = $config->getEditable('smtp.settings');
if (!$smtp_config->get('smtp_on')) {
return;
}
// Set the internal SMTP module config off.
$smtp_config->set('smtp_on', FALSE)->save();
// Set the default back to either the previous mail system or php_mail.
$mail_config = $config->getEditable('system.mail');
$current_default = $mail_config->get('interface.default');
$system_default = 'php_mail';
if ($current_default == 'SMTPMailSystem') {
$default_interface = (!$smtp_config->get('prev_mail_system')) ? $smtp_config->get('prev_mail_system') : $system_default;
$mail_config->set('interface.default', $default_interface)
->save();
}
}
smtp.config:
title: SMTP Authentication Support
route_name: smtp.config
parent: system.admin_config_system
description: 'Allow for site emails to be sent through an SMTP server of your choice.'
weight: -20
<?php
/**
* @file
* Enables Drupal to send e-mail directly to an SMTP server.
*
* This module uses a customized extract of the PHPMailer
* library (originally by Brent R. Matzelle, now maintained
* by Codeworx Tech.) relicensed from LGPL to GPL, included
* as a part of the module.
*
* Overriding mail handling in Drupal to make SMTP the default
* transport layer, requires to change the 'system.mail.interface'
* default value ['default' => 'Drupal\Core\Mail\PhpMail'].
* This module uses ['default' => 'SMTPMailSystem'].
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function smtp_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.smtp':
return t('Allow for site emails to be sent through an SMTP server of your choice.');
}
}
/**
* Implements hook_mail().
*/
function smtp_mail($key, &$message, $params) {
if ($key == 'smtp-test') {
$message['subject'] = $params['subject'];
$message['body'] = $params['body'];
}
}
/**
* Implements hook_queue_info().
*/
function smtp_queue_info() {
$queues['smtp_send_queue'] = [
'worker callback' => 'smtp_send_queue_runner',
'cron' => [
'time' => 60,
],
];
return $queues;
}
/**
* Smtp_send_queue queuer.
*/
function smtp_send_queue($mailerObj) {
$queue = Drupal::queue('smtp_send_queue');
$queue->createItem($mailerObj);
}
/**
* SMTP queue runner function.
*
* @param array $variables
* Variables to send trought runner.
*/
function smtp_send_queue_runner(array $variables) {
_smtp_mailer_send($variables);
}
/**
* Helper function to send mails.
*
* @param array $variables
* Variables to send email.
*
* @return bool
* True if email was sent. False otherwise.
*/
function _smtp_mailer_send(array $variables) {
$smtp_config = \Drupal::config('smtp.settings');
$mailer = $variables['mailer'];
$to = $variables['to'];
$from = $variables['from'];
$logger = \Drupal::logger('smtp');
// Let the people know what is going on.
$logger->info('Sending mail to: @to', ['@to' => $to]);
// Try to send e-mail. If it fails, set watchdog entry.
try {
$mailer->Send();
}
catch (Exception $e) {
$logger->error('Error sending e-mail from @from to @to: @error_message', [
'@from' => $from,
'@to' => $to,
'@error_message' => $mailer->ErrorInfo,
]);
return FALSE;
}
if (!$smtp_config->get('smtp_keepalive')) {
$mailer->SmtpClose();
}
return TRUE;
}
administer smtp module:
title: 'Administer SMTP Authentication Support module'
description: 'Perform administration tasks for SMTP Authentication Support module.'
restrict access: true
<?php
/**
* @file
* Post update functions for Smtp.
*/
/**
* Rebuild caches to ensure services changes are read in.
*/
function smtp_post_update_connection_tester() {
// Empty update to cause a cache rebuild so that the service changes are read.
}
/**
* Add SMTP timeout configuration and change default to 30.
*/
function smtp_post_update_set_smtp_timeout() {
\Drupal::configFactory()->getEditable('smtp.settings')
->set('smtp_timeout', 30)
->save(TRUE);
}
/**
* Add SMTP keepalive configuration and set default to FALSE.
*/
function smtp_post_update_set_smtp_keepalive() {
\Drupal::configFactory()->getEditable('smtp.settings')
->set('smtp_keepalive', FALSE)
->save(TRUE);
}
/**
* Add SMTP Auto TLS configuration and set default to TRUE.
*/
function smtp_post_update_set_smtp_autotls() {
\Drupal::configFactory()->getEditable('smtp.settings')
->set('smtp_autotls', TRUE)
->save(TRUE);
}
/**
* Rebuild caches to ensure the connection typo service change is updated.
*/
function smtp_post_update_connection_typo() {
// Empty update to cause a cache rebuild so that the service changes are read.
// Caused by this typo: https://www.drupal.org/project/smtp/issues/3150369
}
\ No newline at end of file
smtp.config:
path: '/admin/config/system/smtp'
defaults:
_title: 'SMTP Authentication Support'
_form: 'Drupal\smtp\Form\SMTPConfigForm'
requirements:
_permission: 'administer smtp module'
services:
smtp.logger_channel:
class: Drupal\Core\Logger\LoggerChannel
factory: logger.factory:get
arguments: ['smtp']
smtp.config:
class: Drupal\Core\Config\Config
factory: config.factory:getEditable
arguments: ['smtp.settings']
smtp.connection_tester:
class: Drupal\smtp\ConnectionTester\ConnectionTester
arguments: ['@config.factory', '@smtp.logger_channel', '@plugin.manager.mail']
<?php
namespace Drupal\smtp\ConnectionTester;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Mail\MailManager;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\mailsystem\MailsystemManager;
use PHPMailer\PHPMailer\Exception as PHPMailerException;
use PHPMailer\PHPMailer\PHPMailer;
use Psr\Log\LoggerInterface;
/**
* Allows testing the SMTP connection.
*/
class ConnectionTester {
use StringTranslationTrait;
// These constants de not seem to be available outside of the .install file
// so we need to declare them here.
const REQUIREMENT_OK = 0;
const REQUIREMENT_ERROR = 2;
/**
* The severity of the connection issue; set during class construction.
*
* @var int
*/
protected $severity;
/**
* Description of the connection, set during construction..
*
* @var string
*/
protected $value;
/**
* PHP Mailer Object.
*
* @var \PHPMailer\PHPMailer\PHPMailer
*/
protected $phpMailer;
/**
* The Config Factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The SMTP Config.
*
* @var \Drupal\Core\Config\Config
*/
protected $smtpConfig;
/**
* The smtp logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The mail manager.
*
* @var \Drupal\Core\Mail\MailManagerInterface
*/
protected $mailManager;
/**
* The SMTP ConnectionTester constructor.
*
* @param \Drupal\Core\Config\ConfigFactory $config_factory
* @param \Psr\Log\LoggerInterface $logger
* The logger channel.
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
* The Mail manager.
*/
public function __construct(ConfigFactory $config_factory, LoggerInterface $logger, MailManagerInterface $mail_manager) {
$this->configFactory = $config_factory;
$this->smtpConfig = $config_factory->get('smtp.settings');
$this->mailManager = $mail_manager;
$this->logger = $logger;
if (!class_exists(PHPMailer::class)) {
$this->logger->error('Unable to initialize PHPMailer, Class does not exist.');
return;
}
$this->phpMailer = new PHPMailer(TRUE);
}
public function setMailer(PHPMailer $mailer) {
$this->phpMailer = $mailer;
}
/**
* Test SMTP connection.
*/
public function testConnection() {
if (!$this->configurePhpMailer()) {
$this->severity = self::REQUIREMENT_ERROR;
$this->value = $this->t('Unable to initialize PHPMailer.');
return FALSE;
}
$smtp_enabled = $this->smtpConfig->get('smtp_on');
// Check to see if MailSystem is enabled and is using SMTPMailSystem.
if (\Drupal::moduleHandler()->moduleExists('mailsystem')) {
$mailsystem_defaults = $this->configFactory->get('mailsystem.settings')->get('defaults');
$smtp_enabled = in_array('SMTPMailSystem', $mailsystem_defaults);
}
if (!$smtp_enabled) {
$this->severity = self::REQUIREMENT_OK;
$this->value = $this->t('SMTP module is enabled but turned off.');
return FALSE;
}
try {
if ($this->phpMailer->smtpConnect()) {
$this->severity = self::REQUIREMENT_OK;
$this->value = $this->t('SMTP module is enabled, turned on, and connection is valid.');
return TRUE;
}
$this->severity = REQUIREMENT_ERROR;
$this->value = $this->t('SMTP module is enabled, turned on, but SmtpConnect() returned FALSE.');
return FALSE;
}
catch (PHPMailerException $e) {
$this->value = $this->t('SMTP module is enabled, turned on, but SmtpConnect() threw exception @e', [
'@e' => $e->getMessage(),
]);
$this->severity = self::REQUIREMENT_ERROR;
}
catch (\Exception $e) {
$this->value = $this->t('SMTP module is enabled, turned on, but SmtpConnect() threw an unexpected exception');
$this->severity = self::REQUIREMENT_ERROR;
}
return FALSE;
}
/**
* Testable implementation of hook_requirements().
*/
public function hookRequirements(string $phase) {
$requirements = [];
if ($phase == 'runtime') {
$requirements['smtp_connection'] = [
'title' => $this->t('SMTP connection')->__toString(),
'value' => $this->value->__toString(),
'severity' => $this->severity,
];
}
return $requirements;
}
/**
* Get a PHPMailer object ready to be tested.
*
* @return bool
* True if config was set, False if phpMailer didn't exist.
*/
protected function configurePhpMailer() {
if ($this->phpMailer) {
// Set debug to FALSE for the connection test; further debugging can be
// used when sending actual mails.
$this->phpMailer->SMTPDebug = FALSE;
// Hardcoded Timeout for testing so the reports page doesn't stall out.
$this->phpMailer->Timeout = 5;
$this->phpMailer->Host = implode(';', array_filter(
[
$this->smtpConfig->get('smtp_host'),
$this->smtpConfig->get('smtp_hostbackup'),
]
));
$this->phpMailer->Port = $this->smtpConfig->get('smtp_port');
$protocol = $this->smtpConfig->get('smtp_protocol');
$this->phpMailer->SMTPAutoTLS = $this->smtpConfig->get('smtp_autotls');
$this->phpMailer->SMTPSecure = in_array($protocol, ['ssl', 'tls'], TRUE) ? $protocol : '';
if ($smtp_client_hostname = $this->smtpConfig->get('smtp_client_hostname')) {
$this->phpMailer->Hostname = $smtp_client_hostname;
}
if ($helo = $this->smtpConfig->get('smtp_client_helo')) {
$this->phpMailer->Helo = $helo;
}
$username = $this->smtpConfig->get('smtp_username');
$password = $this->smtpConfig->get('smtp_password');
if ($username && $password) {
$this->phpMailer->SMTPAuth = TRUE;
$this->phpMailer->Username = $username;
$this->phpMailer->Password = $password;
}
return TRUE;
}
return FALSE;
}
}
<?php
namespace Drupal\Tests\smtp\Kernel\ConnectionTester;
use Drupal\KernelTests\KernelTestBase;
use Drupal\smtp\ConnectionTester\ConnectionTester;
use PHPMailer\PHPMailer\Exception as PHPMailerException;
use PHPMailer\PHPMailer\PHPMailer;
/**
* Class DeleteEntityTest.
*
* @group acquia_contenthub
*
* @package Drupal\Tests\acquia_contenthub\Kernel
*/
class ConnectionTesterTest extends KernelTestBase {
public static $modules = [
'smtp',
];
/**
* Test for hookRequirements().
*
* @param string $message
* The test message.
* @param bool $smtp_on
* Mock value of whether SMTP is on or not.
* @param bool $result
* Mock result of ::SmtpConnect().
* @param string $exception
* The exception, if any, that the mock SmtpConnect() should throw.
* @param array $expected
* The expected result; ignored if an exception is expected.
*
* @cover ::hookRequirements
* @dataProvider providerHookRequirements
*/
public function testHookRequirements(string $message, bool $smtp_on, bool $result, string $exception, array $expected) {
$smtp_settings = \Drupal::configFactory()
->getEditable('smtp.settings');
$smtp_settings->set('smtp_on', $smtp_on);
$smtp_settings->save();
$object = \Drupal::service('smtp.connection_tester');
$object->setMailer($this->getMockMailer($result, $exception));
$object->testConnection();
$output = $object->hookRequirements('runtime');
if ($output != $expected) {
print_r([
'message' => $message,
'output' => $output,
'expected' => $expected,
]);
}
$this->assertTrue($output == $expected, $message);
}
/**
* Provider for testHookRequirements().
*/
public function providerHookRequirements() {
return [
[
'message' => 'SMTP on, working.',
'$smtp_on' => TRUE,
'result' => TRUE,
'exception' => '',
'expected' => [
'smtp_connection' => [
'title' => 'SMTP connection',
'value' => 'SMTP module is enabled, turned on, and connection is valid.',
'severity' => ConnectionTester::REQUIREMENT_OK,
],
],
],
[
'message' => 'SMTP on, result FALSE.',
'$smtp_on' => TRUE,
'result' => FALSE,
'exception' => '',
'expected' => [
'smtp_connection' => [
'title' => 'SMTP connection',
'value' => 'SMTP module is enabled, turned on, but SmtpConnect() returned FALSE.',
'severity' => ConnectionTester::REQUIREMENT_ERROR,
],
],
],
[
'message' => 'SMTP on, PHPMailerException.',
'$smtp_on' => TRUE,
'result' => FALSE,
'exception' => PHPMailerException::class,
'expected' => [
'smtp_connection' => [
'title' => 'SMTP connection',
'value' => 'SMTP module is enabled, turned on, but SmtpConnect() threw exception EXCEPTION MESSAGE',
'severity' => ConnectionTester::REQUIREMENT_ERROR,
],
],
],
[
'message' => 'SMTP on, Exception.',
'$smtp_on' => TRUE,
'result' => FALSE,
'exception' => \Exception::class,
'expected' => [
'smtp_connection' => [
'title' => 'SMTP connection',
'value' => 'SMTP module is enabled, turned on, but SmtpConnect() threw an unexpected exception',
'severity' => ConnectionTester::REQUIREMENT_ERROR,
],
],
],
[
'message' => 'SMTP off.',
'$smtp_on' => FALSE,
'result' => FALSE,
'exception' => '',
'expected' => [
'smtp_connection' => [
'title' => 'SMTP connection',
'value' => 'SMTP module is enabled but turned off.',
'severity' => ConnectionTester::REQUIREMENT_OK,
],
],
],
];
}
/**
* Create a mock PHPMailer class for testing the exceptions.
*
* @param $result
* Expected Result.
* @param $exception
* Exception passed in.
*
* @return \PHPMailer\PHPMailer\PHPMailer|__anonymous@4029
*/
private function getMockMailer($result, $exception) {
$class = new class($result, $exception) extends PHPMailer {
public function __construct($result, $exception) {
$this->result = $result;
$this->exception = $exception;
}
/**
* Mock function for connection.
*/
public function smtpConnect($options = NULL) {
if ($this->exception) {
$class = $this->exception;
throw new $class('EXCEPTION MESSAGE');
}
return $this->result;
}
};
return $class;
}
}
<?php
namespace Drupal\Tests\smtp\Unit\Plugin\Mail;
use Drupal\Component\Utility\EmailValidator;
use Drupal\Component\Utility\EmailValidatorInterface;
use Drupal\Core\File\FileSystem;
use Drupal\Core\File\MimeType\MimeTypeGuesser;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Session\AccountProxy;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\smtp\Plugin\Mail\SMTPMailSystem;
use Drupal\Tests\UnitTestCase;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception as PHPMailerException;
use Prophecy\Argument;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Validate requirements for SMTPMailSystem.
*
* @group SMTP
*/
class SMTPMailSystemTest extends UnitTestCase {
/**
* The email validator.
*
* @var \Drupal\Component\Utility\EmailValidatorInterface
*/
protected $emailValidator;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->mockConfigFactory = $this->getConfigFactoryStub([
'smtp.settings' => ['smtp_timeout' => 30],
'system.site' => ['name' => 'Mock site name'],
]);
$this->mockLogger = $this->prophesize(LoggerChannelFactoryInterface::class);
$this->mockLogger->get('smtp')->willReturn($this->prophesize(LoggerChannelInterface::class));
$this->mockMessenger = $this->prophesize(Messenger::class);
$this->mockCurrentUser = $this->prophesize(AccountProxy::class);
$this->mockFileSystem = $this->prophesize(FileSystem::class);
$this->mimeTypeGuesser = $this->prophesize(MimeTypeGuesser::class);
$mockContainer = $this->mockContainer = $this->prophesize(ContainerInterface::class);
$mockContainer->get('config.factory')->willReturn($this->mockConfigFactory);
$mockContainer->get('logger.factory')->willReturn($this->mockLogger->reveal());
$mockContainer->get('messenger')->willReturn($this->mockMessenger->reveal());
$mockContainer->get('current_user')->willReturn($this->mockCurrentUser->reveal());
$mockContainer->get('file_system')->willReturn($this->mockFileSystem->reveal());
$mockContainer->get('file.mime_type.guesser')->willReturn($this->mimeTypeGuesser->reveal());
$mockStringTranslation = $this->prophesize(TranslationInterface::class);
$mockStringTranslation->translate(Argument::any())->willReturnArgument(0);
$mockStringTranslation->translate(Argument::any(), Argument::any())->willReturnArgument(0);
$mockStringTranslation->translateString(Argument::any())->willReturn('.');
$mockContainer->get('string_translation')->willReturn($mockStringTranslation->reveal());
// Email validator.
$this->emailValidator = new EmailValidator();
$mockContainer->get('email.validator')->willReturn($this->emailValidator);
\Drupal::setContainer($this->mockContainer->reveal());
}
/**
* Provides scenarios for getComponents().
*/
public function getComponentsProvider() {
return [
[
// Input.
'name@example.com',
// Expected.
[
'name' => '',
'email' => 'name@example.com',
],
],
[
' name@example.com',
[
'name' => '',
'input' => 'name@example.com',
'email' => 'name@example.com',
],
],
[
'name@example.com ',
[
'name' => '',
'input' => 'name@example.com',
'email' => 'name@example.com',
],
],
[
'some name <address@example.com>',
[
'name' => 'some name',
'email' => 'address@example.com',
],
],
[
'"some name" <address@example.com>',
[
'name' => 'some name',
'email' => 'address@example.com',
],
],
[
'<address@example.com>',
[
'name' => '',
'email' => 'address@example.com',
],
],
];
}
/**
* Test getComponents().
*
* @dataProvider getComponentsProvider
*/
public function testGetComponents($input, $expected) {
$mailSystem = new SMTPMailSystemTestHelper([], '', [], $this->mockLogger->reveal(), $this->mockMessenger->reveal(), $this->emailValidator, $this->mockConfigFactory, $this->mockCurrentUser->reveal(), $this->mockFileSystem->reveal(), $this->mimeTypeGuesser->reveal());
$ret = $mailSystem->publiGetComponents($input);
if (!empty($expected['input'])) {
$this->assertEquals($expected['input'], $ret['input']);
}
else {
$this->assertEquals($input, $ret['input']);
}
$this->assertEquals($expected['name'], $ret['name']);
$this->assertEquals($expected['email'], $ret['email']);
}
/**
* Provides scenarios for testMailValidator().
*/
public function mailValidatorProvider() {
$emailValidatorPhpMailerDefault = new EmailValidatorPhpMailerDefault();
$emailValidatorDrupal = new EmailValidator();
return [
'Without umlauts, PHPMailer default validator, no exception' => [
'test@drupal.org',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorPhpMailerDefault,
NULL,
],
'With umlauts in local part, PHPMailer default validator, exception' => [
'testmüller@drupal.org',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorPhpMailerDefault,
PHPMailerException::class,
],
'With umlauts in domain part, PHPMailer default validator, exception' => [
'test@müllertest.de',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorPhpMailerDefault,
PHPMailerException::class,
],
'Without top-level domain in domain part, PHPMailer default validator, exception' => [
'test@drupal',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorPhpMailerDefault,
PHPMailerException::class,
],
'Without umlauts, Drupal mail validator, no exception' => [
'test@drupal.org',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorDrupal,
NULL,
],
'With umlauts in local part, Drupal mail validator, no exception' => [
'testmüller@drupal.org',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorDrupal,
NULL,
],
'With umlauts in domain part, Drupal mail validator, no exception' => [
'test@müllertest.de',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorDrupal,
NULL,
],
'Without top-level domain in domain part, Drupal mail validator, no exception' => [
'test@drupal',
'PhpUnit Localhost <phpunit@localhost.com>',
$emailValidatorDrupal,
NULL,
],
];
}
/**
* Test mail() with focus on the mail validator.
*
* @dataProvider mailValidatorProvider
*/
public function testMailValidator(string $to, string $from, EmailValidatorInterface $validator, $exception) {
$this->emailValidator = $validator;
$mailSystem = new SMTPMailSystemTestHelper(
[],
'',
[],
$this->mockLogger->reveal(),
$this->mockMessenger->reveal(),
$validator,
$this->mockConfigFactory,
$this->mockCurrentUser->reveal(),
$this->mockFileSystem->reveal(),
$this->mimeTypeGuesser->reveal()
);
$message = [
'to' => $to,
'from' => $from,
'body' => 'Some test content for testMailValidatorDrupal',
'headers' => [
'content-type' => 'text/plain',
],
'subject' => 'testMailValidatorDrupal',
];
if (isset($exception)) {
$this->expectException($exception);
}
// Call function.
$result = $mailSystem->mail($message);
// More important than the result is that no exception was thrown, if
// $exception is unset.
self::assertTrue($result);
}
}
/**
* Test helper for SMTPMailSystemTest.
*/
class SMTPMailSystemTestHelper extends SMTPMailSystem {
/**
* Exposes getComponents for testing.
*/
public function publiGetComponents($input) {
return $this->getComponents($input);
}
/**
* Dummy of smtpMailerSend.
*/
public function smtpMailerSend($mailerArr) {
return TRUE;
}
}
/**
* An adaptor class wrapping the default PHPMailer validator.
*/
class EmailValidatorPhpMailerDefault implements EmailValidatorInterface {
/**
* {@inheritdoc}
*
* This function validates in same way the PHPMailer class does in its
* default behavior.
*/
public function isValid($email) {
PHPMailer::$validator = 'php';
return PHPMailer::validateAddress($email);
}
}
<?php
namespace Drupal\Tests\smtp\Unit;
use Drupal\Component\Utility\EmailValidatorInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\smtp\Form\SMTPConfigForm;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Validate requirements for SMTPConfigForm.
*
* @group SMTP
*/
class SMTPConfigFormTest extends UnitTestCase {
/**
* Test setup.
*/
public function setup() {
$this->mockConfigFactory = $this->prophesize(ConfigFactoryInterface::class);
$this->mockConfig = $this->prophesize(Config::class);
$this->mockConfigFactory->get('smtp.settings')->willReturn($this->mockConfig->reveal());
$this->mockConfigFactory->getEditable('smtp.settings')->willReturn($this->mockConfig->reveal());
$this->mockConfigSystemSite = $this->prophesize(Config::class);
$this->mockConfigSystemSite->get('name')->willReturn('Site name');
$this->mockConfigFactory->get('system.site')->willReturn($this->mockConfigSystemSite->reveal());
$this->mockMessenger = $this->prophesize(Messenger::class);
$this->mockEmailValidator = $this->prophesize(EmailValidatorInterface::class);
$this->mockCurrentUser = $this->prophesize(AccountProxyInterface::class);
$this->mockMailManager = $this->prophesize(MailManagerInterface::class);
$this->mockModuleHandler = $this->prophesize(ModuleHandlerInterface::class);
$mockContainer = $this->mockContainer = $this->prophesize(ContainerInterface::class);
$mockContainer->get('config.factory')->willReturn($this->mockConfigFactory->reveal());
$mockContainer->get('messenger')->willReturn($this->mockMessenger->reveal());
$mockContainer->get('email.validator')->willReturn($this->mockEmailValidator->reveal());
$mockContainer->get('current_user')->willReturn($this->mockCurrentUser->reveal());
$mockContainer->get('plugin.manager.mail')->willReturn($this->mockMailManager->reveal());
$mockContainer->get('module_handler')->willReturn($this->mockModuleHandler->reveal());
$mockStringTranslation = $this->prophesize(TranslationInterface::class);
$mockStringTranslation->translate(Argument::any())->willReturnArgument(0);
$mockStringTranslation->translate(Argument::any(), Argument::any())->willReturnArgument(0);
$mockStringTranslation->translateString(Argument::any())->willReturn('.');
$mockContainer->get('string_translation')->willReturn($mockStringTranslation->reveal());
\Drupal::setContainer($this->mockContainer->reveal());
}
/**
* Sets the default smtp config.
*/
public function setDefaultConfig() {
$this->mockConfig->get('smtp_on')->willReturn(TRUE);
$this->mockConfig->get('smtp_host')->willReturn('');
$this->mockConfig->get('smtp_hostbackup')->willReturn('');
$this->mockConfig->get('smtp_port')->willReturn('');
$this->mockConfig->get('smtp_protocol')->willReturn('');
$this->mockConfig->get('smtp_autotls')->willReturn(TRUE);
$this->mockConfig->get('smtp_timeout')->willReturn('');
$this->mockConfig->get('smtp_username')->willReturn('');
$this->mockConfig->get('smtp_password')->willReturn('');
$this->mockConfig->get('smtp_from')->willReturn('');
$this->mockConfig->get('smtp_fromname')->willReturn('');
$this->mockConfig->get('smtp_allowhtml')->willReturn('');
$this->mockConfig->get('smtp_client_hostname')->willReturn('');
$this->mockConfig->get('smtp_client_helo')->willReturn('');
$this->mockConfig->get('smtp_debugging')->willReturn('');
$this->mockConfig->get('smtp_keepalive')->willReturn(FALSE);
}
/**
* Test if enabled message is properly shown.
*/
public function testBuildFormEnabledMessage() {
$this->setDefaultConfig();
$this->mockConfig->get('smtp_on')->willReturn(TRUE);
$formBuilder = SMTPConfigForm::create($this->mockContainer->reveal());
$form = [];
$formBuilder->buildForm($form, new FormState());
$this->mockMessenger->addMessage(Argument::which('getUntranslatedString', 'SMTP module is active.'))->shouldHaveBeenCalled();
}
/**
* Test if enabled message is properly shown.
*/
public function testBuildFormDisabledMessage() {
$this->setDefaultConfig();
$this->mockConfig->get('smtp_on')->willReturn(FALSE);
$formBuilder = SMTPConfigForm::create($this->mockContainer->reveal());
$form = [];
$formBuilder->buildForm($form, new FormState());
$this->mockMessenger->addMessage(Argument::which('getUntranslatedString', 'SMTP module is INACTIVE.'))->shouldHaveBeenCalled();
}
/**
* Test form id.
*/
public function testGetFormId() {
$formBuilder = SMTPConfigForm::create($this->mockContainer->reveal());
$form_id = $formBuilder->getFormId();
$this->assertEquals('smtp_admin_settings', $form_id);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment