3 * This script tries to find the temp folder and unzip all setup files into.
6 * @copyright 2001-2019 WoltLab GmbH
7 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
9 // @codingStandardsIgnoreFile
11 define('INSTALL_SCRIPT', __FILE__
);
12 define('INSTALL_SCRIPT_DIR', dirname(__FILE__
).'/');
13 define('SETUP_FILE', INSTALL_SCRIPT_DIR
. 'WCFSetup.tar.gz');
14 define('NO_IMPORTS', 1);
16 // set exception handler
17 set_exception_handler('handleException');
18 // set php error handler
19 set_error_handler('handleError', E_ALL
);
21 // define list of needed file
22 $neededFilesPattern = [
24 '!^install/files/acp/images/woltlabSuite.*!',
25 '!^install/files/acp/style/setup/.*!',
26 '!^install/files/lib/data/.*!',
27 '!^install/files/icon/.*!',
28 '!^install/files/font/.*!',
29 '!^install/files/lib/system/.*!',
30 '!^install/files/lib/util/.*!',
32 '!^install/packages/.*!'];
34 // define needed functions and classes
35 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
37 * WCF::handleException() calls the show method on exceptions that implement this interface.
39 * @package com.woltlab.wcf
42 interface IPrintableException
{
43 public function show();
46 // define needed classes
48 // SystemException, PrintableException, BasicFileUtil, Tar, File, ZipFile
49 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
51 * A SystemException is thrown when an unexpected error occurs.
53 * @package com.woltlab.wcf
56 class SystemException
extends \Exception
implements IPrintableException
{
57 protected $description;
58 protected $information = '';
59 protected $functions = '';
62 * Creates a new SystemException.
64 * @param string $message error message
65 * @param integer $code error code
66 * @param string $description description of the error
67 * @param \Exception $previous repacked Exception
69 public function __construct($message = '', $code = 0, $description = '', \Exception
$previous = null) {
70 parent
::__construct((string) $message, (int) $code, $previous);
71 $this->description
= $description;
75 * Returns the description of this exception.
79 public function getDescription() {
80 return $this->description
;
84 * Prints this exception.
85 * This method is called by WCF::handleException().
87 public function show() {
89 * A notice on the HTML used below:
91 * It might appear a bit weird to use <p> all over the place where semantically
92 * other elements would fit in way better. The reason behind this is that we avoid
93 * inheriting unwanted styles (e.g. exception displayed in an overlay) and that
94 * the output needs to be properly readable when copied & pasted somewhere.
96 * Besides the visual appearance, the output was built to provide a maximum of
97 * compatibility and readability when pasted somewhere else, e.g. a WYSIWYG editor
98 * without the potential of messing up the formatting and thus harming the readability.
103 <title
>Fatal Error
: <?php
echo htmlentities($this->getMessage()); ?
></title
>
104 <meta charset
="utf-8">
105 <meta name
="viewport" content
="width=device-width, initial-scale=1">
112 .exceptionContainer
{
113 box
-sizing
: border
-box
;
114 font
-family
: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica
, Arial
, sans
-serif
;
116 padding
-bottom
: 20px
;
119 .exceptionContainer
* {
128 background
-color
: rgb(58, 109, 156);
138 .exceptionErrorCode
{
143 .exceptionErrorCode
.exceptionInlineCode
{
144 background
-color
: rgb(43, 79, 113);
147 font
-family
: monospace
;
153 border
-bottom
: 1px solid
rgb(238, 238, 238);
154 color
: rgb(44, 62, 80);
158 padding
-bottom
: 10px
;
161 .exceptionContainer
> .exceptionBoundary
{
165 .exceptionText
.exceptionInlineCodeWrapper
{
166 border
: 1px solid
rgb(169, 169, 169);
171 .exceptionText
.exceptionInlineCode
{
172 font
-family
: monospace
;
176 .exceptionFieldTitle
{
177 color
: rgb(59, 109, 169);
180 .exceptionFieldTitle
.exceptionColon
{
181 /* hide colon in browser, but will be visible after copy & paste */
185 .exceptionFieldValue
{
190 .exceptionSystemInformation
,
191 .exceptionErrorDetails
,
192 .exceptionStacktrace
{
193 list-style
-type
: none
;
196 .exceptionSystemInformation
> li
:not(:first
-child
),
197 .exceptionErrorDetails
> li
:not(:first
-child
) {
201 .exceptionStacktrace
{
205 padding
-bottom
: 20px
;
208 .exceptionStacktraceFile
,
209 .exceptionStacktraceFile span
,
210 .exceptionStacktraceCall
,
211 .exceptionStacktraceCall span
{
212 font
-family
: monospace
!important
;
213 white
-space
: nowrap
!important
;
216 .exceptionStacktraceCall +
.exceptionStacktraceFile
{
220 .exceptionStacktraceCall
{
224 .exceptionStacktraceCall
,
225 .exceptionStacktraceCall span
{
226 color
: rgb(102, 102, 102) !important
;
227 font
-size
: 13px
!important
;
231 @media
(max
-width
: 767px
) {
237 .exceptionText
.exceptionInlineCodeWrapper
{
238 display
: inline
-block
;
242 .exceptionErrorCode
.exceptionInlineCode
{
249 @media
(min
-width
: 768px
) {
257 .exceptionSystemInformation
{
262 .exceptionSystemInformation1
,
263 .exceptionSystemInformation3
,
264 .exceptionSystemInformation5
{
266 margin
: 0 0 10px
0 !important
;
269 .exceptionSystemInformation2
,
270 .exceptionSystemInformation4
,
271 .exceptionSystemInformation6
{
272 flex
: 0 0 calc(100%
- 210px
);
273 margin
: 0 0 10px
10px
!important
;
274 max
-width
: calc(100%
- 210px
);
277 .exceptionSystemInformation1
{ order
: 1; }
278 .exceptionSystemInformation2
{ order
: 2; }
279 .exceptionSystemInformation3
{ order
: 3; }
280 .exceptionSystemInformation4
{ order
: 4; }
281 .exceptionSystemInformation5
{ order
: 5; }
282 .exceptionSystemInformation6
{ order
: 6; }
284 .exceptionSystemInformation
.exceptionFieldValue
{
286 text
-overflow
: ellipsis
;
292 <body
class="exceptionBody">
293 <div
class="exceptionContainer">
294 <div
class="exceptionHeader">
295 <div
class="exceptionBoundary">
296 <p
class="exceptionTitle">An error has occurred
</p
>
300 <div
class="exceptionBoundary">
301 <p
class="exceptionSubtitle">System Information
</p
>
302 <ul
class="exceptionSystemInformation">
303 <li
class="exceptionSystemInformation1">
304 <p
class="exceptionFieldTitle">PHP Version
<span
class="exceptionColon">:</span
></p
>
305 <p
class="exceptionFieldValue"><?php
echo htmlentities(phpversion()); ?
></p
>
307 <li
class="exceptionSystemInformation3">
308 <p
class="exceptionFieldTitle">WoltLab Suite Core
<span
class="exceptionColon">:</span
></p
>
309 <p
class="exceptionFieldValue">5.2</p
>
311 <li
class="exceptionSystemInformation5">
312 <p
class="exceptionFieldTitle">Peak Memory Usage
<span
class="exceptionColon">:</span
></p
>
313 <p
class="exceptionFieldValue"><?php
echo round(memory_get_peak_usage() / 1024 / 1024, 3); ?
>/<?php
echo ini_get('memory_limit'); ?
></p
>
315 <li
class="exceptionSystemInformation2">
316 <p
class="exceptionFieldTitle">Request URI
<span
class="exceptionColon">:</span
></p
>
317 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['REQUEST_URI'])) echo htmlentities($_SERVER['REQUEST_URI']); ?
></p
>
319 <li
class="exceptionSystemInformation4">
320 <p
class="exceptionFieldTitle">Referrer
<span
class="exceptionColon">:</span
></p
>
321 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['HTTP_REFERER'])) echo htmlentities($_SERVER['HTTP_REFERER']); ?
></p
>
323 <li
class="exceptionSystemInformation6">
324 <p
class="exceptionFieldTitle">User Agent
<span
class="exceptionColon">:</span
></p
>
325 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['HTTP_USER_AGENT'])) echo htmlentities($_SERVER['HTTP_USER_AGENT']); ?
></p
>
334 $trace = $e->getTrace();
335 if (isset($trace[0]['function']) && $trace[0]['function'] === 'handleException') {
336 // ignore repacked exception
341 <div
class="exceptionBoundary">
342 <p
class="exceptionSubtitle"><?php
if (!$e->getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?
>Error
</p
>
343 <?php
if (($e instanceof SystemException ||
$e instanceof \wcf\system\exception\SystemException
) && $e->getDescription()) { ?
>
344 <p
class="exceptionText"><?php
echo $e->getDescription(); ?
></p
>
346 <ul
class="exceptionErrorDetails">
348 <p
class="exceptionFieldTitle">Error Type
<span
class="exceptionColon">:</span
></p
>
349 <p
class="exceptionFieldValue"><?php
echo htmlentities(get_class($e)); ?
></p
>
352 <p
class="exceptionFieldTitle">Error Message
<span
class="exceptionColon">:</span
></p
>
353 <p
class="exceptionFieldValue"><?php
echo htmlentities($e->getMessage()); ?
></p
>
355 <?php
if ($e->getCode()) { ?
>
357 <p
class="exceptionFieldTitle">Error Code
<span
class="exceptionColon">:</span
></p
>
358 <p
class="exceptionFieldValue"><?php
echo intval($e->getCode()); ?
></p
>
362 <p
class="exceptionFieldTitle">File
<span
class="exceptionColon">:</span
></p
>
363 <p
class="exceptionFieldValue" style
="word-break: break-all"><?php
echo htmlentities($e->getFile()); ?
> (<?php
echo $e->getLine(); ?
>)</p
>
367 <p
class="exceptionFieldTitle">Stack Trace
<span
class="exceptionColon">:</span
></p
>
368 <ul
class="exceptionStacktrace">
370 $trace = $e->getTrace();
371 for ($i = 0, $max = count($trace); $i < $max; $i++
) {
373 <li
class="exceptionStacktraceFile"><?php
echo '#'.$i.' '.htmlentities($trace[$i]['file']).' ('.$trace[$i]['line'].')'.':'; ?
></li
>
374 <li
class="exceptionStacktraceCall">
376 echo $trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'].'(';
377 echo implode(', ', array_map(function ($item) {
378 switch (gettype($item)) {
385 return "'".addcslashes(htmlentities($item), "\\'")."'";
387 return $item ?
'true' : 'false';
389 $keys = array_keys($item);
390 if (count($keys) > 5) return "[ ".count($keys)." items ]";
391 return '[ '.implode(', ', array_map(function ($item) {
395 return get_class($item);
398 throw new \
LogicException('Unreachable');
399 }, $trace[$i]['args']));
409 } while ($e = $e->getPrevious());
420 * Loads the required classes automatically.
422 spl_autoload_register(function($className) {
423 $namespaces = explode('\\', $className);
424 if (count($namespaces) > 1) {
425 // remove 'wcf' component
426 array_shift($namespaces);
428 $className = implode('/', $namespaces);
429 $classPath = TMP_DIR
. 'install/files/lib/' . $className . '.class.php';
430 if (file_exists($classPath)) {
431 require_once($classPath);
437 * Escapes strings for execution in sql queries.
439 * @param string $string
442 function escapeString($string) {
443 return \wcf\system\WCF
::getDB()->escapeString($string);
447 * Helper method to output debug data for all passed variables,
448 * uses `print_r()` for arrays and objects, `var_dump()` otherwise.
450 function wcfDebug() {
453 $args = func_get_args();
454 $length = count($args);
456 echo "ERROR: No arguments provided.<hr>";
459 for ($i = 0; $i < $length; $i++
) {
462 echo "<h2>Argument {$i} (" . gettype($arg) . ")</h2>";
464 if (is_array($arg) ||
is_object($arg)) {
475 $backtrace = debug_backtrace();
477 // output call location to help finding these debug outputs again
478 echo "wcfDebug() called in {$backtrace[0]['file']} on line {$backtrace[0]['line']}";
486 * Calls the show method on the given exception.
490 function handleException($e) {
492 if (!($e instanceof \Exception
)) throw $e;
494 if ($e instanceof IPrintableException ||
$e instanceof \wcf\system\exception\IPrintableException
) {
500 (new SystemException($e->getMessage(), $e->getCode(), '', $e))->show();
503 catch (\Throwable
$exception) {
504 die("<pre>WCF::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
509 * Catches php errors and throws instead a system exception.
511 * @param integer $errorNo
512 * @param string $message
513 * @param string $filename
514 * @param integer $lineNo
515 * @throws SystemException
517 function handleError($errorNo, $message, $filename, $lineNo) {
518 if (!(error_reporting() & $errorNo)) return;
521 case 2: $type = 'warning';
523 case 8: $type = 'notice';
527 throw new SystemException('PHP '.$type.' in file '.$filename.' ('.$lineNo.'): '.$message, 0);
530 if (!function_exists('is_countable')) {
531 function is_countable($var) { return is_array($var) ||
$var instanceof Countable ||
$var instanceof ResourceBundle ||
$var instanceof SimpleXmlElement
; }
534 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
536 * BasicFileUtil contains file-related functions.
538 * @package com.woltlab.wcf
539 * @author Marcel Werk
541 class BasicFileUtil
{
546 protected static $mode = null;
549 * Tries to find the temp folder.
552 * @throws SystemException
554 public static function getTempFolder() {
555 // use tmp folder in document root by default
556 if (!empty($_SERVER['DOCUMENT_ROOT'])) {
557 if (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false) {
559 // create tmp folder in document root automatically
560 if (!@file_exists
($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
561 @mkdir
($_SERVER['DOCUMENT_ROOT'].'/tmp/', 0777);
563 self
::makeWritable($_SERVER['DOCUMENT_ROOT'].'/tmp/');
565 catch (SystemException
$e) {}
568 if (@file_exists
($_SERVER['DOCUMENT_ROOT'].'/tmp') && @is_writable
($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
569 return $_SERVER['DOCUMENT_ROOT'].'/tmp/';
573 if (isset($_ENV['TMP']) && @is_writable
($_ENV['TMP'])) {
574 return $_ENV['TMP'] . '/';
576 if (isset($_ENV['TEMP']) && @is_writable
($_ENV['TEMP'])) {
577 return $_ENV['TEMP'] . '/';
579 if (isset($_ENV['TMPDIR']) && @is_writable
($_ENV['TMPDIR'])) {
580 return $_ENV['TMPDIR'] . '/';
583 if (($path = ini_get('upload_tmp_dir')) && @is_writable
($path)) {
586 if (@file_exists
('/tmp/') && @is_writable
('/tmp/')) {
589 if (function_exists('session_save_path') && ($path = session_save_path()) && @is_writable
($path)) {
593 $path = INSTALL_SCRIPT_DIR
.'tmp/';
594 if (@file_exists
($path) && @is_writable
($path)) {
598 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.');
603 * Returns the temp folder for the installation.
607 public static function getInstallTempFolder() {
608 $dir = self
::getTempFolder() . TMP_FILE_PREFIX
. '/';
610 self
::makeWritable($dir);
616 * Tries to make a file or directory writable. It starts of with the least
617 * permissions and goes up until 0666 for files and 0777 for directories.
619 * @param string $filename
620 * @throws SystemException
622 public static function makeWritable($filename) {
623 if (!file_exists($filename)) {
628 if (self
::$mode === null) {
629 // do not use PHP_OS here, as this represents the system it was built on != running on
630 // php_uname() is forbidden on some strange hosts; PHP_EOL is reliable
631 if (PHP_EOL
== "\r\n") {
636 // anything but Windows
641 $tmpFilename = '__permissions_'.sha1(time()).'.txt';
642 @touch
($tmpFilename);
644 // create a new file and check the file owner, if it is the same
645 // as this file (uploaded through FTP), we can safely grant write
646 // permissions exclusively to the owner rather than everyone
647 if (file_exists($tmpFilename)) {
648 $scriptOwner = fileowner(__FILE__
);
649 $fileOwner = fileowner($tmpFilename);
651 if ($scriptOwner === $fileOwner) {
655 @unlink
($tmpFilename);
660 if (is_dir($filename)) {
661 if (self
::$mode == 0644) {
662 @chmod
($filename, 0755);
665 @chmod
($filename, 0777);
669 @chmod
($filename, self
::$mode);
672 if (!is_writable($filename)) {
673 throw new SystemException("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider.");
678 * Removes a leading slash from the given path.
680 * @param string $path
683 public static function removeLeadingSlash($path) {
684 return ltrim($path, '/');
688 * Removes a trailing slash from the given path.
690 * @param string $path
693 public static function removeTrailingSlash($path) {
694 return rtrim($path, '/');
698 * Adds a trailing slash to the given path.
700 * @param string $path
703 public static function addTrailingSlash($path) {
704 return rtrim($path, '/').'/';
708 * Adds a leading slash to the given path.
710 * @param string $path
713 public static function addLeadingSlash($path) {
714 return '/'.ltrim($path, '/');
718 * Creates a path on the local filesystem and returns true on success.
719 * Parent directories do not need to exists as they will be created if
722 * @param string $path
725 public static function makePath($path) {
726 // directory already exists, abort
727 if (file_exists($path)) {
731 // check if parent directory exists
732 $parent = dirname($path);
733 if ($parent != $path) {
734 // parent directory does not exist either
735 // we have to create the parent directory first
736 $parent = self
::addTrailingSlash($parent);
737 if (!@file_exists
($parent)) {
738 // could not create parent directory either => abort
739 if (!self
::makePath($parent)) {
744 // well, the parent directory exists or has been created
745 // lets create this path
746 if (!@mkdir
($path)) {
750 self
::makeWritable($path);
759 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
761 * Opens tar or tar.gz archives.
765 * $tar = new Tar('archive.tar');
766 * $contentList = $tar->getContentList();
767 * foreach ($contentList as $key => $val) {
768 * $tar->extract($key, DESTINATION);
773 * name of the archive
776 protected $archiveName = '';
779 * content of the tar file
782 protected $contentList = [];
785 * indicates if tar file is opened
788 protected $opened = false;
791 * indicates if file content has been read
794 protected $read = false;
800 protected $file = null;
803 * indicates if the tar file is (g)zipped
806 protected $isZipped = false;
812 protected $mode = 'rb';
815 * chunk size for extracting
818 const CHUNK_SIZE
= 8192;
821 * Creates a new Tar object.
822 * archiveName must be tarball or gzipped tarball
824 * @param string $archiveName
825 * @throws SystemException
827 public function __construct($archiveName) {
828 if (!is_file($archiveName)) {
829 throw new SystemException("unable to find tar archive '".$archiveName."'");
832 $this->archiveName
= $archiveName;
834 $this->readContent();
838 * Destructor of this class, closes tar archive.
840 public function __destruct() {
845 * Opens the tar archive and stores filehandle.
847 public function open() {
848 if (!$this->opened
) {
849 if ($this->isZipped
) $this->file
= new GZipFile($this->archiveName
, $this->mode
);
852 $this->file
= new File($this->archiveName
, $this->mode
);
853 if ($this->file
->read(2) == "\37\213") {
854 $this->file
->close();
855 $this->isZipped
= true;
856 $this->file
= new GZipFile($this->archiveName
, $this->mode
);
859 $this->file
->seek(0);
862 $this->opened
= true;
867 * Closes the opened file.
869 public function close() {
871 $this->file
->close();
872 $this->opened
= false;
879 public function getContentList() {
882 $this->readContent();
884 return $this->contentList
;
890 public function getFileInfo($fileIndex) {
891 if (!is_int($fileIndex)) {
892 $fileIndex = $this->getIndexByFilename($fileIndex);
895 if (!isset($this->contentList
[$fileIndex])) {
896 throw new SystemException("Tar: could find file '".$fileIndex."' in archive");
898 return $this->contentList
[$fileIndex];
904 public function getIndexByFilename($filename) {
905 foreach ($this->contentList
as $index => $file) {
906 if ($file['filename'] == $filename) {
916 public function extractToString($index) {
919 $this->readContent();
921 $header = $this->getFileInfo($index);
923 // can not extract a folder
924 if ($header['type'] != 'file') {
929 $this->file
->seek($header['offset']);
932 $content = $this->file
->read($header['size']);
934 if (strlen($content) != $header['size']) {
935 throw new SystemException("Could not untar file '".$header['filename']."' to string. Maybe the archive is truncated?");
944 public function extract($index, $destination) {
947 $this->readContent();
949 $header = $this->getFileInfo($index);
951 BasicFileUtil
::makePath(dirname($destination));
952 if ($header['type'] === 'folder') {
953 BasicFileUtil
::makePath($destination);
956 if ($header['type'] === 'symlink') {
962 $this->file
->seek($header['offset']);
964 $targetFile = new File($destination);
966 // read and write data
967 if ($header['size']) {
968 $buffer = $this->file
->read($header['size']);
969 $targetFile->write($buffer);
971 $targetFile->close();
973 BasicFileUtil
::makeWritable($destination);
975 if ($header['mtime']) {
976 @$targetFile->touch($header['mtime']);
980 if (filesize($destination) != $header['size']) {
981 throw new SystemException("Could not untar file '".$header['filename']."' to '".$destination."'. Maybe disk quota exceeded in folder '".dirname($destination)."'.");
988 * Reads table of contents (TOC) from tar archive.
989 * This does not get the entire to memory but only parts of it.
991 protected function readContent() {
992 $this->contentList
= [];
996 // Read the 512 bytes header
997 $longFilename = null;
998 while (strlen($binaryData = $this->file
->read(512)) != 0) {
1000 $header = $this->readHeader($binaryData);
1001 if ($header === false) {
1005 // fixes a bug that files with long names aren't correctly
1007 if ($longFilename !== null) {
1008 $header['filename'] = $longFilename;
1009 $longFilename = null;
1011 if ($header['typeflag'] == 'L') {
1012 $format = 'Z'.$header['size'].'filename';
1014 $fileData = unpack($format, $this->file
->read(512));
1015 $longFilename = $fileData['filename'];
1016 $header['size'] = 0;
1018 // don't include the @LongLink file in the content list
1020 $this->contentList
[$i] = $header;
1021 $this->contentList
[$i]['index'] = $i;
1025 $this->file
->seek($this->file
->tell() +
(512 * ceil($header['size'] / 512)));
1030 * Unpacks file header for one file entry.
1032 * @param string $binaryData
1033 * @return array|boolean
1035 protected function readHeader($binaryData) {
1036 if (strlen($binaryData) != 512) {
1042 // First part of the header
1043 for ($i = 0; $i < 148; $i++
) {
1044 $checksum +
= ord(substr($binaryData, $i, 1));
1046 // Calculate the checksum
1047 // Ignore the checksum value and replace it by ' ' (space)
1048 for ($i = 148; $i < 156; $i++
) {
1049 $checksum +
= ord(' ');
1051 // Last part of the header
1052 for ($i = 156; $i < 512; $i++
) {
1053 $checksum +
= ord(substr($binaryData, $i, 1));
1057 $format = 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor/Z155prefix';
1059 $data = unpack($format, $binaryData);
1061 // Extract the properties
1062 $header['checksum'] = octdec(trim($data['checksum']));
1063 if ($header['checksum'] == $checksum) {
1064 $header['filename'] = trim($data['filename']);
1065 $header['mode'] = octdec(trim($data['mode']));
1066 $header['uid'] = octdec(trim($data['uid']));
1067 $header['gid'] = octdec(trim($data['gid']));
1068 $header['size'] = octdec(trim($data['size']));
1069 $header['mtime'] = octdec(trim($data['mtime']));
1070 $header['prefix'] = trim($data['prefix']);
1071 if ($header['prefix']) {
1072 $header['filename'] = $header['prefix'].'/'.$header['filename'];
1074 $header['typeflag'] = $data['typeflag'];
1075 if ($header['typeflag'] == '5') {
1076 $header['size'] = 0;
1077 $header['type'] = 'folder';
1079 else if ($header['typeflag'] == '2') {
1080 $header['type'] = 'symlink';
1081 $header['target'] = $data['link'];
1084 $header['type'] = 'file';
1086 $header['offset'] = $this->file
->tell();
1096 * Returns true if this tar is (g)zipped.
1100 public function isZipped() {
1101 return $this->isZipped
;
1105 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
1107 * The File class handles all file operations.
1110 * using php functions:
1111 * $fp = fopen('filename', 'wb');
1112 * fwrite($fp, '...');
1116 * $file = new File('filename');
1117 * $file->write('...');
1120 * @author Marcel Werk
1123 protected $resource = null;
1124 protected $filename;
1129 * @param string $filename
1130 * @param string $mode
1131 * @throws SystemException
1133 public function __construct($filename, $mode = 'wb') {
1134 $this->filename
= $filename;
1135 $this->resource = fopen($filename, $mode);
1136 if ($this->resource === false) {
1137 throw new SystemException('Can not open file ' . $filename);
1142 * Calls the specified function on the open file.
1143 * Do not call this function directly. Use $file->write('') instead.
1145 * @param string $function
1146 * @param array $arguments
1148 * @throws SystemException
1150 public function __call($function, $arguments) {
1151 if (function_exists('f' . $function)) {
1152 array_unshift($arguments, $this->resource);
1153 return call_user_func_array('f' . $function, $arguments);
1155 else if (function_exists($function)) {
1156 array_unshift($arguments, $this->filename
);
1157 return call_user_func_array($function, $arguments);
1160 throw new SystemException('Can not call file method ' . $function);
1165 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
1167 * The File class handles all file operations on a zipped file.
1169 * @author Marcel Werk
1171 class GZipFile
extends File
{
1173 * checks if gz*64 functions are available instead of gz*
1174 * https://bugs.php.net/bug.php?id=53829
1177 protected static $gzopen64 = null;
1179 /** @noinspection PhpMissingParentConstructorInspection */
1181 * Opens a gzip file.
1183 * @param string $filename
1184 * @param string $mode
1185 * @throws SystemException
1187 public function __construct($filename, $mode = 'wb') {
1188 if (self
::$gzopen64 === null) {
1189 self
::$gzopen64 = function_exists('gzopen64');
1192 $this->filename
= $filename;
1193 /** @noinspection PhpUndefinedFunctionInspection */
1194 $this->resource = (self
::$gzopen64 ?
gzopen64($filename, $mode) : gzopen($filename, $mode));
1195 if ($this->resource === false) {
1196 throw new SystemException('Can not open file ' . $filename);
1201 * Calls the specified function on the open file.
1203 * @param string $function
1204 * @param array $arguments
1206 * @throws SystemException
1208 public function __call($function, $arguments) {
1209 if (self
::$gzopen64 && function_exists('gz' . $function . '64')) {
1210 array_unshift($arguments, $this->resource);
1211 return call_user_func_array('gz' . $function . '64', $arguments);
1213 else if (function_exists('gz' . $function)) {
1214 array_unshift($arguments, $this->resource);
1215 return call_user_func_array('gz' . $function, $arguments);
1217 else if (function_exists($function)) {
1218 array_unshift($arguments, $this->filename
);
1219 return call_user_func_array($function, $arguments);
1222 throw new SystemException('Can not call method ' . $function);
1227 * Returns the filesize of the unzipped file.
1231 public function getFileSize() {
1235 // the correction is for zip files that are too small
1236 // to get in the first while loop
1238 while ($this->seek($eof) == 0) {
1243 while ($byteBlock > 1) {
1245 $eof +
= $byteBlock * ($this->seek($eof) ?
-1 : 1);
1248 if ($this->seek($eof) == -1) $eof--;
1251 return $eof - $correction;
1256 // get temp file prefix
1257 if (isset($_REQUEST['tmpFilePrefix'])) {
1258 $prefix = preg_replace('/[^a-f0-9_]+/', '', $_REQUEST['tmpFilePrefix']);
1261 $prefix = substr(sha1(uniqid(microtime())), 0, 8);
1263 define('TMP_FILE_PREFIX', $prefix);
1265 // try to find the temp folder
1266 define('TMP_DIR', BasicFileUtil
::getInstallTempFolder());
1269 * Reads a file resource from temp folder.
1271 * @param string $key
1272 * @param string $directory
1274 function readFileResource($key, $directory) {
1275 if (preg_match('~[\w\-]+\.(css|jpg|png|svg|eot|woff|ttf)~', $_GET[$key], $match)) {
1276 switch ($match[1]) {
1278 header('Content-Type: text/css');
1282 header('Content-Type: image/jpg');
1286 header('Content-Type: image/png');
1290 header('Content-Type: image/svg+xml');
1294 header('Content-Type: application/vnd.ms-fontobject');
1298 header('Content-Type: application/font-woff');
1302 header('Content-Type: application/octet-stream');
1306 header('Expires: '.gmdate('D, d M Y H:i:s', time() +
3600).' GMT');
1307 header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT');
1308 header('Cache-Control: public, max-age=3600');
1310 readfile($directory . $_GET[$key]);
1315 // show image from temp folder
1316 if (isset($_GET['showImage'])) {
1317 readFileResource('showImage', TMP_DIR
. 'install/files/acp/images/');
1319 // show icon from temp folder
1320 if (isset($_GET['showIcon'])) {
1321 readFileResource('showIcon', TMP_DIR
. 'install/files/icon/');
1323 // show css from temp folder
1324 if (isset($_GET['showCSS'])) {
1325 readFileResource('showCSS', TMP_DIR
. 'install/files/acp/style/setup/');
1327 // show fonts from temp folder
1328 if (isset($_GET['showFont'])) {
1329 readFileResource('showFont', TMP_DIR
. 'install/files/font/');
1332 // check whether setup files are already unzipped
1333 if (!file_exists(TMP_DIR
. 'install/files/lib/system/WCFSetup.class.php')) {
1334 // try to unzip all setup files into temp folder
1335 $tar = new Tar(SETUP_FILE
);
1336 $contentList = $tar->getContentList();
1337 if (empty($contentList)) {
1338 throw new SystemException("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken.");
1341 foreach ($contentList as $file) {
1342 foreach ($neededFilesPattern as $pattern) {
1343 if (preg_match($pattern, $file['filename'])) {
1344 // create directory if not exists
1345 $dir = TMP_DIR
. dirname($file['filename']);
1346 if (!@is_dir
($dir)) {
1347 @mkdir
($dir, 0777, true);
1348 BasicFileUtil
::makeWritable($dir);
1351 $tar->extract($file['index'], TMP_DIR
. $file['filename']);
1357 // create cache folders
1358 @mkdir
(TMP_DIR
. 'setup/lang/cache/', 0777);
1359 BasicFileUtil
::makeWritable(TMP_DIR
. 'setup/lang/cache/');
1361 @mkdir
(TMP_DIR
. 'setup/template/compiled/', 0777);
1362 BasicFileUtil
::makeWritable(TMP_DIR
. 'setup/template/compiled/');
1365 if (!class_exists('wcf\system\WCFSetup')) {
1366 throw new SystemException("Cannot find class 'WCFSetup'");
1370 new \wcf\system\
WCFSetup();