1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Core\Mail; 17 18use Psr\EventDispatcher\EventDispatcherInterface; 19use Symfony\Component\Mailer\Envelope; 20use Symfony\Component\Mailer\MailerInterface; 21use Symfony\Component\Mailer\SentMessage; 22use Symfony\Component\Mailer\Transport\TransportInterface; 23use Symfony\Component\Mime\Address; 24use Symfony\Component\Mime\Email; 25use Symfony\Component\Mime\RawMessage; 26use TYPO3\CMS\Core\Exception as CoreException; 27use TYPO3\CMS\Core\Mail\Event\AfterMailerInitializationEvent; 28use TYPO3\CMS\Core\Utility\GeneralUtility; 29use TYPO3\CMS\Core\Utility\MailUtility; 30 31/** 32 * Adapter for Symfony/Mailer to be used by TYPO3 extensions. 33 * 34 * This will use the setting in TYPO3_CONF_VARS to choose the correct transport 35 * for it to work out-of-the-box. 36 */ 37class Mailer implements MailerInterface 38{ 39 /** 40 * @var TransportInterface 41 */ 42 protected $transport; 43 44 /** 45 * @var array 46 */ 47 protected $mailSettings = []; 48 49 /** 50 * @var SentMessage|null 51 */ 52 protected $sentMessage; 53 54 /** 55 * @var string This will be added as X-Mailer to all outgoing mails 56 */ 57 protected $mailerHeader = 'TYPO3'; 58 59 /** 60 * @var EventDispatcherInterface 61 */ 62 protected $eventDispatcher; 63 64 /** 65 * When constructing, also initializes the Symfony Transport like configured 66 * 67 * @param TransportInterface|null $transport optionally pass a transport to the constructor. 68 * @param EventDispatcherInterface|null $eventDispatcher 69 * @throws CoreException 70 */ 71 public function __construct(TransportInterface $transport = null, EventDispatcherInterface $eventDispatcher = null) 72 { 73 if ($transport !== null) { 74 $this->transport = $transport; 75 } else { 76 if (empty($this->mailSettings)) { 77 $this->injectMailSettings(); 78 } 79 try { 80 $this->initializeTransport(); 81 } catch (\Exception $e) { 82 throw new CoreException($e->getMessage(), 1291068569); 83 } 84 } 85 if ($eventDispatcher !== null) { 86 $this->eventDispatcher = $eventDispatcher; 87 $this->eventDispatcher->dispatch(new AfterMailerInitializationEvent($this)); 88 } 89 } 90 91 /** 92 * @inheritdoc 93 */ 94 public function send(RawMessage $message, Envelope $envelope = null): void 95 { 96 if ($message instanceof Email) { 97 // Ensure to always have a From: header set 98 if (empty($message->getFrom())) { 99 $address = MailUtility::getSystemFromAddress(); 100 if ($address) { 101 $name = MailUtility::getSystemFromName(); 102 if ($name) { 103 $from = new Address($address, $name); 104 } else { 105 $from = new Address($address); 106 } 107 $message->from($from); 108 } 109 } 110 if (empty($message->getReplyTo())) { 111 $replyTo = MailUtility::getSystemReplyTo(); 112 if (!empty($replyTo)) { 113 $address = key($replyTo); 114 if ($address === 0) { 115 $replyTo = new Address($replyTo[$address]); 116 } else { 117 $replyTo = new Address((string)$address, reset($replyTo)); 118 } 119 $message->replyTo($replyTo); 120 } 121 } 122 $message->getHeaders()->addTextHeader('X-Mailer', $this->mailerHeader); 123 } 124 125 $this->sentMessage = $this->transport->send($message, $envelope); 126 } 127 128 public function getSentMessage(): ?SentMessage 129 { 130 return $this->sentMessage; 131 } 132 133 public function getTransport(): TransportInterface 134 { 135 return $this->transport; 136 } 137 138 /** 139 * Prepares a transport using the TYPO3_CONF_VARS configuration 140 * 141 * Used options: 142 * $TYPO3_CONF_VARS['MAIL']['transport'] = 'smtp' | 'sendmail' | 'null' | 'mbox' 143 * 144 * $TYPO3_CONF_VARS['MAIL']['transport_smtp_server'] = 'smtp.example.org:25'; 145 * $TYPO3_CONF_VARS['MAIL']['transport_smtp_encrypt'] = FALSE; # requires openssl in PHP 146 * $TYPO3_CONF_VARS['MAIL']['transport_smtp_username'] = 'username'; 147 * $TYPO3_CONF_VARS['MAIL']['transport_smtp_password'] = 'password'; 148 * 149 * $TYPO3_CONF_VARS['MAIL']['transport_sendmail_command'] = '/usr/sbin/sendmail -bs' 150 * 151 * @throws CoreException 152 * @throws \RuntimeException 153 */ 154 private function initializeTransport() 155 { 156 $this->transport = $this->getTransportFactory()->get($this->mailSettings); 157 } 158 159 /** 160 * This method is only used in unit tests 161 * 162 * @param array $mailSettings 163 * @internal 164 */ 165 public function injectMailSettings(array $mailSettings = null) 166 { 167 if (is_array($mailSettings)) { 168 $this->mailSettings = $mailSettings; 169 } else { 170 $this->mailSettings = (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL']; 171 } 172 } 173 174 /** 175 * Returns the real transport (not a spool). 176 * 177 * @return TransportInterface 178 */ 179 public function getRealTransport(): TransportInterface 180 { 181 $mailSettings = !empty($this->mailSettings) ? $this->mailSettings : (array)$GLOBALS['TYPO3_CONF_VARS']['MAIL']; 182 unset($mailSettings['transport_spool_type']); 183 return $this->getTransportFactory()->get($mailSettings); 184 } 185 186 /** 187 * @return TransportFactory 188 */ 189 protected function getTransportFactory(): TransportFactory 190 { 191 return GeneralUtility::makeInstance(TransportFactory::class); 192 } 193} 194