Mailer.php 6.05 KB
Newer Older
1
<?php
2

3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
7
8
 * 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.
9
 *
10
11
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
14
 * The TYPO3 project - inspiring people to share!
 */
15

16
17
namespace TYPO3\CMS\Core\Mail;

18
use Psr\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\Mailer\Envelope;
20
21
22
23
24
25
26
use Symfony\Component\Mailer\MailerInterface;
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;
27
use TYPO3\CMS\Core\Mail\Event\AfterMailerInitializationEvent;
28
29
use TYPO3\CMS\Core\Mail\Event\AfterMailerSentMessageEvent;
use TYPO3\CMS\Core\Mail\Event\BeforeMailerSentMessageEvent;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
use TYPO3\CMS\Core\Utility\MailUtility;
32

33
/**
34
 * Adapter for Symfony/Mailer to be used by TYPO3 extensions.
35
36
37
38
 *
 * This will use the setting in TYPO3_CONF_VARS to choose the correct transport
 * for it to work out-of-the-box.
 */
39
class Mailer implements MailerInterface
40
{
41
    protected array $mailSettings = [];
42

43
    protected ?SentMessage $sentMessage;
44

45
    /**
46
     * This will be added as X-Mailer to all outgoing mails
47
     */
48
    protected string $mailerHeader = 'TYPO3';
49

50
51
    /**
     * When constructing, also initializes the Symfony Transport like configured
52
     *
53
     * @param TransportInterface|null $transport optionally pass a transport to the constructor.
54
     * @param EventDispatcherInterface|null $eventDispatcher
55
     * @throws CoreException
56
     */
57
58
59
60
61
62
    public function __construct(
        protected ?TransportInterface $transport = null,
        protected readonly ?EventDispatcherInterface $eventDispatcher = null,
    ) {
        if (empty($this->mailSettings)) {
            $this->injectMailSettings();
63
        }
64
65
66
67
68

        try {
            $this->initializeTransport();
        } catch (\Exception $e) {
            throw new CoreException($e->getMessage(), 1291068569);
69
        }
70
71

        $this->eventDispatcher?->dispatch(new AfterMailerInitializationEvent($this));
72
    }
73

74
    public function send(RawMessage $message, Envelope $envelope = null): void
75
76
77
78
79
80
81
82
    {
        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) {
83
                        $from = new Address($address, $name);
84
85
86
87
88
89
90
91
92
93
94
95
96
                    } 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 {
97
                        $replyTo = new Address((string)$address, reset($replyTo));
98
99
100
101
102
103
104
                    }
                    $message->replyTo($replyTo);
                }
            }
            $message->getHeaders()->addTextHeader('X-Mailer', $this->mailerHeader);
        }

105
106
107
108
109
110
111
112
113
        // 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));
114
115
116
117
118
119
120
121
122
123
124
125
    }

    public function getSentMessage(): ?SentMessage
    {
        return $this->sentMessage;
    }

    public function getTransport(): TransportInterface
    {
        return $this->transport;
    }

126
127
128
129
    /**
     * Prepares a transport using the TYPO3_CONF_VARS configuration
     *
     * Used options:
130
     * $TYPO3_CONF_VARS['MAIL']['transport'] = 'smtp' | 'sendmail' | 'null' | 'mbox'
131
     *
132
     * $TYPO3_CONF_VARS['MAIL']['transport_smtp_server'] = 'smtp.example.org:25';
133
134
135
136
137
138
     * $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'
     *
139
     * @throws CoreException
140
141
142
143
     * @throws \RuntimeException
     */
    private function initializeTransport()
    {
144
        $this->transport ??= $this->getTransportFactory()->get($this->mailSettings);
145
    }
146

147
148
149
150
    /**
     * This method is only used in unit tests
     *
     * @param array $mailSettings
151
     * @internal
152
153
154
     */
    public function injectMailSettings(array $mailSettings = null)
    {
155
        $this->mailSettings = $mailSettings ?? (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL'];
156
    }
157

158
159
160
    /**
     * Returns the real transport (not a spool).
     *
161
     * @return TransportInterface
162
     */
163
    public function getRealTransport(): TransportInterface
164
    {
165
        $mailSettings = !empty($this->mailSettings) ? $this->mailSettings : (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL'];
166
167
168
169
170
171
172
173
174
175
176
        unset($mailSettings['transport_spool_type']);
        return $this->getTransportFactory()->get($mailSettings);
    }

    /**
     * @return TransportFactory
     */
    protected function getTransportFactory(): TransportFactory
    {
        return GeneralUtility::makeInstance(TransportFactory::class);
    }
177
}