Adds check if delivered package matches required package
[GitHub/WoltLab/WCF.git] / wcfsetup / install.php
index 94a16960d0703e31e39f252877e313cc51f5be0a..abc233dfcaa05c6b1ba57730fca596a283ced31f 100644 (file)
@@ -3,10 +3,11 @@
  * This script tries to find the temp folder and unzip all setup files into.
  *
  * @author     Marcel Werk
- * @copyright  2001-2011 WoltLab GmbH
+ * @copyright  2001-2013 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
 // define constants
+define('INSTALL_SCRIPT', __FILE__);
 define('INSTALL_SCRIPT_DIR', dirname(__FILE__).'/');
 define('SETUP_FILE', INSTALL_SCRIPT_DIR . 'WCFSetup.tar.gz');
 define('NO_IMPORTS', 1);
@@ -20,9 +21,10 @@ set_error_handler('handleError', E_ALL);
 $neededFilesPattern = array(
        '!^setup/.*!',
        '!^install/files/acp/images/wcfLogo.*!',
-       '!^install/files/acp/style/.*!',
+       '!^install/files/acp/style/setup/.*!',
        '!^install/files/lib/data/.*!',
        '!^install/files/icon/.*!',
+       '!^install/files/font/.*!',
        '!^install/files/lib/system/.*!',
        '!^install/files/lib/util/.*!',
        '!^install/lang/.*!',
@@ -32,7 +34,7 @@ $neededFilesPattern = array(
 /**
  * WCF::handleException() calls the show method on exceptions that implement this interface.
  *
- * @package    com.woltlab.wcf.system.exception
+ * @package    com.woltlab.wcf
  * @author     Marcel Werk
  */
 interface IPrintableException {
@@ -45,7 +47,7 @@ interface IPrintableException {
 /**
  * A SystemException is thrown when an unexpected error occurs.
  *
- * @package    com.woltlab.wcf.system.exception
+ * @package    com.woltlab.wcf
  * @author     Marcel Werk
  */
 class SystemException extends \Exception implements IPrintableException {
@@ -68,7 +70,7 @@ class SystemException extends \Exception implements IPrintableException {
        /**
         * Returns the description of this exception.
         *
-        * @return      string
+        * @return      string
         */
        public function getDescription() {
                return $this->description;
@@ -163,7 +165,7 @@ function __autoload($className) {
                array_shift($namespaces);
                
                $className = implode('/', $namespaces);
-               $classPath = TMP_DIR . 'install/files/lib/' . $className  . '.class.php';
+               $classPath = TMP_DIR . 'install/files/lib/' . $className . '.class.php';
                if (file_exists($classPath)) {
                        require_once($classPath);
                }
@@ -216,55 +218,139 @@ function handleError($errorNo, $message, $filename, $lineNo) {
 /**
  * BasicFileUtil contains file-related functions.
  *
- * @package    com.woltlab.wcf.util
+ * @package    com.woltlab.wcf
  * @author     Marcel Werk
  */
 class BasicFileUtil {
+       /**
+        * chmod mode
+        * @var integer
+        */
+       protected static $mode = null;
+       
        /**
         * Tries to find the temp folder.
         *
         * @return      string
         */
        public static function getTempFolder() {
-               $tmpDirName = TMP_FILE_PREFIX.'/';
-               
                // use tmp folder in document root by default
                if (!empty($_SERVER['DOCUMENT_ROOT'])) {
-                       if (!@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName)) {
-                               @mkdir($_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName, 0777, true);
-                               @chmod($_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName, 0777);
+                       if (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false) {
+                               // strato bugfix
+                               // create tmp folder in document root automatically
+                               if (!@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
+                                       @mkdir($_SERVER['DOCUMENT_ROOT'].'/tmp/', 0777);
+                                       try {
+                                               self::makeWritable($_SERVER['DOCUMENT_ROOT'].'/tmp/');
+                                       }
+                                       catch (SystemException $e) {}
+                               }
                        }
-                       
-                       if (@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName) && @is_writable($_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName)) {
-                               return $_SERVER['DOCUMENT_ROOT'].'/tmp/'.$tmpDirName;
+                       if (@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp') && @is_writable($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
+                               return $_SERVER['DOCUMENT_ROOT'].'/tmp/';
                        }
                }
+       
+               if (isset($_ENV['TMP']) && @is_writable($_ENV['TMP'])) {
+                       return $_ENV['TMP'] . '/';
+               }
+               if (isset($_ENV['TEMP']) && @is_writable($_ENV['TEMP'])) {
+                       return $_ENV['TEMP'] . '/';
+               }
+               if (isset($_ENV['TMPDIR']) && @is_writable($_ENV['TMPDIR'])) {
+                       return $_ENV['TMPDIR'] . '/';
+               }
+       
+               if (($path = ini_get('upload_tmp_dir')) && @is_writable($path)) {
+                       return $path . '/';
+               }
+               if (@file_exists('/tmp/') && @is_writable('/tmp/')) {
+                       return '/tmp/';
+               }
+               if (function_exists('session_save_path') && ($path = session_save_path()) && @is_writable($path)) {
+                       return $path . '/';
+               }
+       
+               $path = INSTALL_SCRIPT_DIR.'tmp/';
+               if (@file_exists($path) && @is_writable($path)) {
+                       return $path;
+               }
+               else {
+                       throw new SystemException('There is no access to the system temporary folder due to an unknown reason and no user specific temporary folder exists in '.INSTALL_SCRIPT_DIR.'! This is a misconfiguration of your webserver software! Please create a folder called '.$path.' using your favorite ftp program, make it writable and then retry this installation.');
+               }
+       }
+       
+       /**
+        * Returns the temp folder for the installation.
+        *
+        * @return      string
+        */
+       public static function getInstallTempFolder() {
+               $dir = self::getTempFolder() . TMP_FILE_PREFIX . '/';
+               @mkdir($dir);
+               self::makeWritable($dir);
+               
+               return $dir;
+       }
+       
+       /**
+        * Tries to make a file or directory writable. It starts of with the least
+        * permissions and goes up until 0666 for files and 0777 for directories.
+        *
+        * @param       string          $filename
+        */
+       public static function makeWritable($filename) {
+               if (!file_exists($filename)) {
+                       return;
+               }
                
-               foreach (array('TMP', 'TEMP', 'TMPDIR') as $tmpDir) {
-                       if (isset($_ENV[$tmpDir]) && @is_writable($_ENV[$tmpDir])) {
-                               $dir = $_ENV[$tmpDir] . '/' . $tmpDirName;
-                               @mkdir($dir, 0777);
-                               @chmod($dir, 0777);
+               // determine mode
+               if (self::$mode === null) {
+                       // do not use PHP_OS here, as this represents the system it was built on != running on
+                       if (strpos(php_uname(), 'Windows') !== false) {
+                               self::$mode = 0777;
+                       }
+                       else {
+                               clearstatcache();
+                               
+                               self::$mode = 0666;
                                
-                               if (@file_exists($dir) && @is_writable($dir)) {
-                                       return $dir;
+                               $tmpFilename = '__permissions_'.sha1(time()).'.txt';
+                               @touch($tmpFilename);
+                               
+                               // create a new file and check the file owner, if it is the same
+                               // as this file (uploaded through FTP), we can safely grant write
+                               // permissions exclusively to the owner rather than everyone
+                               if (file_exists($tmpFilename)) {
+                                       $scriptOwner = fileowner(__FILE__);
+                                       $fileOwner = fileowner($tmpFilename);
+                                       
+                                       if ($scriptOwner === $fileOwner) {
+                                               self::$mode = 0644;
+                                       }
+                                       
+                                       @unlink($tmpFilename);
                                }
                        }
                }
                
-               $dir = INSTALL_SCRIPT_DIR . 'tmp/' . $tmpDirName;
-               @mkdir($dir, 0777);
-               @chmod($dir, 0777);
-               
-               if (!@file_exists($dir) || !@is_writable($dir)) {
-                       $tmpDir = explode('/', $dir);
-                       array_pop($tmpDir);
-                       $dir = implode('/', $tmpDir);
-                       
-                       throw new SystemException('There is no access to the system temporary folder due to an unknown reason and no user specific temporary folder exists in '.INSTALL_SCRIPT_DIR.'! This is a misconfiguration of your webserver software! Please create a folder called '.$dir.' using your favorite ftp program, make it writable and then retry this installation.');
+               $startIndex = 0;
+               if (is_dir($filename)) {
+                       if (self::$mode == 0644) {
+                               @chmod($filename, 0755);
+                       }
+                       else {
+                               @chmod($filename, 0777);
+                       }
+               }
+               else {
+                       @chmod($filename, self::$mode);
                }
                
-               return $dir;
+               if (!is_writable($filename)) {
+                       throw new SystemException("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider.");
+               }
        }
 }
 
@@ -292,10 +378,9 @@ class Tar {
         * Creates a new Tar object.
         * archiveName must be tarball or gzipped tarball
         *
-        * @param       string          $archiveName
+        * @param       string          $archiveName
         */
        public function __construct($archiveName) {
-               $match = array();
                if (!is_file($archiveName)) {
                        throw new SystemException("unable to find tar archive '".$archiveName."'");
                }
@@ -347,7 +432,7 @@ class Tar {
        /**
         * Returns the table of contents (TOC) list for this tar archive.
         *
-        * @return      array           list of content
+        * @return      array           list of content
         */
        public function getContentList() {
                if (!$this->read) {
@@ -361,8 +446,8 @@ class Tar {
         * Returns an associative array with information
         * about a specific file in the archive.
         *
-        * @param       mixed   $fileindex      index or name of the requested file
-        * @return      array   $fileInfo
+        * @param       mixed   $fileindex      index or name of the requested file
+        * @return      array   $fileInfo
         */
        public function getFileInfo($fileIndex) {
                if (!is_int($fileIndex)) {
@@ -370,7 +455,7 @@ class Tar {
                }
                
                if (!isset($this->contentList[$fileIndex])) {
-                       throw new SystemException("Tar: could find file '$index' in archive");
+                       throw new SystemException("Tar: could find file '".$fileIndex."' in archive");
                }
                return $this->contentList[$fileIndex];
        }
@@ -380,8 +465,8 @@ class Tar {
         * and returns the numeric fileindex.
         * Returns false if not found.
         *
-        * @param       string          $filename
-        * @return      integer                         index of the requested file
+        * @param       string          $filename
+        * @return      integer                 index of the requested file
         */
        public function getIndexByFilename($filename) {
                foreach ($this->contentList as $index => $file) {
@@ -396,8 +481,8 @@ class Tar {
         * Extracts a specific file and returns the content as string.
         * Returns false if extraction failed.
         *
-        * @param       mixed           $index          index or name of the requested file
-        * @return      string                          content of the requested file
+        * @param       mixed           $index          index or name of the requested file
+        * @return      string                          content of the requested file
         */
        public function extractToString($index) {
                if (!$this->read) {
@@ -432,9 +517,9 @@ class Tar {
         * Extracts a specific file and writes it's content
         * to the file specified with $destination.
         *
-        * @param       mixed           $index          index or name of the requested file
-        * @param       string          $destination
-        * @return      boolean         $success
+        * @param       mixed           $index          index or name of the requested file
+        * @param       string          $destination
+        * @return      boolean $success
         */
        public function extract($index, $destination) {
                if (!$this->read) {
@@ -465,12 +550,7 @@ class Tar {
                }
                
                $targetFile->close();
-               if (function_exists('apache_get_version') || !@$targetFile->is_writable()) {
-                       @$targetFile->chmod(0777);
-               }
-               else {
-                       @$targetFile->chmod(0755);
-               }
+               BasicFileUtil::makeWritable($destination);
                
                if ($header['mtime']) {
                        @$targetFile->touch($header['mtime']);
@@ -511,8 +591,8 @@ class Tar {
        /**
         * Unpacks file header for one file entry.
         *
-        * @param       string          $binaryData
-        * @return      array           $fileheader
+        * @param       string          $binaryData
+        * @return      array           $fileheader
         */
        protected function readHeader($binaryData) {
                if (strlen($binaryData) != 512) {
@@ -537,7 +617,14 @@ class Tar {
 
                // Extract the values
                //$data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $binaryData);
-               $data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $binaryData);
+               if (version_compare(PHP_VERSION, '5.5.0-dev', '>=')) {
+                       $format = 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor/Z155prefix';
+               }
+               else {
+                       $format = 'a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix';
+               }
+               
+               $data = unpack($format, $binaryData);
                
                // Extract the properties
                $header['checksum'] = octDec(trim($data['checksum']));
@@ -592,8 +679,8 @@ class File {
        /**
         * Opens a new file.
         *
-        * @param       string          $filename
-        * @param       string          $mode
+        * @param       string          $filename
+        * @param       string          $mode
         */
        public function __construct($filename, $mode = 'wb') {
                $this->filename = $filename;
@@ -607,17 +694,17 @@ class File {
         * Calls the specified function on the open file.
         * Do not call this function directly. Use $file->write('') instead.
         *
-        * @param       string          $function
-        * @param       array           $arguments
+        * @param       string          $function
+        * @param       array           $arguments
         */
        public function __call($function, $arguments) {
                if (function_exists('f' . $function)) {
                        array_unshift($arguments, $this->resource);
-                       return call_user_func_array('f' . $function, $arguments);
+                       return call_user_func_array('f' . $function, $arguments);
                }
                else if (function_exists($function)) {
                        array_unshift($arguments, $this->filename);
-                       return call_user_func_array($function, $arguments);
+                       return call_user_func_array($function, $arguments);
                }
                else {
                        throw new SystemException('Can not call file method ' . $function);
@@ -634,8 +721,8 @@ class ZipFile extends File {
        /**
         * Opens a new zipped file.
         *
-        * @param       string          $filename
-        * @param       string          $mode
+        * @param       string          $filename
+        * @param       string          $mode
         */
        public function __construct($filename, $mode = 'wb') {
                $this->filename = $filename;
@@ -651,17 +738,17 @@ class ZipFile extends File {
        /**
         * Calls the specified function on the open file.
         *
-        * @param       string          $function
-        * @param       array           $arguments
+        * @param       string          $function
+        * @param       array           $arguments
         */
        public function __call($function, $arguments) {
                if (function_exists('gz' . $function)) {
                        array_unshift($arguments, $this->resource);
-                       return call_user_func_array('gz' . $function, $arguments);
+                       return call_user_func_array('gz' . $function, $arguments);
                }
                else if (function_exists($function)) {
                        array_unshift($arguments, $this->filename);
-                       return call_user_func_array($function, $arguments);
+                       return call_user_func_array($function, $arguments);
                }
                else {
                        throw new SystemException('Can not call method ' . $function);
@@ -706,7 +793,7 @@ else {
 define('TMP_FILE_PREFIX', $prefix);
 
 // try to find the temp folder
-define('TMP_DIR', BasicFileUtil::getTempFolder());
+define('TMP_DIR', BasicFileUtil::getInstallTempFolder());
 
 /**
  * Reads a file resource from temp folder.
@@ -715,7 +802,7 @@ define('TMP_DIR', BasicFileUtil::getTempFolder());
  * @param      string          $directory
  */
 function readFileResource($key, $directory) {
-       if (preg_match('~[\w\-]+\.(css|jpg|png|svg)~', $_GET[$key], $match)) {
+       if (preg_match('~[\w\-]+\.(css|jpg|png|svg|eot|woff|ttf)~', $_GET[$key], $match)) {
                switch ($match[1]) {
                        case 'css':
                                header('Content-Type: text/css');
@@ -732,8 +819,24 @@ function readFileResource($key, $directory) {
                        case 'svg':
                                header('Content-Type: image/svg+xml');
                        break;
+                       
+                       case 'eot':
+                               header('Content-Type: application/vnd.ms-fontobject');
+                       break;
+                               
+                       case 'woff':
+                               header('Content-Type: application/font-woff');
+                       break;
+                                       
+                       case 'ttf':
+                               header('Content-Type: application/octet-stream');
+                       break;
                }
                
+               header('Expires: '.gmdate('D, d M Y H:i:s', time() + 3600).' GMT');
+               header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT');
+               header('Cache-Control: public, max-age=3600');
+               
                readfile($directory . $_GET[$key]);
        }
        exit;
@@ -749,7 +852,11 @@ if (isset($_GET['showIcon'])) {
 }
 // show css from temp folder
 if (isset($_GET['showCSS'])) {
-       readFileResource('showCSS', TMP_DIR . 'install/files/acp/style/');
+       readFileResource('showCSS', TMP_DIR . 'install/files/acp/style/setup/');
+}
+// show fonts from temp folder
+if (isset($_GET['showFont'])) {
+       readFileResource('showFont', TMP_DIR . 'install/files/font/');
 }
 
 // check whether setup files are already unzipped
@@ -757,7 +864,7 @@ if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) {
        // try to unzip all setup files into temp folder
        $tar = new Tar(SETUP_FILE);
        $contentList = $tar->getContentList();
-       if (!count($contentList)) {
+       if (empty($contentList)) {
                throw new SystemException("Can not unpack 'WCFSetup.tar.gz'. File is probably broken.");
        }
        
@@ -768,7 +875,7 @@ if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) {
                                $dir = TMP_DIR . dirname($file['filename']);
                                if (!@is_dir($dir)) {
                                        @mkdir($dir, 0777, true);
-                                       @chmod($dir, 0777);
+                                       BasicFileUtil::makeWritable($dir);
                                }
                                
                                $tar->extract($file['index'], TMP_DIR . $file['filename']);
@@ -779,10 +886,10 @@ if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) {
        
        // create cache folders
        @mkdir(TMP_DIR . 'setup/lang/cache/', 0777);
-       @chmod(TMP_DIR . 'setup/lang/cache/', 0777);
+       BasicFileUtil::makeWritable(TMP_DIR . 'setup/lang/cache/');
        
        @mkdir(TMP_DIR . 'setup/template/compiled/', 0777);
-       @chmod(TMP_DIR . 'setup/template/compiled/', 0777);
+       BasicFileUtil::makeWritable(TMP_DIR . 'setup/template/compiled/');
 }
 
 if (!class_exists('wcf\system\WCFSetup')) {