class.t3lib_formmail.php 13.3 KB
Newer Older
Kasper Skårhøj's avatar
Kasper Skårhøj committed
1
2
<?php
/***************************************************************
3
4
 *  Copyright notice
 *
5
 *  (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *  A copy is found in the textfile GPL.txt and important notices to the license
 *  from the author is found in LICENSE.txt distributed with these scripts.
 *
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/
Kasper Skårhøj's avatar
Kasper Skårhøj committed
27
28
29
/**
 * Contains a class for formmail
 *
30
 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
31
 *
32
 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
Kasper Skårhøj's avatar
Kasper Skårhøj committed
33
34
 */
/**
35
 * Formmail class, used by the TYPO3 "cms" extension (default frontend) to send email forms.
Kasper Skårhøj's avatar
Kasper Skårhøj committed
36
 *
37
 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
Kasper Skårhøj's avatar
Kasper Skårhøj committed
38
 * @see tslib_fe::sendFormmail(), t3lib/formmail.php
39
 * @TODO: Deprecate this class
Kasper Skårhøj's avatar
Kasper Skårhøj committed
40
 */
41
class t3lib_formmail {
42

43
	protected $reserved_names = 'recipient,recipient_copy,auto_respond_msg,auto_respond_checksum,redirect,subject,attachment,from_email,from_name,replyto_email,replyto_name,organisation,priority,html_enabled,quoted_printable,submit_x,submit_y';
44
45

	// Collection of suspicious header data, used for logging
46
	protected $dirtyHeaders = array();
Kasper Skårhøj's avatar
Kasper Skårhøj committed
47

48
	protected $characterSet;
49

50
	protected $subject;
51

52
	protected $fromName;
53

54
	protected $replyToName;
55

56
	protected $organisation;
57

58
	protected $fromAddress;
59

60
	protected $replyToAddress;
61

62
	protected $priority;
63

64
	protected $autoRespondMessage;
65

66
67
	protected $encoding = 'quoted-printable';

68
69
70
	/**
	 * @var \TYPO3\CMS\Core\Mail\MailMessage
	 */
71
	protected $mailMessage;
72

73
	protected $recipient;
74

75
76
	protected $plainContent = '';

77
78
79
	/**
	 * @var array Files to clean up at the end (attachments)
	 */
80
	protected $temporaryFiles = array();
Kasper Skårhøj's avatar
Kasper Skårhøj committed
81
82
83
84
85

	/**
	 * Start function
	 * This class is able to generate a mail in formmail-style from the data in $V
	 * Fields:
Kasper Skårhøj's avatar
Kasper Skårhøj committed
86
	 *
87
	 * [recipient]:			email-adress of the one to receive the mail. If array, then all values are expected to be recipients
Kasper Skårhøj's avatar
Kasper Skårhøj committed
88
	 * [attachment]:		....
Kasper Skårhøj's avatar
Kasper Skårhøj committed
89
	 *
Kasper Skårhøj's avatar
Kasper Skårhøj committed
90
91
	 * [subject]:			The subject of the mail
	 * [from_email]:		Sender email. If not set, [email] is used
92
93
	 * [from_name]:			Sender name. If not set, [name] is used
	 * [replyto_email]:		Reply-to email. If not set [from_email] is used
Kasper Skårhøj's avatar
Kasper Skårhøj committed
94
	 * [replyto_name]:		Reply-to name. If not set [from_name] is used
95
	 * [organisation]:		Organization (header)
Kasper Skårhøj's avatar
Kasper Skårhøj committed
96
97
	 * [priority]:			Priority, 1-5, default 3
	 * [html_enabled]:		If mail is sent as html
98
	 * [use_base64]:		If set, base64 encoding will be used instead of quoted-printable
Kasper Skårhøj's avatar
Kasper Skårhøj committed
99
	 *
100
101
102
	 * @param array $valueList Contains values for the field names listed above (with slashes removed if from POST input)
	 * @param boolean $base64 Whether to base64 encode the mail content
	 * @return void
103
	 * @todo Define visibility
Kasper Skårhøj's avatar
Kasper Skårhøj committed
104
	 */
105
106
	public function start($valueList, $base64 = FALSE) {
		$this->mailMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
107
		if ($GLOBALS['TSFE']->config['config']['formMailCharset']) {
108
			// Respect formMailCharset if it was set
109
110
			$this->characterSet = $GLOBALS['TSFE']->csConvObj->parse_charset($GLOBALS['TSFE']->config['config']['formMailCharset']);
		} elseif ($GLOBALS['TSFE']->metaCharset != $GLOBALS['TSFE']->renderCharset) {
111
			// Use metaCharset for mail if different from renderCharset
112
			$this->characterSet = $GLOBALS['TSFE']->metaCharset;
113
		} else {
114
			// Otherwise use renderCharset as default
115
			$this->characterSet = $GLOBALS['TSFE']->renderCharset;
116
117
118
		}
		if ($base64 || $valueList['use_base64']) {
			$this->encoding = 'base64';
119
		}
120
		if (isset($valueList['recipient'])) {
121
122
			// Convert form data from renderCharset to mail charset
			$this->subject = $valueList['subject'] ? $valueList['subject'] : 'Formmail on ' . \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_HOST');
123
			$this->subject = $this->sanitizeHeaderString($this->subject);
124
			$this->fromName = $valueList['from_name'] ? $valueList['from_name'] : ($valueList['name'] ? $valueList['name'] : '');
125
			$this->fromName = $this->sanitizeHeaderString($this->fromName);
126
			$this->replyToName = $valueList['replyto_name'] ? $valueList['replyto_name'] : $this->fromName;
127
			$this->replyToName = $this->sanitizeHeaderString($this->replyToName);
128
			$this->organisation = $valueList['organisation'] ? $valueList['organisation'] : '';
129
			$this->organisation = $this->sanitizeHeaderString($this->organisation);
130
131
132
133
			$this->fromAddress = $valueList['from_email'] ? $valueList['from_email'] : ($valueList['email'] ? $valueList['email'] : '');
			if (!\TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($this->fromAddress)) {
				$this->fromAddress = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFromAddress();
				$this->fromName = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFromName();
134
			}
135
136
137
138
			$this->replyToAddress = $valueList['replyto_email'] ? $valueList['replyto_email'] : $this->fromAddress;
			$this->priority = $valueList['priority'] ? \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($valueList['priority'], 1, 5) : 3;
			// Auto responder
			$this->autoRespondMessage = trim($valueList['auto_respond_msg']) && $this->fromAddress ? trim($valueList['auto_respond_msg']) : '';
139
			if ($this->autoRespondMessage !== '') {
140
				// Check if the value of the auto responder message has been modified with evil intentions
141
				$autoRespondChecksum = $valueList['auto_respond_checksum'];
142
				$correctHmacChecksum = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac($this->autoRespondMessage);
143
				if ($autoRespondChecksum !== $correctHmacChecksum) {
144
					\TYPO3\CMS\Core\Utility\GeneralUtility::sysLog('Possible misuse of t3lib_formmail auto respond method. Subject: ' . $valueList['subject'], 'Core', \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR);
145
146
					return;
				} else {
147
					$this->autoRespondMessage = $this->sanitizeHeaderString($this->autoRespondMessage);
148
149
				}
			}
150
151
			$plainTextContent = '';
			$htmlContent = '<table border="0" cellpadding="2" cellspacing="2">';
152
			// Runs through $V and generates the mail
153
154
			if (is_array($valueList)) {
				foreach ($valueList as $key => $val) {
155
156
157
158
					if (!\TYPO3\CMS\Core\Utility\GeneralUtility::inList($this->reserved_names, $key)) {
						$space = strlen($val) > 60 ? LF : '';
						$val = is_array($val) ? implode($val, LF) : $val;
						// Convert form data from renderCharset to mail charset (HTML may use entities)
159
160
						$plainTextValue = $val;
						$HtmlValue = htmlspecialchars($val);
161
162
						$plainTextContent .= strtoupper($key) . ':  ' . $space . $plainTextValue . LF . $space;
						$htmlContent .= '<tr><td bgcolor="#eeeeee"><font face="Verdana" size="1"><strong>' . strtoupper($key) . '</strong></font></td><td bgcolor="#eeeeee"><font face="Verdana" size="1">' . nl2br($HtmlValue) . '&nbsp;</font></td></tr>';
Kasper Skårhøj's avatar
Kasper Skårhøj committed
163
164
165
					}
				}
			}
166
167
168
			$htmlContent .= '</table>';
			$this->plainContent = $plainTextContent;
			if ($valueList['html_enabled']) {
169
170
				$this->mailMessage->setBody($htmlContent, 'text/html', $this->characterSet);
				$this->mailMessage->addPart($plainTextContent, 'text/plain', $this->characterSet);
171
			} else {
172
				$this->mailMessage->setBody($plainTextContent, 'text/plain', $this->characterSet);
Kasper Skårhøj's avatar
Kasper Skårhøj committed
173
			}
174
			for ($a = 0; $a < 10; $a++) {
175
				$variableName = 'attachment' . ($a ? $a : '');
176
				if (!isset($_FILES[$variableName])) {
177
178
					continue;
				}
179
				if (!is_uploaded_file($_FILES[$variableName]['tmp_name'])) {
180
					\TYPO3\CMS\Core\Utility\GeneralUtility::sysLog('Possible abuse of t3lib_formmail: temporary file "' . $_FILES[$variableName]['tmp_name'] . '" ("' . $_FILES[$variableName]['name'] . '") was not an uploaded file.', 'Core', \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR);
181
				}
182
				if ($_FILES[$variableName]['tmp_name']['error'] !== UPLOAD_ERR_OK) {
183
					\TYPO3\CMS\Core\Utility\GeneralUtility::sysLog('Error in uploaded file in t3lib_formmail: temporary file "' . $_FILES[$variableName]['tmp_name'] . '" ("' . $_FILES[$variableName]['name'] . '") Error code: ' . $_FILES[$variableName]['tmp_name']['error'], 'Core', \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR);
184
				}
185
				$theFile = \TYPO3\CMS\Core\Utility\GeneralUtility::upload_to_tempfile($_FILES[$variableName]['tmp_name']);
186
				$theName = $_FILES[$variableName]['name'];
187
188
				if ($theFile && file_exists($theFile)) {
					if (filesize($theFile) < $GLOBALS['TYPO3_CONF_VARS']['FE']['formmailMaxAttachmentSize']) {
189
						$this->mailMessage->attach(Swift_Attachment::fromPath($theFile)->setFilename($theName));
Kasper Skårhøj's avatar
Kasper Skårhøj committed
190
191
					}
				}
192
				$this->temporaryFiles[] = $theFile;
Kasper Skårhøj's avatar
Kasper Skårhøj committed
193
			}
194
			$from = $this->fromName ? array($this->fromAddress => $this->fromName) : array($this->fromAddress);
195
			$this->recipient = $this->parseAddresses($valueList['recipient']);
196
			$this->mailMessage->setSubject($this->subject)->setFrom($from)->setTo($this->recipient)->setPriority($this->priority);
197
			$replyTo = $this->replyToName ? array($this->replyToAddress => $this->replyToName) : array($this->replyToAddress);
198
			$this->mailMessage->setReplyTo($replyTo);
199
200
			$this->mailMessage->getHeaders()->addTextHeader('Organization', $this->organisation);
			if ($valueList['recipient_copy']) {
201
				$this->mailMessage->setCc($this->parseAddresses($valueList['recipient_copy']));
Kasper Skårhøj's avatar
Kasper Skårhøj committed
202
			}
203
			$this->mailMessage->setCharset($this->characterSet);
204
205
206
			// Ignore target encoding. This is handled automatically by Swift Mailer and overriding the defaults
			// is not worth the trouble
			// Log dirty header lines
207
			if ($this->dirtyHeaders) {
208
				\TYPO3\CMS\Core\Utility\GeneralUtility::sysLog('Possible misuse of t3lib_formmail: see TYPO3 devLog', 'Core', \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR);
209
				if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG']) {
210
					\TYPO3\CMS\Core\Utility\GeneralUtility::devLog('t3lib_formmail: ' . \TYPO3\CMS\Core\Utility\GeneralUtility::arrayToLogString($this->dirtyHeaders, '', 200), 'Core', 3);
211
212
				}
			}
Kasper Skårhøj's avatar
Kasper Skårhøj committed
213
214
215
		}
	}

216
217
218
	/**
	 * Checks string for suspicious characters
	 *
219
220
	 * @param string $string String to check
	 * @return string Valid or empty string
221
	 */
222
	protected function sanitizeHeaderString($string) {
223
		$pattern = '/[\\r\\n\\f\\e]/';
224
		if (preg_match($pattern, $string) > 0) {
225
226
227
228
229
			$this->dirtyHeaders[] = $string;
			$string = '';
		}
		return $string;
	}
230

231
232
233
234
235
236
237
238
239
240
241
242
	/**
	 * Parses mailbox headers and turns them into an array.
	 *
	 * Mailbox headers are a comma separated list of 'name <email@example.org' combinations or plain email addresses (or a mix
	 * of these).
	 * The resulting array has key-value pairs where the key is either a number (no display name in the mailbox header) and the
	 * value is the email address, or the key is the email address and the value is the display name.
	 *
	 * @param string $rawAddresses Comma separated list of email addresses (optionally with display name)
	 * @return array Parsed list of addresses.
	 */
	protected function parseAddresses($rawAddresses = '') {
243
244
		/** @var $addressParser \TYPO3\CMS\Core\Mail\Rfc822AddressesParser */
		$addressParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\Rfc822AddressesParser', $rawAddresses);
245
246
247
248
		$addresses = $addressParser->parseAddressList();
		$addressList = array();
		foreach ($addresses as $address) {
			if ($address->personal) {
249
				// Item with name found ( name <email@example.org> )
250
				$addressList[$address->mailbox . '@' . $address->host] = $address->personal;
251
			} else {
252
				// Item without name found ( email@example.org )
253
				$addressList[] = $address->mailbox . '@' . $address->host;
254
255
256
257
258
			}
		}
		return $addressList;
	}

259
260
261
262
263
264
	/**
	 * Sends the actual mail and handles autorespond message
	 *
	 * @return boolean
	 */
	public function sendTheMail() {
265
		// Sending the mail requires the recipient and message to be set.
266
267
268
269
		if (!$this->mailMessage->getTo() || !trim($this->mailMessage->getBody())) {
			return FALSE;
		}
		$this->mailMessage->send();
270
		// Auto response
271
272
273
		if ($this->autoRespondMessage) {
			$theParts = explode('/', $this->autoRespondMessage, 2);
			$theParts[0] = str_replace('###SUBJECT###', $this->subject, $theParts[0]);
274
275
			$theParts[1] = str_replace('/', LF, $theParts[1]);
			$theParts[1] = str_replace('###MESSAGE###', $this->plainContent, $theParts[1]);
276
277
278
			/** @var $autoRespondMail \TYPO3\CMS\Core\Mail\MailMessage */
			$autoRespondMail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
			$autoRespondMail->setTo($this->fromAddress)->setSubject($theParts[0])->setFrom($this->recipient)->setBody($theParts[1]);
279
280
281
282
283
284
285
286
287
288
			$autoRespondMail->send();
		}
		return $this->mailMessage->isSent();
	}

	/**
	 * Do some cleanup at the end (deleting attachment files)
	 */
	public function __destruct() {
		foreach ($this->temporaryFiles as $file) {
289
290
			if (\TYPO3\CMS\Core\Utility\GeneralUtility::isAllowedAbsPath($file) && \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($file, PATH_site . 'typo3temp/upload_temp_')) {
				\TYPO3\CMS\Core\Utility\GeneralUtility::unlink_tempfile($file);
291
			}
292
293
		}
	}
294

Kasper Skårhøj's avatar
Kasper Skårhøj committed
295
296
}

297
?>