[BUGFIX] Always use system getFrom() when sending emails
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Mail / MailMessage.php
1 <?php
2 namespace TYPO3\CMS\Core\Mail;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\MailUtility;
18
19 /**
20 * Adapter for Swift_Mailer to be used by TYPO3 extensions
21 */
22 class MailMessage extends \Swift_Message
23 {
24 /**
25 * @var \TYPO3\CMS\Core\Mail\Mailer
26 */
27 protected $mailer;
28
29 /**
30 * @var string This will be added as X-Mailer to all outgoing mails
31 */
32 protected $mailerHeader = 'TYPO3';
33
34 /**
35 * TRUE if the message has been sent.
36 *
37 * @var bool
38 */
39 protected $sent = false;
40
41 /**
42 * Holds the failed recipients after the message has been sent
43 *
44 * @var array
45 */
46 protected $failedRecipients = [];
47
48 /**
49 * @return void
50 */
51 private function initializeMailer()
52 {
53 $this->mailer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\Mailer::class);
54 }
55
56 /**
57 * Sends the message.
58 *
59 * @return int the number of recipients who were accepted for delivery
60 */
61 public function send()
62 {
63 // Ensure to always have a From: header set
64 if (empty($this->getFrom())) {
65 $this->setFrom(MailUtility::getSystemFrom());
66 }
67 $this->initializeMailer();
68 $this->sent = true;
69 $this->getHeaders()->addTextHeader('X-Mailer', $this->mailerHeader);
70 return $this->mailer->send($this, $this->failedRecipients);
71 }
72
73 /**
74 * Checks whether the message has been sent.
75 *
76 * @return bool
77 */
78 public function isSent()
79 {
80 return $this->sent;
81 }
82
83 /**
84 * Returns the recipients for which the mail was not accepted for delivery.
85 *
86 * @return array the recipients who were not accepted for delivery
87 */
88 public function getFailedRecipients()
89 {
90 return $this->failedRecipients;
91 }
92
93 /**
94 * Set the return-path (the bounce address) of this message.
95 *
96 * @param string $address
97 * @return \TYPO3\CMS\Core\Mail\MailMessage
98 */
99 public function setReturnPath($address)
100 {
101 $address = $this->idnaEncodeAddresses($address);
102 return parent::setReturnPath($address);
103 }
104
105 /**
106 * Set the sender of this message.
107 *
108 * This does not override the From field, but it has a higher significance.
109 *
110 * @param string $address
111 * @param string $name optional
112 * @return \TYPO3\CMS\Core\Mail\MailMessage
113 */
114 public function setSender($address, $name = null)
115 {
116 $address = $this->idnaEncodeAddresses($address);
117 return parent::setSender($address, $name);
118 }
119
120 /**
121 * Set the from address of this message.
122 *
123 * You may pass an array of addresses if this message is from multiple people.
124 *
125 * If $name is passed and the first parameter is a string, this name will be
126 * associated with the address.
127 *
128 * @param string|array $addresses
129 * @param string $name optional
130 * @return \TYPO3\CMS\Core\Mail\MailMessage
131 */
132 public function setFrom($addresses, $name = null)
133 {
134 $addresses = $this->idnaEncodeAddresses($addresses);
135 return parent::setFrom($addresses, $name);
136 }
137
138 /**
139 * Set the reply-to address of this message.
140 *
141 * You may pass an array of addresses if replies will go to multiple people.
142 *
143 * If $name is passed and the first parameter is a string, this name will be
144 * associated with the address.
145 *
146 * @param string|array $addresses
147 * @param string $name optional
148 * @return \TYPO3\CMS\Core\Mail\MailMessage
149 */
150 public function setReplyTo($addresses, $name = null)
151 {
152 $addresses = $this->idnaEncodeAddresses($addresses);
153 return parent::setReplyTo($addresses, $name);
154 }
155
156 /**
157 * Set the to addresses of this message.
158 *
159 * If multiple recipients will receive the message an array should be used.
160 * Example: array('receiver@domain.org', 'other@domain.org' => 'A name')
161 *
162 * If $name is passed and the first parameter is a string, this name will be
163 * associated with the address.
164 *
165 * @param string|array $addresses
166 * @param string $name optional
167 * @return \TYPO3\CMS\Core\Mail\MailMessage
168 */
169 public function setTo($addresses, $name = null)
170 {
171 $addresses = $this->idnaEncodeAddresses($addresses);
172 return parent::setTo($addresses, $name);
173 }
174
175 /**
176 * Set the Cc addresses of this message.
177 *
178 * If $name is passed and the first parameter is a string, this name will be
179 * associated with the address.
180 *
181 * @param string|array $addresses
182 * @param string $name optional
183 * @return \TYPO3\CMS\Core\Mail\MailMessage
184 */
185 public function setCc($addresses, $name = null)
186 {
187 $addresses = $this->idnaEncodeAddresses($addresses);
188 return parent::setCc($addresses, $name);
189 }
190
191 /**
192 * Set the Bcc addresses of this message.
193 *
194 * If $name is passed and the first parameter is a string, this name will be
195 * associated with the address.
196 *
197 * @param string|array $addresses
198 * @param string $name optional
199 * @return \TYPO3\CMS\Core\Mail\MailMessage
200 */
201 public function setBcc($addresses, $name = null)
202 {
203 $addresses = $this->idnaEncodeAddresses($addresses);
204 return parent::setBcc($addresses, $name);
205 }
206
207 /**
208 * Ask for a delivery receipt from the recipient to be sent to $addresses.
209 *
210 * @param array $addresses
211 * @return \TYPO3\CMS\Core\Mail\MailMessage
212 */
213 public function setReadReceiptTo($addresses)
214 {
215 $addresses = $this->idnaEncodeAddresses($addresses);
216 return parent::setReadReceiptTo($addresses);
217 }
218
219 /**
220 * IDNA encode email addresses. Accepts addresses in all formats that SwiftMailer supports
221 *
222 * @param string|array $addresses
223 * @return string|array
224 */
225 protected function idnaEncodeAddresses($addresses)
226 {
227 if (!is_array($addresses)) {
228 return $this->idnaEncodeAddress($addresses);
229 }
230 $newAddresses = [];
231 foreach ($addresses as $email => $name) {
232 if (ctype_digit($email)) {
233 $newAddresses[] = $this->idnaEncodeAddress($name);
234 } else {
235 $newAddresses[$this->idnaEncodeAddress($email)] = $name;
236 }
237 }
238
239 return $newAddresses;
240 }
241
242 /**
243 * IDNA encode the domain part of an email address if it contains non ASCII characters
244 *
245 * @param mixed $email
246 * @return mixed
247 * @see \TYPO3\CMS\Core\Utility\GeneralUtility::validEmail
248 */
249 protected function idnaEncodeAddress($email)
250 {
251 // Early return in case input is not a string
252 if (!is_string($email)) {
253 return $email;
254 }
255 // Split on the last "@" since adresses like "foo@bar"@example.org are valid
256 $atPosition = strrpos($email, '@');
257 if (!$atPosition || $atPosition + 1 === strlen($email)) {
258 // Return if no @ found or it is placed at the very beginning or end of the email
259 return $email;
260 }
261 $domain = substr($email, $atPosition + 1);
262 $local = substr($email, 0, $atPosition);
263 $domain = \TYPO3\CMS\Core\Utility\GeneralUtility::idnaEncode($domain);
264
265 return $local . '@' . $domain;
266 }
267 }