3 * This script tries to find the temp folder and unzip all setup files into.
6 * @copyright 2001-2016 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
68 public function __construct($message = '', $code = 0, $description = '') {
69 parent
::__construct($message, $code);
70 $this->description
= $description;
74 * Returns the description of this exception.
78 public function getDescription() {
79 return $this->description
;
83 * Prints this exception.
84 * This method is called by WCF::handleException().
86 public function show() {
88 * A notice on the HTML used below:
90 * It might appear a bit weird to use <p> all over the place where semantically
91 * other elements would fit in way better. The reason behind this is that we avoid
92 * inheriting unwanted styles (e.g. exception displayed in an overlay) and that
93 * the output needs to be properly readable when copied & pasted somewhere.
95 * Besides the visual appearance, the output was built to provide a maximum of
96 * compatibility and readability when pasted somewhere else, e.g. a WYSIWYG editor
97 * without the potential of messing up the formatting and thus harming the readability.
102 <title
>Fatal Error
: <?php
echo htmlentities($this->getMessage()); ?
></title
>
103 <meta charset
="utf-8">
104 <meta name
="viewport" content
="width=device-width, initial-scale=1">
111 .exceptionContainer
{
112 box
-sizing
: border
-box
;
113 font
-family
: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica
, Arial
, sans
-serif
;
115 padding
-bottom
: 20px
;
118 .exceptionContainer
* {
127 background
-color
: rgb(44, 62, 80);
137 .exceptionErrorCode
{
142 .exceptionErrorCode
.exceptionInlineCode
{
143 background
-color
: rgb(52, 73, 94);
146 font
-family
: monospace
;
152 border
-bottom
: 1px solid
rgb(238, 238, 238);
153 color
: rgb(44, 62, 80);
157 padding
-bottom
: 10px
;
160 .exceptionContainer
> .exceptionBoundary
{
164 .exceptionText
.exceptionInlineCodeWrapper
{
165 border
: 1px solid
rgb(169, 169, 169);
170 .exceptionText
.exceptionInlineCode
{
171 font
-family
: monospace
;
175 .exceptionFieldTitle
{
176 color
: rgb(59, 109, 169);
179 .exceptionFieldTitle
.exceptionColon
{
180 /* hide colon in browser, but will be visible after copy & paste */
184 .exceptionFieldValue
{
189 .exceptionSystemInformation
,
190 .exceptionErrorDetails
,
191 .exceptionStacktrace
{
192 list-style
-type
: none
;
195 .exceptionSystemInformation
> li
:not(:first
-child
),
196 .exceptionErrorDetails
> li
:not(:first
-child
) {
200 .exceptionStacktrace
{
204 padding
-bottom
: 20px
;
207 .exceptionStacktraceFile
,
208 .exceptionStacktraceFile span
,
209 .exceptionStacktraceCall
,
210 .exceptionStacktraceCall span
{
211 font
-family
: monospace
!important
;
212 white
-space
: nowrap
!important
;
215 .exceptionStacktraceCall +
.exceptionStacktraceFile
{
219 .exceptionStacktraceCall
{
223 .exceptionStacktraceCall
,
224 .exceptionStacktraceCall span
{
225 color
: rgb(102, 102, 102) !important
;
226 font
-size
: 13px
!important
;
230 @media
(max
-width
: 767px
) {
236 .exceptionText
.exceptionInlineCodeWrapper
{
237 display
: inline
-block
;
241 .exceptionErrorCode
.exceptionInlineCode
{
248 @media
(min
-width
: 768px
) {
256 .exceptionSystemInformation
{
261 .exceptionSystemInformation1
,
262 .exceptionSystemInformation3
,
263 .exceptionSystemInformation5
{
265 margin
: 0 0 10px
0 !important
;
268 .exceptionSystemInformation2
,
269 .exceptionSystemInformation4
,
270 .exceptionSystemInformation6
{
271 flex
: 0 0 calc(100%
- 210px
);
272 margin
: 0 0 10px
10px
!important
;
273 max
-width
: calc(100%
- 210px
);
276 .exceptionSystemInformation1
{ order
: 1; }
277 .exceptionSystemInformation2
{ order
: 2; }
278 .exceptionSystemInformation3
{ order
: 3; }
279 .exceptionSystemInformation4
{ order
: 4; }
280 .exceptionSystemInformation5
{ order
: 5; }
281 .exceptionSystemInformation6
{ order
: 6; }
283 .exceptionSystemInformation
.exceptionFieldValue
{
285 text
-overflow
: ellipsis
;
291 <body
class="exceptionBody">
292 <div
class="exceptionContainer">
293 <div
class="exceptionHeader">
294 <div
class="exceptionBoundary">
295 <p
class="exceptionTitle">An error has occured
</p
>
299 <div
class="exceptionBoundary">
300 <p
class="exceptionSubtitle">System Information
</p
>
301 <ul
class="exceptionSystemInformation">
302 <li
class="exceptionSystemInformation1">
303 <p
class="exceptionFieldTitle">PHP Version
<span
class="exceptionColon">:</span
></p
>
304 <p
class="exceptionFieldValue"><?php
echo htmlentities(phpversion()); ?
></p
>
306 <li
class="exceptionSystemInformation3">
307 <p
class="exceptionFieldTitle">WoltLab Suite Core
<span
class="exceptionColon">:</span
></p
>
308 <p
class="exceptionFieldValue">3.0</p
>
310 <li
class="exceptionSystemInformation5">
311 <p
class="exceptionFieldTitle">Peak Memory Usage
<span
class="exceptionColon">:</span
></p
>
312 <p
class="exceptionFieldValue"><?php
echo round(memory_get_peak_usage() / 1024 / 1024, 3); ?
>/<?php
echo ini_get('memory_limit'); ?
></p
>
314 <li
class="exceptionSystemInformation2">
315 <p
class="exceptionFieldTitle">Request URI
<span
class="exceptionColon">:</span
></p
>
316 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['REQUEST_URI'])) echo htmlentities($_SERVER['REQUEST_URI']); ?
></p
>
318 <li
class="exceptionSystemInformation4">
319 <p
class="exceptionFieldTitle">Referrer
<span
class="exceptionColon">:</span
></p
>
320 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['HTTP_REFERER'])) echo htmlentities($_SERVER['HTTP_REFERER']); ?
></p
>
322 <li
class="exceptionSystemInformation6">
323 <p
class="exceptionFieldTitle">User Agent
<span
class="exceptionColon">:</span
></p
>
324 <p
class="exceptionFieldValue"><?php
if (isset($_SERVER['HTTP_USER_AGENT'])) echo htmlentities($_SERVER['HTTP_USER_AGENT']); ?
></p
>
333 <div
class="exceptionBoundary">
334 <p
class="exceptionSubtitle"><?php
if (!$this->getPrevious() && !$first) { echo "Original "; } else if ($this->getPrevious() && $first) { echo "Final "; } ?
>Error
</p
>
335 <?php
if ($this instanceof SystemException
&& $this->getDescription()) { ?
>
336 <p
class="exceptionText"><?php
echo $this->getDescription(); ?
></p
>
338 <ul
class="exceptionErrorDetails">
340 <p
class="exceptionFieldTitle">Error Type
<span
class="exceptionColon">:</span
></p
>
341 <p
class="exceptionFieldValue"><?php
echo htmlentities(get_class($this)); ?
></p
>
344 <p
class="exceptionFieldTitle">Error Message
<span
class="exceptionColon">:</span
></p
>
345 <p
class="exceptionFieldValue"><?php
echo htmlentities($this->getMessage()); ?
></p
>
347 <?php
if ($this->getCode()) { ?
>
349 <p
class="exceptionFieldTitle">Error Code
<span
class="exceptionColon">:</span
></p
>
350 <p
class="exceptionFieldValue"><?php
echo intval($this->getCode()); ?
></p
>
354 <p
class="exceptionFieldTitle">File
<span
class="exceptionColon">:</span
></p
>
355 <p
class="exceptionFieldValue" style
="word-break: break-all"><?php
echo htmlentities($this->getFile()); ?
> (<?php
echo $this->getLine(); ?
>)</p
>
359 <p
class="exceptionFieldTitle">Stack Trace
<span
class="exceptionColon">:</span
></p
>
360 <ul
class="exceptionStacktrace">
362 $trace = $this->getTrace();
363 for ($i = 0, $max = count($trace); $i < $max; $i++
) {
365 <li
class="exceptionStacktraceFile"><?php
echo '#'.$i.' '.htmlentities($trace[$i]['file']).' ('.$trace[$i]['line'].')'.':'; ?
></li
>
366 <li
class="exceptionStacktraceCall">
368 echo $trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'].'(';
369 echo implode(', ', array_map(function ($item) {
370 switch (gettype($item)) {
377 return "'".addcslashes(htmlentities($item), "\\'")."'";
379 return $item ?
'true' : 'false';
381 $keys = array_keys($item);
382 if (count($keys) > 5) return "[ ".count($keys)." items ]";
383 return '[ '.implode(', ', array_map(function ($item) {
387 return get_class($item);
390 throw new \
LogicException('Unreachable');
391 }, $trace[$i]['args']));
401 } while ($e = $this->getPrevious());
412 * Loads the required classes automatically.
414 spl_autoload_register(function($className) {
415 $namespaces = explode('\\', $className);
416 if (count($namespaces) > 1) {
417 // remove 'wcf' component
418 array_shift($namespaces);
420 $className = implode('/', $namespaces);
421 $classPath = TMP_DIR
. 'install/files/lib/' . $className . '.class.php';
422 if (file_exists($classPath)) {
423 require_once($classPath);
429 * Escapes strings for execution in sql queries.
431 * @param string $string
434 function escapeString($string) {
435 return \wcf\system\WCF
::getDB()->escapeString($string);
439 * Calls the show method on the given exception.
443 function handleException($e) {
445 if (!($e instanceof \Exception
)) throw $e;
447 if ($e instanceof IPrintableException ||
$e instanceof \wcf\system\exception\IPrintableException
) {
452 catch (\Throwable
$exception) {
453 die("<pre>WCF::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
455 catch (\Exception
$exception) {
456 die("<pre>WCF::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
461 * Catches php errors and throws instead a system exception.
463 * @param integer $errorNo
464 * @param string $message
465 * @param string $filename
466 * @param integer $lineNo
467 * @throws SystemException
469 function handleError($errorNo, $message, $filename, $lineNo) {
470 if (error_reporting() != 0) {
473 case 2: $type = 'warning';
475 case 8: $type = 'notice';
479 throw new SystemException('PHP '.$type.' in file '.$filename.' ('.$lineNo.'): '.$message, 0);
483 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
485 * BasicFileUtil contains file-related functions.
487 * @package com.woltlab.wcf
488 * @author Marcel Werk
490 class BasicFileUtil
{
495 protected static $mode = null;
498 * Tries to find the temp folder.
501 * @throws SystemException
503 public static function getTempFolder() {
504 // use tmp folder in document root by default
505 if (!empty($_SERVER['DOCUMENT_ROOT'])) {
506 if (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false) {
508 // create tmp folder in document root automatically
509 if (!@file_exists
($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
510 @mkdir
($_SERVER['DOCUMENT_ROOT'].'/tmp/', 0777);
512 self
::makeWritable($_SERVER['DOCUMENT_ROOT'].'/tmp/');
514 catch (SystemException
$e) {}
517 if (@file_exists
($_SERVER['DOCUMENT_ROOT'].'/tmp') && @is_writable
($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
518 return $_SERVER['DOCUMENT_ROOT'].'/tmp/';
522 if (isset($_ENV['TMP']) && @is_writable
($_ENV['TMP'])) {
523 return $_ENV['TMP'] . '/';
525 if (isset($_ENV['TEMP']) && @is_writable
($_ENV['TEMP'])) {
526 return $_ENV['TEMP'] . '/';
528 if (isset($_ENV['TMPDIR']) && @is_writable
($_ENV['TMPDIR'])) {
529 return $_ENV['TMPDIR'] . '/';
532 if (($path = ini_get('upload_tmp_dir')) && @is_writable
($path)) {
535 if (@file_exists
('/tmp/') && @is_writable
('/tmp/')) {
538 if (function_exists('session_save_path') && ($path = session_save_path()) && @is_writable
($path)) {
542 $path = INSTALL_SCRIPT_DIR
.'tmp/';
543 if (@file_exists
($path) && @is_writable
($path)) {
547 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.');
552 * Returns the temp folder for the installation.
556 public static function getInstallTempFolder() {
557 $dir = self
::getTempFolder() . TMP_FILE_PREFIX
. '/';
559 self
::makeWritable($dir);
565 * Tries to make a file or directory writable. It starts of with the least
566 * permissions and goes up until 0666 for files and 0777 for directories.
568 * @param string $filename
569 * @throws SystemException
571 public static function makeWritable($filename) {
572 if (!file_exists($filename)) {
577 if (self
::$mode === null) {
578 // do not use PHP_OS here, as this represents the system it was built on != running on
579 // php_uname() is forbidden on some strange hosts; PHP_EOL is reliable
580 if (PHP_EOL
== "\r\n") {
585 // anything but Windows
590 $tmpFilename = '__permissions_'.sha1(time()).'.txt';
591 @touch
($tmpFilename);
593 // create a new file and check the file owner, if it is the same
594 // as this file (uploaded through FTP), we can safely grant write
595 // permissions exclusively to the owner rather than everyone
596 if (file_exists($tmpFilename)) {
597 $scriptOwner = fileowner(__FILE__
);
598 $fileOwner = fileowner($tmpFilename);
600 if ($scriptOwner === $fileOwner) {
604 @unlink
($tmpFilename);
609 if (is_dir($filename)) {
610 if (self
::$mode == 0644) {
611 @chmod
($filename, 0755);
614 @chmod
($filename, 0777);
618 @chmod
($filename, self
::$mode);
621 if (!is_writable($filename)) {
622 throw new SystemException("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider.");
627 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
629 * Opens tar or tar.gz archives.
633 * $tar = new Tar('archive.tar');
634 * $contentList = $tar->getContentList();
635 * foreach ($contentList as $key => $val) {
636 * $tar->extract($key, DESTINATION);
640 protected $archiveName = '';
641 protected $contentList = [];
642 protected $opened = false;
643 protected $read = false;
644 protected $file = null;
645 protected $isZipped = false;
646 protected $mode = 'rb';
649 * Creates a new Tar object.
650 * archiveName must be tarball or gzipped tarball
652 * @param string $archiveName
653 * @throws SystemException
655 public function __construct($archiveName) {
656 if (!is_file($archiveName)) {
657 throw new SystemException("unable to find tar archive '".$archiveName."'");
660 $this->archiveName
= $archiveName;
662 $this->readContent();
666 * Destructor of this class, closes tar archive.
668 public function __destruct() {
673 * Opens the tar archive and stores filehandle.
675 public function open() {
676 if (!$this->opened
) {
677 if ($this->isZipped
) $this->file
= new ZipFile($this->archiveName
, $this->mode
);
680 $this->file
= new File($this->archiveName
, $this->mode
);
681 if ($this->file
->read(2) == "\37\213") {
682 $this->file
->close();
683 $this->isZipped
= true;
684 $this->file
= new ZipFile($this->archiveName
, $this->mode
);
687 $this->file
->seek(0);
690 $this->opened
= true;
695 * Closes the opened file.
697 public function close() {
699 $this->file
->close();
700 $this->opened
= false;
705 * Returns the table of contents (TOC) list for this tar archive.
707 * @return array list of content
709 public function getContentList() {
712 $this->readContent();
714 return $this->contentList
;
718 * Returns an associative array with information
719 * about a specific file in the archive.
721 * @param mixed $fileIndex index or name of the requested file
723 * @throws SystemException
725 public function getFileInfo($fileIndex) {
726 if (!is_int($fileIndex)) {
727 $fileIndex = $this->getIndexByFilename($fileIndex);
730 if (!isset($this->contentList
[$fileIndex])) {
731 throw new SystemException("Tar: could find file '".$fileIndex."' in archive");
733 return $this->contentList
[$fileIndex];
737 * Searchs a file in the tar archive
738 * and returns the numeric fileindex.
739 * Returns false if not found.
741 * @param string $filename
742 * @return integer index of the requested file
744 public function getIndexByFilename($filename) {
745 foreach ($this->contentList
as $index => $file) {
746 if ($file['filename'] == $filename) {
754 * Extracts a specific file and returns the content as string.
755 * Returns false if extraction failed.
757 * @param mixed $index index or name of the requested file
758 * @return string content of the requested file
760 public function extractToString($index) {
763 $this->readContent();
765 $header = $this->getFileInfo($index);
767 // can not extract a folder
768 if ($header['type'] != 'file') {
773 $this->file
->seek($header['offset']);
777 $n = floor($header['size'] / 512);
778 for($i = 0; $i < $n; $i++
) {
779 $content .= $this->file
->read(512);
781 if(($header['size'] %
512) != 0) {
782 $buffer = $this->file
->read(512);
783 $content .= substr($buffer, 0, $header['size'] %
512);
790 * Extracts a specific file and writes it's content
791 * to the file specified with $destination.
793 * @param mixed $index index or name of the requested file
794 * @param string $destination
796 * @throws SystemException
798 public function extract($index, $destination) {
801 $this->readContent();
803 $header = $this->getFileInfo($index);
805 // can not extract a folder
806 if ($header['type'] != 'file') {
811 $this->file
->seek($header['offset']);
813 $targetFile = new File($destination);
816 $n = floor($header['size'] / 512);
817 for ($i = 0; $i < $n; $i++
) {
818 $content = $this->file
->read(512);
819 $targetFile->write($content, 512);
821 if (($header['size'] %
512) != 0) {
822 $content = $this->file
->read(512);
823 $targetFile->write($content, $header['size'] %
512);
826 $targetFile->close();
827 BasicFileUtil
::makeWritable($destination);
829 if ($header['mtime']) {
830 @$targetFile->touch($header['mtime']);
834 if (filesize($destination) != $header['size']) {
835 throw new SystemException("Could not untar file '".$header['filename']."' to '".$destination."'. Maybe disk quota exceeded in folder '".dirname($destination)."'.");
842 * Reads table of contents (TOC) from tar archive.
843 * This does not get the entire to memory but only parts of it.
845 protected function readContent() {
846 $this->contentList
= [];
850 // Read the 512 bytes header
851 $longFilename = null;
852 while (strlen($binaryData = $this->file
->read(512)) != 0) {
854 $header = $this->readHeader($binaryData);
855 if ($header === false) {
859 // fixes a bug that files with long names aren't correctly
861 if ($longFilename !== null) {
862 $header['filename'] = $longFilename;
863 $longFilename = null;
865 if ($header['typeflag'] == 'L') {
866 $format = 'Z'.$header['size'].'filename';
868 $fileData = unpack($format, $this->file
->read(512));
869 $longFilename = $fileData['filename'];
872 // don't include the @LongLink file in the content list
874 $this->contentList
[$i] = $header;
875 $this->contentList
[$i]['index'] = $i;
879 $this->file
->seek($this->file
->tell() +
(512 * ceil($header['size'] / 512)));
884 * Unpacks file header for one file entry.
886 * @param string $binaryData
887 * @return array|boolean
889 protected function readHeader($binaryData) {
890 if (strlen($binaryData) != 512) {
896 // First part of the header
897 for ($i = 0; $i < 148; $i++
) {
898 $checksum +
= ord(substr($binaryData, $i, 1));
900 // Calculate the checksum
901 // Ignore the checksum value and replace it by ' ' (space)
902 for ($i = 148; $i < 156; $i++
) {
903 $checksum +
= ord(' ');
905 // Last part of the header
906 for ($i = 156; $i < 512; $i++
) {
907 $checksum +
= ord(substr($binaryData, $i, 1));
911 $format = 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor/Z155prefix';
913 $data = unpack($format, $binaryData);
915 // Extract the properties
916 $header['checksum'] = octdec(trim($data['checksum']));
917 if ($header['checksum'] == $checksum) {
918 $header['filename'] = trim($data['filename']);
919 $header['mode'] = octdec(trim($data['mode']));
920 $header['uid'] = octdec(trim($data['uid']));
921 $header['gid'] = octdec(trim($data['gid']));
922 $header['size'] = octdec(trim($data['size']));
923 $header['mtime'] = octdec(trim($data['mtime']));
924 $header['prefix'] = trim($data['prefix']);
925 if ($header['prefix']) {
926 $header['filename'] = $header['prefix'].'/'.$header['filename'];
928 if (($header['typeflag'] = $data['typeflag']) == '5') {
930 $header['type'] = 'folder';
933 $header['type'] = 'file';
935 $header['offset'] = $this->file
->tell();
945 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
947 * The File class handles all file operations.
950 * using php functions:
951 * $fp = fopen('filename', 'wb');
952 * fwrite($fp, '...');
956 * $file = new File('filename');
957 * $file->write('...');
960 * @author Marcel Werk
963 protected $resource = null;
969 * @param string $filename
970 * @param string $mode
971 * @throws SystemException
973 public function __construct($filename, $mode = 'wb') {
974 $this->filename
= $filename;
975 $this->resource = fopen($filename, $mode);
976 if ($this->resource === false) {
977 throw new SystemException('Can not open file ' . $filename);
982 * Calls the specified function on the open file.
983 * Do not call this function directly. Use $file->write('') instead.
985 * @param string $function
986 * @param array $arguments
988 * @throws SystemException
990 public function __call($function, $arguments) {
991 if (function_exists('f' . $function)) {
992 array_unshift($arguments, $this->resource);
993 return call_user_func_array('f' . $function, $arguments);
995 else if (function_exists($function)) {
996 array_unshift($arguments, $this->filename
);
997 return call_user_func_array($function, $arguments);
1000 throw new SystemException('Can not call file method ' . $function);
1005 /** @noinspection PhpMultipleClassesDeclarationsInOneFile */
1007 * The File class handles all file operations on a zipped file.
1009 * @author Marcel Werk
1011 class ZipFile
extends File
{
1013 * checks if gz*64 functions are available instead of gz*
1014 * https://bugs.php.net/bug.php?id=53829
1017 protected static $gzopen64 = null;
1019 /** @noinspection PhpMissingParentConstructorInspection */
1021 * Opens a new zipped file.
1023 * @param string $filename
1024 * @param string $mode
1025 * @throws SystemException
1027 public function __construct($filename, $mode = 'wb') {
1028 if (self
::$gzopen64 === null) {
1029 self
::$gzopen64 = function_exists('gzopen64');
1032 $this->filename
= $filename;
1033 if (!self
::$gzopen64 && !function_exists('gzopen')) {
1034 throw new SystemException('Can not find functions of the zlib extension');
1036 /** @noinspection PhpUndefinedFunctionInspection */
1037 $this->resource = (self
::$gzopen64 ? @gzopen64
($filename, $mode) : @gzopen
($filename, $mode));
1038 if ($this->resource === false) {
1039 throw new SystemException('Can not open file ' . $filename);
1044 * Calls the specified function on the open file.
1046 * @param string $function
1047 * @param array $arguments
1049 * @throws SystemException
1051 public function __call($function, $arguments) {
1052 if (self
::$gzopen64 && function_exists('gz' . $function . '64')) {
1053 array_unshift($arguments, $this->resource);
1054 return call_user_func_array('gz' . $function . '64', $arguments);
1056 else if (function_exists('gz' . $function)) {
1057 array_unshift($arguments, $this->resource);
1058 return call_user_func_array('gz' . $function, $arguments);
1060 else if (function_exists($function)) {
1061 array_unshift($arguments, $this->filename
);
1062 return call_user_func_array($function, $arguments);
1065 throw new SystemException('Can not call method ' . $function);
1070 * Returns the filesize of the unzipped file
1072 public function getFileSize() {
1076 // the correction is for zip files that are too small
1077 // to get in the first while loop
1079 while ($this->seek($eof) == 0) {
1084 while ($byteBlock > 1) {
1086 $eof +
= $byteBlock * ($this->seek($eof) ?
-1 : 1);
1089 if ($this->seek($eof) == -1) $eof -= 1;
1092 return $eof - $correction;
1097 // get temp file prefix
1098 if (isset($_REQUEST['tmpFilePrefix'])) {
1099 $prefix = preg_replace('/[^a-f0-9_]+/', '', $_REQUEST['tmpFilePrefix']);
1102 $prefix = substr(sha1(uniqid(microtime())), 0, 8);
1104 define('TMP_FILE_PREFIX', $prefix);
1106 // try to find the temp folder
1107 define('TMP_DIR', BasicFileUtil
::getInstallTempFolder());
1110 * Reads a file resource from temp folder.
1112 * @param string $key
1113 * @param string $directory
1115 function readFileResource($key, $directory) {
1116 if (preg_match('~[\w\-]+\.(css|jpg|png|svg|eot|woff|ttf)~', $_GET[$key], $match)) {
1117 switch ($match[1]) {
1119 header('Content-Type: text/css');
1123 header('Content-Type: image/jpg');
1127 header('Content-Type: image/png');
1131 header('Content-Type: image/svg+xml');
1135 header('Content-Type: application/vnd.ms-fontobject');
1139 header('Content-Type: application/font-woff');
1143 header('Content-Type: application/octet-stream');
1147 header('Expires: '.gmdate('D, d M Y H:i:s', time() +
3600).' GMT');
1148 header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT');
1149 header('Cache-Control: public, max-age=3600');
1151 readfile($directory . $_GET[$key]);
1156 // show image from temp folder
1157 if (isset($_GET['showImage'])) {
1158 readFileResource('showImage', TMP_DIR
. 'install/files/acp/images/');
1160 // show icon from temp folder
1161 if (isset($_GET['showIcon'])) {
1162 readFileResource('showIcon', TMP_DIR
. 'install/files/icon/');
1164 // show css from temp folder
1165 if (isset($_GET['showCSS'])) {
1166 readFileResource('showCSS', TMP_DIR
. 'install/files/acp/style/setup/');
1168 // show fonts from temp folder
1169 if (isset($_GET['showFont'])) {
1170 readFileResource('showFont', TMP_DIR
. 'install/files/font/');
1173 // check whether setup files are already unzipped
1174 if (!file_exists(TMP_DIR
. 'install/files/lib/system/WCFSetup.class.php')) {
1175 // try to unzip all setup files into temp folder
1176 $tar = new Tar(SETUP_FILE
);
1177 $contentList = $tar->getContentList();
1178 if (empty($contentList)) {
1179 throw new SystemException("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken.");
1182 foreach ($contentList as $file) {
1183 foreach ($neededFilesPattern as $pattern) {
1184 if (preg_match($pattern, $file['filename'])) {
1185 // create directory if not exists
1186 $dir = TMP_DIR
. dirname($file['filename']);
1187 if (!@is_dir
($dir)) {
1188 @mkdir
($dir, 0777, true);
1189 BasicFileUtil
::makeWritable($dir);
1192 $tar->extract($file['index'], TMP_DIR
. $file['filename']);
1198 // create cache folders
1199 @mkdir
(TMP_DIR
. 'setup/lang/cache/', 0777);
1200 BasicFileUtil
::makeWritable(TMP_DIR
. 'setup/lang/cache/');
1202 @mkdir
(TMP_DIR
. 'setup/template/compiled/', 0777);
1203 BasicFileUtil
::makeWritable(TMP_DIR
. 'setup/template/compiled/');
1206 if (!class_exists('wcf\system\WCFSetup')) {
1207 throw new SystemException("Cannot find class 'WCFSetup'");
1211 new \wcf\system\
WCFSetup();