[TASK] Fix broken references to exceptions in global namespace
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Session.php
1 <?php
2 namespace TYPO3\CMS\Install;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2009-2011 Ernesto Baschny <ernst@cron-it.de>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Secure session handling for the install tool.
31 *
32 * @author Ernesto Baschny <ernst@cron-it.de>
33 * @package TYPO3
34 * @subpackage tx_install
35 */
36 class Session {
37
38 /**
39 * The path to our typo3temp (where we can write our sessions). Set in the
40 * constructor.
41 *
42 * @var string
43 */
44 private $typo3tempPath;
45
46 /**
47 * Path where to store our session files in typo3temp. %s will be
48 * non-guessable.
49 *
50 * @var string
51 */
52 private $sessionPath = 'InstallToolSessions/%s';
53
54 /**
55 * the cookie to store the session ID of the install tool
56 *
57 * @var string
58 */
59 private $cookieName = 'Typo3InstallTool';
60
61 /**
62 * time (minutes) to expire an ununsed session
63 *
64 * @var integer
65 */
66 private $expireTimeInMinutes = 60;
67
68 /**
69 * time (minutes) to generate a new session id for our current session
70 *
71 * @var integer
72 */
73 private $regenerateSessionIdTime = 5;
74
75 /**
76 * part of the referer when the install tool has been called from the backend
77 *
78 * @var string
79 */
80 private $backendFile = 'backend.php';
81
82 /**
83 * Constructor. Starts PHP session handling in our own private store
84 *
85 * Side-effect: might set a cookie, so must be called before any other output.
86 */
87 public function __construct() {
88 $this->typo3tempPath = PATH_site . 'typo3temp/';
89 // Start our PHP session early so that hasSession() works
90 $sessionSavePath = $this->getSessionSavePath();
91 // Register our "save" session handler
92 session_set_save_handler(array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc'));
93 session_save_path($sessionSavePath);
94 session_name($this->cookieName);
95 ini_set('session.cookie_path', \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
96 // Always call the garbage collector to clean up stale session files
97 ini_set('session.gc_probability', 100);
98 ini_set('session.gc_divisor', 100);
99 ini_set('session.gc_maxlifetime', ($this->expireTimeInMinutes * 2) * 60);
100 if (version_compare(phpversion(), '5.2', '<')) {
101 ini_set('session.cookie_httponly', TRUE);
102 }
103 if (\TYPO3\CMS\Core\Utility\PhpOptionsUtility::isSessionAutoStartEnabled()) {
104 $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
105 $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
106 $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
107 throw new \RuntimeException($sessionCreationError, 1294587485);
108 } elseif (defined('SID')) {
109 $sessionCreationError = 'Session already started by session_start().<br />';
110 $sessionCreationError .= 'Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.';
111 throw new \RuntimeException($sessionCreationError, 1294587486);
112 }
113 session_start();
114 }
115
116 /**
117 * Returns the path where to store our session files
118 */
119 private function getSessionSavePath() {
120 $sessionSavePath = sprintf($this->typo3tempPath . $this->sessionPath, \TYPO3\CMS\Core\Utility\GeneralUtility::hmac('session:' . $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']));
121 $this->ensureSessionSavePathExists($sessionSavePath);
122 return $sessionSavePath;
123 }
124
125 /**
126 * Create directories for the session save path
127 * and throw an exception if that fails.
128 *
129 * @param string $sessionSavePath The absolute path to the session files
130 * @throws \RuntimeException
131 */
132 private function ensureSessionSavePathExists($sessionSavePath) {
133 if (!is_dir($sessionSavePath)) {
134 try {
135 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($sessionSavePath);
136 } catch (\RuntimeException $exception) {
137 throw new \RuntimeException('Could not create session folder in typo3temp/. Make sure it is writeable!', 1294587484);
138 }
139 \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($sessionSavePath . '/.htaccess', (('Order deny, allow' . '
140 ') . 'Deny from all') . '
141 ');
142 $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
143 $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
144 $indexContent .= '</HEAD></HTML>';
145 \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($sessionSavePath . '/index.html', $indexContent);
146 }
147 }
148
149 /**
150 * Starts a new session
151 *
152 * @return string The session ID
153 */
154 public function startSession() {
155 $_SESSION['created'] = time();
156 // Be sure to use our own session id, so create a new one
157 return $this->renewSession();
158 }
159
160 /**
161 * Destroys a session
162 */
163 public function destroySession() {
164 session_destroy();
165 }
166
167 /**
168 * Generates a new session ID and sends it to the client.
169 *
170 * Also moves session information from the old session to the new one
171 * (in PHP 5.1 or later)
172 *
173 * @return string the new session ID
174 */
175 private function renewSession() {
176 if (version_compare(phpversion(), '5.1', '<')) {
177 session_regenerate_id(TRUE);
178 } else {
179 session_regenerate_id();
180 }
181 return session_id();
182 }
183
184 /**
185 * Checks whether we already have an active session.
186 *
187 * @return boolean TRUE if there is an active session, FALSE otherwise
188 */
189 public function hasSession() {
190 return isset($_SESSION['created']);
191 }
192
193 /**
194 * Returns the session ID of the running session.
195 *
196 * @return string the session ID
197 */
198 public function getSessionId() {
199 return session_id();
200 }
201
202 /**
203 * Returns a session hash, which can only be calculated by the server.
204 * Used to store our session files without exposing the session ID.
205 *
206 * @param string $sessionId An alternative session ID. Defaults to our current session ID
207 * @return string the session hash
208 */
209 private function getSessionHash($sessionId = '') {
210 if (!$sessionId) {
211 $sessionId = $this->getSessionId();
212 }
213 return md5(($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'] . '|') . $sessionId);
214 }
215
216 /**
217 * Marks this session as an "authorized" one (login successful).
218 * Should only be called if:
219 * a) we have a valid session running
220 * b) the "password" or some other authorization mechanism really matched
221 *
222 * @return void
223 */
224 public function setAuthorized() {
225 $_SESSION['authorized'] = TRUE;
226 $_SESSION['lastSessionId'] = time();
227 $_SESSION['tstamp'] = time();
228 $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
229 // Renew the session id to avoid session fixation
230 $this->renewSession();
231 }
232
233 /**
234 * Check if we have an already authorized session
235 *
236 * @return boolean TRUE if this session has been authorized before (by a correct password)
237 */
238 public function isAuthorized() {
239 if (!$_SESSION['authorized']) {
240 return FALSE;
241 }
242 if ($_SESSION['expires'] < time()) {
243 // This session has already expired
244 return FALSE;
245 }
246 return TRUE;
247 }
248
249 /**
250 * Check if our session is expired.
251 * Useful only right after a FALSE "isAuthorized" to see if this is the
252 * reason for not being authorized anymore.
253 *
254 * @return boolean TRUE if an authorized session exists, but is expired
255 */
256 public function isExpired() {
257 if (!$_SESSION['authorized']) {
258 // Session never existed, means it is not "expired"
259 return FALSE;
260 }
261 if ($_SESSION['expires'] < time()) {
262 // This session was authorized before, but has expired
263 return TRUE;
264 }
265 return FALSE;
266 }
267
268 /**
269 * Refreshes our session information, rising the expire time.
270 * Also generates a new session ID every 5 minutes to minimize the risk of
271 * session hijacking.
272 *
273 * @return void
274 */
275 public function refreshSession() {
276 $_SESSION['tstamp'] = time();
277 $_SESSION['expires'] = time() + $this->expireTimeInMinutes * 60;
278 if (time() > $_SESSION['lastSessionId'] + $this->regenerateSessionIdTime * 60) {
279 // Renew our session ID
280 $_SESSION['lastSessionId'] = time();
281 $this->renewSession();
282 }
283 }
284
285 /*************************
286 *
287 * PHP session handling with "secure" session files (hashed session id)
288 * see http://www.php.net/manual/en/function.session-set-save-handler.php
289 *
290 *************************/
291 /**
292 * Returns the file where to store our session data
293 *
294 * @param string $id
295 * @return string A filename
296 */
297 private function getSessionFile($id) {
298 $sessionSavePath = $this->getSessionSavePath();
299 return ($sessionSavePath . '/hash_') . $this->getSessionHash($id);
300 }
301
302 /**
303 * Open function. See @session_set_save_handler
304 *
305 * @param string $savePath
306 * @param string $sessionName
307 * @return boolean
308 */
309 public function open($savePath, $sessionName) {
310 return TRUE;
311 }
312
313 /**
314 * Close function. See @session_set_save_handler
315 *
316 * @return boolean
317 */
318 public function close() {
319 return TRUE;
320 }
321
322 /**
323 * Read session data. See @session_set_save_handler
324 *
325 * @param string $id The session id
326 * @return string
327 */
328 public function read($id) {
329 $sessionFile = $this->getSessionFile($id);
330 return (string) (@file_get_contents($sessionFile));
331 }
332
333 /**
334 * Write session data. See @session_set_save_handler
335 *
336 * @param string $id The session id
337 * @param string $sessionData The data to be stored
338 * @return boolean
339 */
340 public function write($id, $sessionData) {
341 $sessionFile = $this->getSessionFile($id);
342 return \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($sessionFile, $sessionData);
343 }
344
345 /**
346 * Destroys one session. See @session_set_save_handler
347 *
348 * @param string $id The session id
349 * @return string
350 */
351 public function destroy($id) {
352 $sessionFile = $this->getSessionFile($id);
353 return @unlink($sessionFile);
354 }
355
356 /**
357 * Garbage collect session info. See @session_set_save_handler
358 *
359 * @param integer $maxLifeTime The setting of session.gc_maxlifetime
360 * @return boolean
361 */
362 public function gc($maxLifeTime) {
363 $sessionSavePath = $this->getSessionSavePath();
364 $files = glob($sessionSavePath . '/hash_*');
365 if (!is_array($files)) {
366 return TRUE;
367 }
368 foreach ($files as $filename) {
369 if (filemtime($filename) + $this->expireTimeInMinutes * 60 < time()) {
370 @unlink($filename);
371 }
372 }
373 return TRUE;
374 }
375
376 /**
377 * Writes the session data at the end, to overcome a PHP APC bug.
378 *
379 * Writes the session data in a proper context that is not affected by the APC bug:
380 * http://pecl.php.net/bugs/bug.php?id=16721.
381 *
382 * This behaviour was introduced in #17511, where self::write() made use of t3lib_div
383 * which due to the APC bug throws a "Fatal error: Class 't3lib_div' not found"
384 * (and the session data is not saved). Calling session_write_close() at this point
385 * seems to be the most easy solution, according to PHP author.
386 *
387 * @return void
388 */
389 public function __destruct() {
390 session_write_close();
391 }
392
393 }
394
395
396 ?>