[TASK] Re-work/simplify copyright header in PHP files - Part 3
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Mail / SwiftMailerAdapter.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 /**
18 * Hook subscriber for using Swift Mailer with \TYPO3\CMS\Core\Utility\MailUtility
19 *
20 * @author Jigal van Hemert <jigal@xs4all.nl>
21 * @deprecated since 6.1, will be removed two versions later - will be removed together with \TYPO3\CMS\Core\Utility\MailUtility::mail()
22 */
23 class SwiftMailerAdapter implements \TYPO3\CMS\Core\Mail\MailerAdapterInterface {
24
25 /**
26 * @var $mailer \TYPO3\CMS\Core\Mail\Mailer
27 */
28 protected $mailer;
29
30 /**
31 * @var $message Swift_Message
32 */
33 protected $message;
34
35 /**
36 * @var $messageHeaders Swift_Mime_HeaderSet
37 */
38 protected $messageHeaders;
39
40 /**
41 * @var string
42 */
43 protected $boundary = '';
44
45 /**
46 * Constructor
47 *
48 * @return void
49 */
50 public function __construct() {
51 // create mailer object
52 $this->mailer = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\Mailer');
53 // create message object
54 $this->message = \Swift_Message::newInstance();
55 }
56
57 /**
58 * Parses parts of the mail message and sends it with the Swift Mailer functions
59 *
60 * @param string $to Email address to send the message to
61 * @param string $subject Subject of mail message
62 * @param string $messageBody Raw body (may be multipart)
63 * @param array $additionalHeaders Additional mail headers
64 * @param array $additionalParameters Extra parameters for the mail() command
65 * @param boolean $fakeSending If set fake sending a mail
66 * @throws \TYPO3\CMS\Core\Exception
67 * @return boolean
68 */
69 public function mail($to, $subject, $messageBody, $additionalHeaders = NULL, $additionalParameters = NULL, $fakeSending = FALSE) {
70 // report success for fake sending
71 if ($fakeSending === TRUE) {
72 return TRUE;
73 }
74 $this->message->setSubject($subject);
75 // handle recipients
76 $toAddresses = $this->parseAddresses($to);
77 $this->message->setTo($toAddresses);
78 // handle additional headers
79 $headers = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(LF, $additionalHeaders, TRUE);
80 $this->messageHeaders = $this->message->getHeaders();
81 foreach ($headers as $header) {
82 list($headerName, $headerValue) = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $header, FALSE, 2);
83 $this->setHeader($headerName, $headerValue);
84 }
85 // handle additional parameters (force return path)
86 if (preg_match('/-f\\s*(\\S*?)/', $additionalParameters, $matches)) {
87 $this->message->setReturnPath($this->unescapeShellArguments($matches[1]));
88 }
89 // handle from:
90 $this->fixSender();
91 // handle message body
92 $this->setBody($messageBody);
93 // send mail
94 $result = $this->mailer->send($this->message);
95 // report success/failure
96 return (bool) $result;
97 }
98
99 /**
100 * Tries to undo the action by escapeshellarg()
101 *
102 * @param string $escapedString String escaped by escapeshellarg()
103 * @return string String with escapeshellarg() action undone as best as possible
104 */
105 protected function unescapeShellArguments($escapedString) {
106 if (TYPO3_OS === 'WIN') {
107 // on Windows double quotes are used and % signs are replaced by spaces
108 if (preg_match('/^"([^"]*)"$/', trim($escapedString), $matches)) {
109 $result = str_replace('\\"', '"', $matches[1]);
110 }
111 } else {
112 // on Unix-like systems single quotes are escaped
113 if (preg_match('/^\'([^' . preg_quote('\'') . ']*)\'$/', trim($escapedString), $matches)) {
114 $result = str_replace('\\\'', '\'', $matches[1]);
115 }
116 }
117 return $result;
118 }
119
120 /**
121 * Handles setting and replacing of mail headers
122 *
123 * @param string $headerName Name of header
124 * @param string $headerValue Value of header
125 * @return void
126 */
127 protected function setHeader($headerName, $headerValue) {
128 // check for boundary in headers
129 if (preg_match('/^boundary="(.*)"$/', $headerName, $matches) > 0) {
130 $this->boundary = $matches[1];
131 return;
132 }
133
134 // Ignore empty header-values (like from an 'Reply-To:' without an email-address)
135 $headerValue = trim($headerValue);
136 if (empty($headerValue)) {
137 return;
138 }
139
140 // process other, real headers
141 if ($this->messageHeaders->has($headerName)) {
142 $header = $this->messageHeaders->get($headerName);
143 $headerType = $header->getFieldType();
144 switch ($headerType) {
145 case \Swift_Mime_Header::TYPE_TEXT:
146 $header->setValue($headerValue);
147 break;
148 case \Swift_Mime_Header::TYPE_PARAMETERIZED:
149 $header->setValue(rtrim($headerValue, ';'));
150 break;
151 case \Swift_Mime_Header::TYPE_MAILBOX:
152 $addressList = $this->parseAddresses($headerValue);
153 if (count($addressList) > 0) {
154 $header->setNameAddresses($addressList);
155 }
156 break;
157 case \Swift_Mime_Header::TYPE_DATE:
158 $header->setTimeStamp(strtotime($headerValue));
159 break;
160 case \Swift_Mime_Header::TYPE_ID:
161 // remove '<' and '>' from ID headers
162 $header->setId(trim($headerValue, '<>'));
163 break;
164 case \Swift_Mime_Header::TYPE_PATH:
165 $header->setAddress($headerValue);
166 break;
167 }
168 } else {
169 switch ($headerName) {
170 case 'From':
171 case 'To':
172 case 'Cc':
173 case 'Bcc':
174 case 'Reply-To':
175 case 'Sender':
176 $addressList = $this->parseAddresses($headerValue);
177 if (count($addressList) > 0) {
178 $this->messageHeaders->addMailboxHeader($headerName, $addressList);
179 }
180 break;
181 case 'Date':
182 $this->messageHeaders->addDateHeader($headerName, strtotime($headerValue));
183 break;
184 case 'Message-ID':
185 // remove '<' and '>' from ID headers
186 $this->messageHeaders->addIdHeader($headerName, trim($headerValue, '<>'));
187 case 'Return-Path':
188 $this->messageHeaders->addPathHeader($headerName, $headerValue);
189 break;
190 case 'Content-Type':
191 case 'Content-Disposition':
192 $this->messageHeaders->addParameterizedHeader($headerName, rtrim($headerValue, ';'));
193 break;
194 default:
195 $this->messageHeaders->addTextheader($headerName, $headerValue);
196 }
197 }
198 }
199
200 /**
201 * Sets body of mail message. Handles multi-part and single part messages. Encoded body parts are decoded prior to adding
202 * them to the message object.
203 *
204 * @param string $body Raw body, may be multi-part
205 * @return void
206 */
207 protected function setBody($body) {
208 if ($this->boundary) {
209 // handle multi-part
210 $bodyParts = preg_split('/--' . preg_quote($this->boundary, '/') . '(--)?/m', $body, NULL, PREG_SPLIT_NO_EMPTY);
211 foreach ($bodyParts as $bodyPart) {
212 // skip empty parts
213 if (trim($bodyPart) == '') {
214 continue;
215 }
216 // keep leading white space when exploding the text
217 $lines = explode(LF, $bodyPart);
218 // set defaults for this part
219 $encoding = '';
220 $charset = 'utf-8';
221 $contentType = 'text/plain';
222 // skip intro messages
223 if (trim($lines[0]) == 'This is a multi-part message in MIME format.') {
224 continue;
225 }
226 // first line is empty leftover from splitting
227 array_shift($lines);
228 while (count($lines) > 0) {
229 $line = array_shift($lines);
230 if (preg_match('/^content-type:(.*);( charset=(.*))?$/i', $line, $matches)) {
231 $contentType = trim($matches[1]);
232 if ($matches[2]) {
233 $charset = trim($matches[3]);
234 }
235 } elseif (preg_match('/^content-transfer-encoding:(.*)$/i', $line, $matches)) {
236 $encoding = trim($matches[1]);
237 } elseif (strlen(trim($line)) == 0) {
238 // empty line before actual content of this part
239 break;
240 }
241 }
242 // use rest of part as body, but reverse encoding first
243 $bodyPart = $this->decode(implode(LF, $lines), $encoding);
244 $this->message->addPart($bodyPart, $contentType, $charset);
245 }
246 } else {
247 // Handle single body
248 // The headers have already been set, so use header information
249 $contentType = $this->message->getContentType();
250 $charset = $this->message->getCharset();
251 $encoding = $this->message->getEncoder()->getName();
252 // reverse encoding and set body
253 $rawBody = $this->decode($body, $encoding);
254 $this->message->setBody($rawBody, $contentType, $charset);
255 }
256 }
257
258 /**
259 * Reverts encoding of body text
260 *
261 * @param string $text Body text to be decoded
262 * @param string $encoding Encoding type to be reverted
263 * @return string Decoded message body
264 */
265 protected function decode($text, $encoding) {
266 $result = $text;
267 switch ($encoding) {
268 case 'quoted-printable':
269 $result = quoted_printable_decode($text);
270 break;
271 case 'base64':
272 $result = base64_decode($text);
273 break;
274 }
275 return $result;
276 }
277
278 /**
279 * Parses mailbox headers and turns them into an array.
280 *
281 * Mailbox headers are a comma separated list of 'name <email@example.org' combinations or plain email addresses (or a mix
282 * of these).
283 * The resulting array has key-value pairs where the key is either a number (no display name in the mailbox header) and the
284 * value is the email address, or the key is the email address and the value is the display name.
285 *
286 * @param string $rawAddresses Comma separated list of email addresses (optionally with display name)
287 * @return array Parsed list of addresses.
288 */
289 protected function parseAddresses($rawAddresses = '') {
290 /** @var $addressParser \TYPO3\CMS\Core\Mail\Rfc822AddressesParser */
291 $addressParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\Rfc822AddressesParser', $rawAddresses);
292 $addresses = $addressParser->parseAddressList();
293 $addressList = array();
294 foreach ($addresses as $address) {
295 if ($address->personal) {
296 // item with name found ( name <email@example.org> )
297 $addressList[$address->mailbox . '@' . $address->host] = $address->personal;
298 } else {
299 // item without name found ( email@example.org )
300 $addressList[] = $address->mailbox . '@' . $address->host;
301 }
302 }
303 return $addressList;
304 }
305
306 /**
307 * Makes sure there is a correct sender set.
308 *
309 * If there is no from header the returnpath will be used. If that also fails a fake address will be used to make sure
310 * Swift Mailer will be able to send the message. Some SMTP server will not accept mail messages without a valid sender.
311 *
312 * @return void
313 */
314 protected function fixSender() {
315 $from = $this->message->getFrom();
316 if (count($from) > 0) {
317 reset($from);
318 list($fromAddress, $fromName) = each($from);
319 } else {
320 $fromAddress = $this->message->getReturnPath();
321 $fromName = $fromAddress;
322 }
323 if (strlen($fromAddress) == 0) {
324 $fromAddress = 'no-reply@example.org';
325 $fromName = 'TYPO3 CMS';
326 }
327 $this->message->setFrom(array($fromAddress => $fromName));
328 }
329
330 }