[BUGFIX] Avoid logout when changing the encryption key
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / mod / class.tx_install_session.php
index 8c5c06f..8745c39 100644 (file)
 /**
  * Secure session handling for the install tool.
  *
- * @author     Ernesto Baschny <ernst@cron-it.de>
+ * @author Ernesto Baschny <ernst@cron-it.de>
  *
  * @package TYPO3
  * @subpackage tx_install
- *
- * @version $Id$
  */
 class tx_install_session {
 
@@ -47,11 +45,11 @@ class tx_install_session {
 
        /**
         * Path where to store our session files in typo3temp. %s will be
-        * non-guesseable.
+        * non-guessable.
         *
         * @var string
         */
-       private $sessionPath = 'sessions%s';
+       private $sessionPath = 'InstallToolSessions/%s';
 
        /**
         * the cookie to store the session ID of the install tool
@@ -89,19 +87,10 @@ class tx_install_session {
        public function __construct() {
                $this->typo3tempPath = PATH_site . 'typo3temp/';
 
-               // Start our PHP session early so that hasSession() works
+                       // Start our PHP session early so that hasSession() works
                $sessionSavePath = $this->getSessionSavePath();
-               if (!is_dir($sessionSavePath)) {
-                       if (!t3lib_div::mkdir($sessionSavePath)) {
-                               throw new Exception('<p><strong>Could not create session folder in typo3temp/.</strong></p><p>Make sure it is writeable!</p>');
-                       }
-                       t3lib_div::writeFile($sessionSavePath.'/.htaccess', 'Order deny, allow'."\n".'Deny from all'."\n");
-                       $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
-                       $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
-                       $indexContent .= '</HEAD></HTML>';
-                       t3lib_div::writeFile($sessionSavePath.'/index.html', $indexContent);
-               }
-               // Register our "save" session handler
+
+                       // Register our "save" session handler
                session_set_save_handler(
                        array($this, 'open'),
                        array($this, 'close'),
@@ -113,22 +102,22 @@ class tx_install_session {
                session_save_path($sessionSavePath);
                session_name($this->cookieName);
                ini_set('session.cookie_path', t3lib_div::getIndpEnv('TYPO3_SITE_PATH'));
-               // Always call the garbage collector to clean up stale session files
+                       // Always call the garbage collector to clean up stale session files
                ini_set('session.gc_probability', 100);
                ini_set('session.gc_divisor', 100);
                ini_set('session.gc_maxlifetime', $this->expireTimeInMinutes*2*60);
                if (version_compare(phpversion(), '5.2', '<')) {
                        ini_set('session.cookie_httponly', TRUE);
                }
-               if (ini_get('session.auto_start')) {
-                       $sessionCreationError = '<p><strong>Error: session.auto-start is enabled</strong></p>';
-                       $sessionCreationError .= '<p>The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:</p>';
+               if (t3lib_utility_PhpOptions::isSessionAutoStartEnabled()) {
+                       $sessionCreationError = 'Error: session.auto-start is enabled.<br />';
+                       $sessionCreationError .= 'The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:<br />';
                        $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
-                       throw new Exception($sessionCreationError);
-               } else if (defined('SID')) {
-                       $sessionCreationError = '<p><strong>Error: Session already started by session_start().</strong></p>';
-                       $sessionCreationError .= '<p>Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.</p>';
-                       throw new Exception($sessionCreationError);
+                       throw new RuntimeException($sessionCreationError, 1294587485);
+               } elseif (defined('SID')) {
+                       $sessionCreationError = 'Session already started by session_start().<br />';
+                       $sessionCreationError .= 'Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.';
+                       throw new RuntimeException($sessionCreationError, 1294587486);
                }
                session_start();
        }
@@ -137,13 +126,39 @@ class tx_install_session {
         * Returns the path where to store our session files
         */
        private function getSessionSavePath() {
-               return sprintf(
+               $sessionSavePath = sprintf(
                        $this->typo3tempPath . $this->sessionPath,
-                       md5(
+                       t3lib_div::hmac(
                                'session:' .
                                        $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']
                        )
                );
+               $this->ensureSessionSavePathExists($sessionSavePath);
+
+               return $sessionSavePath;
+       }
+
+       /**
+        * Create directories for the session save path
+        * and throw an exception if that fails.
+        *
+        * @param string $sessionSavePath The absolute path to the session files
+        * @throws RuntimeException
+        */
+       private function ensureSessionSavePathExists($sessionSavePath) {
+               if (!is_dir($sessionSavePath)) {
+                       try {
+                               t3lib_div::mkdir_deep($sessionSavePath);
+                       }
+                       catch (RuntimeException $exception) {
+                               throw new RuntimeException('Could not create session folder in typo3temp/. Make sure it is writeable!', 1294587484);
+                       }
+                       t3lib_div::writeFile($sessionSavePath . '/.htaccess', 'Order deny, allow' . "\n" . 'Deny from all' . "\n");
+                       $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
+                       $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
+                       $indexContent .= '</HEAD></HTML>';
+                       t3lib_div::writeFile($sessionSavePath . '/index.html', $indexContent);
+               }
        }
 
        /**
@@ -185,7 +200,7 @@ class tx_install_session {
        /**
         * Checks whether we already have an active session.
         *
-        * @return boolean true if there is an active session, false otherwise
+        * @return boolean TRUE if there is an active session, FALSE otherwise
         */
        public function hasSession() {
                return (isset($_SESSION['created']));
@@ -204,7 +219,7 @@ class tx_install_session {
         * Returns a session hash, which can only be calculated by the server.
         * Used to store our session files without exposing the session ID.
         *
-        * @param string An alternative session ID. Defaults to our current session ID
+        * @param string $sessionId An alternative session ID. Defaults to our current session ID
         *
         * @return string the session hash
         */
@@ -235,7 +250,7 @@ class tx_install_session {
        /**
         * Check if we have an already authorized session
         *
-        * @return boolean True if this session has been authorized before (by a correct password)
+        * @return boolean TRUE if this session has been authorized before (by a correct password)
         */
        public function isAuthorized() {
                if (!$_SESSION['authorized']) {
@@ -250,10 +265,10 @@ class tx_install_session {
 
        /**
         * Check if our session is expired.
-        * Useful only right after a false "isAuthorized" to see if this is the
+        * Useful only right after a FALSE "isAuthorized" to see if this is the
         * reason for not being authorized anymore.
         *
-        * @return boolean True if an authorized session exists, but is expired
+        * @return boolean TRUE if an authorized session exists, but is expired
         */
        public function isExpired() {
                if (!$_SESSION['authorized']) {
@@ -295,6 +310,7 @@ class tx_install_session {
        /**
         * Returns the file where to store our session data
         *
+        * @param string $id
         * @return string A filename
         */
        private function getSessionFile($id) {
@@ -307,6 +323,7 @@ class tx_install_session {
         *
         * @param string $savePath
         * @param string $sessionName
+        *
         * @return boolean
         */
        public function open($savePath, $sessionName) {
@@ -325,7 +342,7 @@ class tx_install_session {
        /**
         * Read session data. See @session_set_save_handler
         *
-        * @param string The session id
+        * @param string $id The session id
         *
         * @return string
         */
@@ -337,8 +354,8 @@ class tx_install_session {
        /**
         * Write session data. See @session_set_save_handler
         *
-        * @param string The session id
-        * @param string The data to be stored
+        * @param string $id The session id
+        * @param string $sessionData The data to be stored
         *
         * @return boolean
         */
@@ -350,7 +367,7 @@ class tx_install_session {
        /**
         * Destroys one session. See @session_set_save_handler
         *
-        * @param string The session id
+        * @param string $id The session id
         *
         * @return string
         */
@@ -362,7 +379,7 @@ class tx_install_session {
        /**
         * Garbage collect session info. See @session_set_save_handler
         *
-        * @param integer The setting of session.gc_maxlifetime
+        * @param integer $maxLifeTime The setting of session.gc_maxlifetime
         *
         * @return boolean
         */
@@ -380,10 +397,21 @@ class tx_install_session {
                return TRUE;
        }
 
+       /**
+        * Writes the session data at the end, to overcome a PHP APC bug.
+        *
+        * Writes the session data in a proper context that is not affected by the APC bug:
+        * http://pecl.php.net/bugs/bug.php?id=16721.
+        *
+        * This behaviour was introduced in #17511, where self::write() made use of t3lib_div
+        * which due to the APC bug throws a "Fatal error: Class 't3lib_div' not found"
+        * (and the session data is not saved). Calling session_write_close() at this point
+        * seems to be the most easy solution, according to PHP author.
+        *
+        * @return void
+        */
+       public function __destruct() {
+               session_write_close();
+       }
 }
-
-if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_session.php'])) {
-       include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_session.php']);
-}
-
 ?>
\ No newline at end of file