‪TYPO3CMS  ‪main
TransportFactory.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 namespace ‪TYPO3\CMS\Core\Mail;
19 
20 use Psr\Log\LoggerAwareInterface;
21 use Psr\Log\LoggerAwareTrait;
22 use Symfony\Component\Mailer\Transport;
23 use Symfony\Component\Mailer\Transport\NullTransport;
24 use Symfony\Component\Mailer\Transport\SendmailTransport;
25 use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
26 use Symfony\Component\Mailer\Transport\TransportInterface;
27 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
33 
37 class ‪TransportFactory implements ‪SingletonInterface, LoggerAwareInterface
38 {
39  use LoggerAwareTrait;
40 
41  public const ‪SPOOL_MEMORY = 'memory';
42  public const ‪SPOOL_FILE = 'file';
43 
44  public function ‪__construct(
45  protected readonly EventDispatcherInterface $dispatcher,
46  protected readonly ‪LogManagerInterface $logManager,
47  ) {}
48 
56  public function get(array $mailSettings): TransportInterface
57  {
58  if (!isset($mailSettings['transport'])) {
59  throw new \InvalidArgumentException('Key "transport" must be set in the mail settings', 1469363365);
60  }
61  if ($mailSettings['transport'] === 'spool') {
62  throw new \InvalidArgumentException('Mail transport can not be set to "spool"', 1469363238);
63  }
64 
65  $transportType = isset($mailSettings['transport_spool_type'])
66  && !empty($mailSettings['transport_spool_type'])
67  ? 'spool' : (string)$mailSettings['transport'];
68 
69  switch ($transportType) {
70  case 'spool':
71  $transport = $this->‪createSpool($mailSettings);
72  break;
73  case 'smtp':
74  // Get settings to be used when constructing the transport object
75  if (
76  isset($mailSettings['transport_smtp_server'])
77  && strpos($mailSettings['transport_smtp_server'], ':') > 0
78  ) {
79  $parts = ‪GeneralUtility::trimExplode(':', $mailSettings['transport_smtp_server'], true);
80  $host = $parts[0];
81  $port = $parts[1] ?? null;
82  } else {
83  $host = (string)($mailSettings['transport_smtp_server'] ?? '');
84  $port = null;
85  }
86 
87  if ($host === '') {
88  throw new ‪Exception('$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'transport_smtp_server\'] needs to be set when transport is set to "smtp".', 1291068606);
89  }
90  if ($port === null) {
91  $port = 25;
92  } else {
93  $port = (int)$port;
94  }
95  $useEncryption = (bool)($mailSettings['transport_smtp_encrypt'] ?? false) ?: null;
96  // Create transport
97  $transport = new EsmtpTransport(
98  $host,
99  $port,
100  $useEncryption,
101  $this->dispatcher,
102  $this->logManager->getLogger(EsmtpTransport::class)
103  );
104  $streamOptions = (array)($mailSettings['transport_smtp_stream_options'] ?? []);
105  if (!empty($streamOptions)) {
106  $stream = $transport->getStream();
107  $stream->setStreamOptions(array_merge_recursive($stream->getStreamOptions(), $streamOptions));
108  }
109  // Need authentication?
110  $username = (string)($mailSettings['transport_smtp_username'] ?? '');
111  if ($username !== '') {
112  $transport->setUsername($username);
113  }
114  $password = (string)($mailSettings['transport_smtp_password'] ?? '');
115  if ($password !== '') {
116  $transport->setPassword($password);
117  }
118  $mailDomain = (string)($mailSettings['transport_smtp_domain'] ?? '');
119  if ($mailDomain !== '') {
120  $transport->setLocalDomain($mailDomain);
121  }
122  $restartThreshold = (int)($mailSettings['transport_smtp_restart_threshold'] ?? 0);
123  $restartThresholdSleep = (int)($mailSettings['transport_smtp_restart_threshold_sleep'] ?? 0);
124  if ($restartThreshold > 0) {
125  if ($restartThresholdSleep < 0) {
126  // invalid, use default for threshold sleep
127  $restartThresholdSleep = 0;
128  }
129  $transport->setRestartThreshold($restartThreshold, $restartThresholdSleep);
130  }
131  $pingThreshold = (int)($mailSettings['transport_smtp_ping_threshold'] ?? 0);
132  if ($pingThreshold > 0) {
133  $transport->setPingThreshold($pingThreshold);
134  }
135  break;
136  case 'sendmail':
137  $sendmailCommand = $mailSettings['transport_sendmail_command'] ?? @ini_get('sendmail_path');
138  if (empty($sendmailCommand)) {
139  $sendmailCommand = '/usr/sbin/sendmail -bs';
140  $this->logger->warning('Mailer transport "sendmail" was chosen without a specific command, using "{command}"', ['command' => $sendmailCommand]);
141  }
142  // Create transport
143  $transport = new SendmailTransport(
144  $sendmailCommand,
145  $this->dispatcher,
146  $this->logManager->getLogger(SendmailTransport::class)
147  );
148  break;
149  case 'mbox':
150  $mboxFile = (string)($mailSettings['transport_mbox_file'] ?? '');
151  if ($mboxFile === '') {
152  throw new ‪Exception('$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'transport_mbox_file\'] needs to be set when transport is set to "mbox".', 1294586645);
153  }
154  $fileNameValidator = GeneralUtility::makeInstance(FileNameValidator::class);
155  if (!$fileNameValidator->isValid($mboxFile)) {
156  throw new ‪Exception('$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'transport_mbox_file\'] failed against deny-pattern', 1705312431);
157  }
158  // Create our transport
159  $transport = GeneralUtility::makeInstance(
160  MboxTransport::class,
161  $mboxFile,
162  $this->dispatcher,
163  $this->logManager->getLogger(MboxTransport::class)
164  );
165  break;
166  // Used for testing purposes
167  case 'null':
168  case NullTransport::class:
169  $transport = new NullTransport(
170  $this->dispatcher,
171  $this->logManager->getLogger(NullTransport::class)
172  );
173  break;
174  // Used by Symfony's Transport Factory
175  case !empty($mailSettings['dsn']):
176  case 'dsn':
177  if (empty($mailSettings['dsn'])) {
178  throw new ‪Exception('$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'dsn\'] needs to be set when transport is set to "dsn".', 1615021869);
179  }
180  $transport = Transport::fromDsn(
181  $mailSettings['dsn'],
182  $this->dispatcher,
183  null,
184  $this->logManager->getLogger(Transport::class)
185  );
186  break;
187  default:
188  // Custom mail transport
189  $transport = GeneralUtility::makeInstance($mailSettings['transport'], $mailSettings);
190  if (!$transport instanceof TransportInterface) {
191  throw new \RuntimeException($mailSettings['transport'] . ' is not an implementation of Symfony\Mailer\TransportInterface,
192  but must implement that interface to be used as a mail transport.', 1323006478);
193  }
194  }
195 
196  return $transport;
197  }
198 
204  protected function ‪createSpool(array $mailSettings): ‪DelayedTransportInterface
205  {
206  $transportSpoolType = (string)($mailSettings['transport_spool_type'] ?? '');
207  switch ($transportSpoolType) {
208  case ‪self::SPOOL_FILE:
209  $path = GeneralUtility::getFileAbsFileName($mailSettings['transport_spool_filepath'] ?? '');
210  if (empty($path)) {
211  throw new \RuntimeException('The Spool Type filepath must be configured for TYPO3 in order to be used. Be sure that it\'s not accessible via the web.', 1518558797);
212  }
213  $spool = GeneralUtility::makeInstance(
214  FileSpool::class,
215  $path,
216  $this->dispatcher,
217  $this->logManager->getLogger(FileSpool::class)
218  );
219  break;
221  $spool = GeneralUtility::makeInstance(
222  MemorySpool::class,
223  $this->dispatcher,
224  $this->logManager->getLogger(MemorySpool::class)
225  );
226  break;
227  default:
228  $spool = GeneralUtility::makeInstance($transportSpoolType, $mailSettings);
229  if (!($spool instanceof ‪DelayedTransportInterface)) {
230  throw new \RuntimeException(
231  $mailSettings['transport_spool_type'] . ' is not an implementation of DelayedTransportInterface, but must implement that interface to be used as a mail spool.',
232  1466799482
233  );
234  }
235  break;
236  }
237  return $spool;
238  }
239 }
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Core\Log\LogManagerInterface
Definition: LogManagerInterface.php:24
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Core\Mail\TransportFactory\SPOOL_FILE
‪const SPOOL_FILE
Definition: TransportFactory.php:42
‪TYPO3\CMS\Core\Mail\TransportFactory\SPOOL_MEMORY
‪const SPOOL_MEMORY
Definition: TransportFactory.php:41
‪TYPO3\CMS\Core\Mail\DelayedTransportInterface
Definition: DelayedTransportInterface.php:26
‪TYPO3\CMS\Core\Mail\TransportFactory\createSpool
‪createSpool(array $mailSettings)
Definition: TransportFactory.php:204
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Mail
Definition: DelayedTransportInterface.php:18
‪TYPO3\CMS\Core\Mail\TransportFactory\__construct
‪__construct(protected readonly EventDispatcherInterface $dispatcher, protected readonly LogManagerInterface $logManager,)
Definition: TransportFactory.php:44
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Core\Mail\TransportFactory
Definition: TransportFactory.php:38