173 lines
6.0 KiB
PHP
173 lines
6.0 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Core\Mail;
|
|
|
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
|
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
|
use Symfony\Component\Mailer\Envelope;
|
|
use Symfony\Component\Mailer\SentMessage;
|
|
use Symfony\Component\Mailer\Transport\TransportInterface;
|
|
use Symfony\Component\Mime\Address;
|
|
use Symfony\Component\Mime\Email;
|
|
use Symfony\Component\Mime\RawMessage;
|
|
use TYPO3\CMS\Core\Exception as CoreException;
|
|
use TYPO3\CMS\Core\Mail\Event\AfterMailerSentMessageEvent;
|
|
use TYPO3\CMS\Core\Mail\Event\BeforeMailerSentMessageEvent;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Core\Utility\MailUtility;
|
|
|
|
/**
|
|
* Adapter for Symfony/Mailer to be used by TYPO3 extensions.
|
|
*
|
|
* This will use the setting in TYPO3_CONF_VARS to choose the correct transport
|
|
* for it to work out-of-the-box.
|
|
*/
|
|
#[Autoconfigure(public: true), AsAlias(MailerInterface::class, public: true)]
|
|
class Mailer implements MailerInterface
|
|
{
|
|
protected array $mailSettings = [];
|
|
|
|
protected ?SentMessage $sentMessage;
|
|
|
|
/**
|
|
* This will be added as X-Mailer to all outgoing mails
|
|
*/
|
|
protected string $mailerHeader = 'TYPO3';
|
|
|
|
/**
|
|
* When constructing, also initializes the Symfony Transport like configured
|
|
*
|
|
* @param TransportInterface|null $transport optionally pass a transport to the constructor.
|
|
* @throws CoreException
|
|
*/
|
|
public function __construct(
|
|
protected ?TransportInterface $transport = null,
|
|
protected readonly ?EventDispatcherInterface $eventDispatcher = null,
|
|
) {
|
|
if (empty($this->mailSettings)) {
|
|
$this->injectMailSettings();
|
|
}
|
|
|
|
try {
|
|
$this->initializeTransport();
|
|
} catch (\Exception $e) {
|
|
throw new CoreException($e->getMessage(), 1291068569);
|
|
}
|
|
}
|
|
|
|
public function send(RawMessage $message, ?Envelope $envelope = null): void
|
|
{
|
|
if ($message instanceof Email) {
|
|
// Ensure to always have a From: header set
|
|
if (empty($message->getFrom())) {
|
|
$address = MailUtility::getSystemFromAddress();
|
|
if ($address) {
|
|
$name = MailUtility::getSystemFromName();
|
|
if ($name) {
|
|
$from = new Address($address, $name);
|
|
} else {
|
|
$from = new Address($address);
|
|
}
|
|
$message->from($from);
|
|
}
|
|
}
|
|
if (empty($message->getReplyTo())) {
|
|
$replyTo = MailUtility::getSystemReplyTo();
|
|
if (!empty($replyTo)) {
|
|
$address = key($replyTo);
|
|
if ($address === 0) {
|
|
$replyTo = new Address($replyTo[$address]);
|
|
} else {
|
|
$replyTo = new Address((string)$address, reset($replyTo));
|
|
}
|
|
$message->replyTo($replyTo);
|
|
}
|
|
}
|
|
// Only set X-Mailer header once, if message is re-used
|
|
if (!$message->getHeaders()->has('X-Mailer')) {
|
|
$message->getHeaders()->addTextHeader('X-Mailer', $this->mailerHeader);
|
|
}
|
|
}
|
|
|
|
// After static enrichment took place, allow listeners to further manipulate message and envelope
|
|
$event = new BeforeMailerSentMessageEvent($this, $message, $envelope);
|
|
$this->eventDispatcher?->dispatch($event);
|
|
|
|
// Send message using the defined transport, with message and envelope from the event
|
|
$this->sentMessage = $this->transport->send($event->getMessage(), $event->getEnvelope());
|
|
|
|
// Finally, allow further processing by listeners after the message has been sent
|
|
$this->eventDispatcher?->dispatch(new AfterMailerSentMessageEvent($this));
|
|
}
|
|
|
|
public function getSentMessage(): ?SentMessage
|
|
{
|
|
return $this->sentMessage;
|
|
}
|
|
|
|
public function getTransport(): TransportInterface
|
|
{
|
|
return $this->transport;
|
|
}
|
|
|
|
/**
|
|
* Prepares a transport using the TYPO3_CONF_VARS configuration
|
|
*
|
|
* Used options:
|
|
* $TYPO3_CONF_VARS['MAIL']['transport'] = 'smtp' | 'sendmail' | 'null' | 'mbox'
|
|
*
|
|
* $TYPO3_CONF_VARS['MAIL']['transport_smtp_server'] = 'smtp.example.org:25';
|
|
* $TYPO3_CONF_VARS['MAIL']['transport_smtp_encrypt'] = FALSE; # requires openssl in PHP
|
|
* $TYPO3_CONF_VARS['MAIL']['transport_smtp_username'] = 'username';
|
|
* $TYPO3_CONF_VARS['MAIL']['transport_smtp_password'] = 'password';
|
|
*
|
|
* $TYPO3_CONF_VARS['MAIL']['transport_sendmail_command'] = '/usr/sbin/sendmail -bs'
|
|
*
|
|
* @throws CoreException
|
|
* @throws \RuntimeException
|
|
*/
|
|
private function initializeTransport()
|
|
{
|
|
$this->transport ??= $this->getTransportFactory()->get($this->mailSettings);
|
|
}
|
|
|
|
/**
|
|
* This method is only used in unit tests
|
|
*
|
|
* @internal
|
|
*/
|
|
public function injectMailSettings(?array $mailSettings = null)
|
|
{
|
|
$this->mailSettings = $mailSettings ?? (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL'];
|
|
}
|
|
|
|
/**
|
|
* Returns the real transport (not a spool).
|
|
*/
|
|
public function getRealTransport(): TransportInterface
|
|
{
|
|
$mailSettings = !empty($this->mailSettings) ? $this->mailSettings : (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL'];
|
|
unset($mailSettings['transport_spool_type']);
|
|
return $this->getTransportFactory()->get($mailSettings);
|
|
}
|
|
|
|
protected function getTransportFactory(): TransportFactory
|
|
{
|
|
return GeneralUtility::makeInstance(TransportFactory::class);
|
|
}
|
|
}
|