Fixed `WCF::getActivePage()` failing for CMS pages
[GitHub/WoltLab/WCF.git] / wcfsetup / install.php
CommitLineData
158bd3ca
TD
1<?php
2/**
3 * This script tries to find the temp folder and unzip all setup files into.
e3369fd2 4 *
158bd3ca 5 * @author Marcel Werk
2b6cb5c2 6 * @copyright 2001-2015 WoltLab GmbH
158bd3ca
TD
7 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
8 */
392cd6cb 9// @codingStandardsIgnoreFile
158bd3ca 10// define constants
728b9dd6 11define('INSTALL_SCRIPT', __FILE__);
158bd3ca
TD
12define('INSTALL_SCRIPT_DIR', dirname(__FILE__).'/');
13define('SETUP_FILE', INSTALL_SCRIPT_DIR . 'WCFSetup.tar.gz');
14define('NO_IMPORTS', 1);
15
16// set exception handler
17set_exception_handler('handleException');
18// set php error handler
19set_error_handler('handleError', E_ALL);
20
21// define list of needed file
22$neededFilesPattern = array(
23 '!^setup/.*!',
7da7f7cc 24 '!^install/files/acp/images/wcfLogo.*!',
e94c3830 25 '!^install/files/acp/style/setup/.*!',
158bd3ca 26 '!^install/files/lib/data/.*!',
7da7f7cc 27 '!^install/files/icon/.*!',
e2a34399 28 '!^install/files/font/.*!',
158bd3ca
TD
29 '!^install/files/lib/system/.*!',
30 '!^install/files/lib/util/.*!',
158bd3ca
TD
31 '!^install/lang/.*!',
32 '!^install/packages/.*!');
33
34// define needed functions and classes
35/**
36 * WCF::handleException() calls the show method on exceptions that implement this interface.
37 *
f4f05aa5 38 * @package com.woltlab.wcf
158bd3ca
TD
39 * @author Marcel Werk
40 */
41interface IPrintableException {
42 public function show();
43}
44
45// define needed classes
46// needed are:
47// SystemException, PrintableException, BasicFileUtil, Tar, File, ZipFile
48/**
49 * A SystemException is thrown when an unexpected error occurs.
50 *
f4f05aa5 51 * @package com.woltlab.wcf
158bd3ca
TD
52 * @author Marcel Werk
53 */
54class SystemException extends \Exception implements IPrintableException {
55 protected $description;
56 protected $information = '';
57 protected $functions = '';
58
59 /**
60 * Creates a new SystemException.
61 *
ac52543a
MS
62 * @param string $message error message
63 * @param integer $code error code
64 * @param string $description description of the error
158bd3ca
TD
65 */
66 public function __construct($message = '', $code = 0, $description = '') {
67 parent::__construct($message, $code);
68 $this->description = $description;
69 }
70
71 /**
72 * Returns the description of this exception.
73 *
39bea7dd 74 * @return string
158bd3ca
TD
75 */
76 public function getDescription() {
77 return $this->description;
78 }
79
80 /**
81 * Prints this exception.
82 * This method is called by WCF::handleException().
83 */
84 public function show() {
85 ?>
86<html>
87<head>
88<title>Fatal error: <?php echo htmlspecialchars($this->getMessage()); ?></title>
158bd3ca 89
53e00c6b
L
90<style type="text/css">
91 body {
92 font-family: Verdana, Helvetica, sans-serif;
93 font-size: 0.8em;
94 }
95 div {
96 border: 1px outset lightgrey;
97 padding: 3px;
98 background-color: lightgrey;
99 }
100
101 div div {
102 border: 1px inset lightgrey;
103 padding: 4px;
104 }
105
106 h1 {
107 background-color: #154268;
108 padding: 4px;
109 color: #fff;
110 margin: 0 0 3px 0;
111 font-size: 1.15em;
112 }
113 h2 {
114 font-size: 1.1em;
115 margin-bottom: 0;
116 }
117
118 pre, p {
119 margin: 0;
120 }
158bd3ca
TD
121</style>
122</head>
53e00c6b 123
158bd3ca
TD
124<body>
125 <div>
126 <h1>Fatal error: <?php echo htmlspecialchars($this->getMessage()); ?></h1>
2d63c13c 127
158bd3ca
TD
128 <div>
129 <p><?php echo $this->getDescription(); ?></p>
130 <?php if ($this->getCode()) { ?><p>You get more information about the problem in our knowledge base: <a href="http://www.woltlab.com/help/?code=<?php echo intval($this->getCode()); ?>">http://www.woltlab.com/help/?code=<?php echo intval($this->getCode()); ?></a></p><?php } ?>
131
132 <h2>Information:</h2>
133 <p>
134 <b>error message:</b> <?php echo htmlspecialchars($this->getMessage()); ?><br />
135 <b>error code:</b> <?php echo intval($this->getCode()); ?><br />
136 <?php echo $this->information; ?>
137 <b>file:</b> <?php echo htmlspecialchars($this->getFile()); ?> (<?php echo $this->getLine(); ?>)<br />
138 <b>php version:</b> <?php echo htmlspecialchars(phpversion()); ?><br />
139 <b>wcf version:</b> <?php if (defined('WCF_VERSION')) echo WCF_VERSION; ?><br />
140 <b>date:</b> <?php echo gmdate('r'); ?><br />
141 <b>request:</b> <?php if (isset($_SERVER['REQUEST_URI'])) echo htmlspecialchars($_SERVER['REQUEST_URI']); ?><br />
142 <b>referer:</b> <?php if (isset($_SERVER['HTTP_REFERER'])) echo htmlspecialchars($_SERVER['HTTP_REFERER']); ?><br />
143 </p>
144
145 <h2>Stacktrace:</h2>
146 <pre><?php echo htmlspecialchars($this->getTraceAsString()); ?></pre>
147 </div>
148
149 <?php echo $this->functions; ?>
150 </div>
151</body>
152</html>
153
154<?php
155 }
156}
157
158bd3ca
TD
158/**
159 * Loads the required classes automatically.
160 */
db8aa273 161spl_autoload_register(function($className) {
158bd3ca
TD
162 $namespaces = explode('\\', $className);
163 if (count($namespaces) > 1) {
164 // remove 'wcf' component
165 array_shift($namespaces);
166
167 $className = implode('/', $namespaces);
01bd2eff 168 $classPath = TMP_DIR . 'install/files/lib/' . $className . '.class.php';
158bd3ca
TD
169 if (file_exists($classPath)) {
170 require_once($classPath);
171 }
172 }
db8aa273 173});
158bd3ca
TD
174
175/**
176 * Escapes strings for execution in sql queries.
ac52543a
MS
177 *
178 * @param string $string
179 * @return string
158bd3ca
TD
180 */
181function escapeString($string) {
182 return \wcf\system\WCF::getDB()->escapeString($string);
183}
184
185/**
186 * Calls the show method on the given exception.
187 *
9a132951 188 * @param mixed $e
158bd3ca 189 */
9a132951
M
190function handleException($e) {
191 try {
192 if (!($e instanceof \Exception)) throw $e;
193
194 if ($e instanceof IPrintableException || $e instanceof \wcf\system\exception\IPrintableException) {
195 $e->show();
196 exit;
197 }
198 }
199 catch (\Throwable $exception) {
200 die("<pre>WCF::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
201 }
202 catch (\Exception $exception) {
203 die("<pre>WCF::handleException() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString());
158bd3ca 204 }
158bd3ca
TD
205}
206
207/**
208 * Catches php errors and throws instead a system exception.
209 *
210 * @param integer $errorNo
211 * @param string $message
212 * @param string $filename
213 * @param integer $lineNo
2b770bdd 214 * @throws SystemException
158bd3ca
TD
215 */
216function handleError($errorNo, $message, $filename, $lineNo) {
217 if (error_reporting() != 0) {
218 $type = 'error';
219 switch ($errorNo) {
220 case 2: $type = 'warning';
221 break;
222 case 8: $type = 'notice';
223 break;
224 }
225
226 throw new SystemException('PHP '.$type.' in file '.$filename.' ('.$lineNo.'): '.$message, 0);
227 }
228}
229
230/**
231 * BasicFileUtil contains file-related functions.
232 *
f4f05aa5 233 * @package com.woltlab.wcf
158bd3ca
TD
234 * @author Marcel Werk
235 */
236class BasicFileUtil {
d8fa09e0
AE
237 /**
238 * chmod mode
239 * @var integer
240 */
241 protected static $mode = null;
242
158bd3ca
TD
243 /**
244 * Tries to find the temp folder.
245 *
246 * @return string
2b770bdd 247 * @throws SystemException
158bd3ca
TD
248 */
249 public static function getTempFolder() {
158bd3ca
TD
250 // use tmp folder in document root by default
251 if (!empty($_SERVER['DOCUMENT_ROOT'])) {
069cd37e
MW
252 if (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false) {
253 // strato bugfix
254 // create tmp folder in document root automatically
255 if (!@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
256 @mkdir($_SERVER['DOCUMENT_ROOT'].'/tmp/', 0777);
257 try {
258 self::makeWritable($_SERVER['DOCUMENT_ROOT'].'/tmp/');
259 }
260 catch (SystemException $e) {}
261 }
158bd3ca 262 }
069cd37e
MW
263 if (@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp') && @is_writable($_SERVER['DOCUMENT_ROOT'].'/tmp')) {
264 return $_SERVER['DOCUMENT_ROOT'].'/tmp/';
158bd3ca
TD
265 }
266 }
e3369fd2 267
069cd37e
MW
268 if (isset($_ENV['TMP']) && @is_writable($_ENV['TMP'])) {
269 return $_ENV['TMP'] . '/';
158bd3ca 270 }
069cd37e
MW
271 if (isset($_ENV['TEMP']) && @is_writable($_ENV['TEMP'])) {
272 return $_ENV['TEMP'] . '/';
273 }
274 if (isset($_ENV['TMPDIR']) && @is_writable($_ENV['TMPDIR'])) {
275 return $_ENV['TMPDIR'] . '/';
276 }
e3369fd2 277
069cd37e
MW
278 if (($path = ini_get('upload_tmp_dir')) && @is_writable($path)) {
279 return $path . '/';
280 }
281 if (@file_exists('/tmp/') && @is_writable('/tmp/')) {
282 return '/tmp/';
158bd3ca 283 }
069cd37e
MW
284 if (function_exists('session_save_path') && ($path = session_save_path()) && @is_writable($path)) {
285 return $path . '/';
286 }
2d63c13c 287
5805a819 288 $path = INSTALL_SCRIPT_DIR.'tmp/';
069cd37e
MW
289 if (@file_exists($path) && @is_writable($path)) {
290 return $path;
291 }
292 else {
293 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.');
294 }
295 }
296
297 /**
298 * Returns the temp folder for the installation.
299 *
300 * @return string
301 */
302 public static function getInstallTempFolder() {
303 $dir = self::getTempFolder() . TMP_FILE_PREFIX . '/';
304 @mkdir($dir);
305 self::makeWritable($dir);
158bd3ca 306
158bd3ca
TD
307 return $dir;
308 }
1232bce2
AE
309
310 /**
311 * Tries to make a file or directory writable. It starts of with the least
d8fa09e0 312 * permissions and goes up until 0666 for files and 0777 for directories.
1232bce2
AE
313 *
314 * @param string $filename
2b770bdd 315 * @throws SystemException
1232bce2
AE
316 */
317 public static function makeWritable($filename) {
043b049d 318 if (!file_exists($filename)) {
1232bce2 319 return;
158bd3ca
TD
320 }
321
d8fa09e0
AE
322 // determine mode
323 if (self::$mode === null) {
324 // do not use PHP_OS here, as this represents the system it was built on != running on
0436b618
AE
325 // php_uname() is forbidden on some strange hosts; PHP_EOL is reliable
326 if (PHP_EOL == "\r\n") {
327 // Windows
d8fa09e0
AE
328 self::$mode = 0777;
329 }
330 else {
0436b618 331 // anything but Windows
adbd8054
AE
332 clearstatcache();
333
d8fa09e0
AE
334 self::$mode = 0666;
335
0c1810be
AE
336 $tmpFilename = '__permissions_'.sha1(time()).'.txt';
337 @touch($tmpFilename);
d8fa09e0
AE
338
339 // create a new file and check the file owner, if it is the same
340 // as this file (uploaded through FTP), we can safely grant write
341 // permissions exclusively to the owner rather than everyone
0c1810be 342 if (file_exists($tmpFilename)) {
d8fa09e0 343 $scriptOwner = fileowner(__FILE__);
0c1810be 344 $fileOwner = fileowner($tmpFilename);
d8fa09e0
AE
345
346 if ($scriptOwner === $fileOwner) {
347 self::$mode = 0644;
348 }
349
0c1810be 350 @unlink($tmpFilename);
d8fa09e0
AE
351 }
352 }
353 }
158bd3ca 354
1232bce2 355 if (is_dir($filename)) {
d8fa09e0 356 if (self::$mode == 0644) {
7fe5312d 357 @chmod($filename, 0755);
1232bce2 358 }
d8fa09e0 359 else {
7fe5312d 360 @chmod($filename, 0777);
1232bce2
AE
361 }
362 }
d8fa09e0 363 else {
7fe5312d 364 @chmod($filename, self::$mode);
d8fa09e0
AE
365 }
366
367 if (!is_writable($filename)) {
368 throw new SystemException("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider.");
369 }
158bd3ca
TD
370 }
371}
372
373/**
374 * Opens tar or tar.gz archives.
375 *
376 * Usage:
377 * ------
378 * $tar = new Tar('archive.tar');
379 * $contentList = $tar->getContentList();
380 * foreach ($contentList as $key => $val) {
381 * $tar->extract($key, DESTINATION);
382 * }
383 */
384class Tar {
385 protected $archiveName = '';
386 protected $contentList = array();
387 protected $opened = false;
388 protected $read = false;
389 protected $file = null;
390 protected $isZipped = false;
391 protected $mode = 'rb';
392
393 /**
394 * Creates a new Tar object.
395 * archiveName must be tarball or gzipped tarball
396 *
39bea7dd 397 * @param string $archiveName
2b770bdd 398 * @throws SystemException
158bd3ca
TD
399 */
400 public function __construct($archiveName) {
158bd3ca 401 if (!is_file($archiveName)) {
4fe0b42b 402 throw new SystemException("unable to find tar archive '".$archiveName."'");
158bd3ca
TD
403 }
404
405 $this->archiveName = $archiveName;
406 $this->open();
407 $this->readContent();
408 }
409
410 /**
411 * Destructor of this class, closes tar archive.
412 */
413 public function __destruct() {
414 $this->close();
415 }
416
417 /**
418 * Opens the tar archive and stores filehandle.
419 */
420 public function open() {
421 if (!$this->opened) {
422 if ($this->isZipped) $this->file = new ZipFile($this->archiveName, $this->mode);
423 else {
424 // test compression
425 $this->file = new File($this->archiveName, $this->mode);
426 if ($this->file->read(2) == "\37\213") {
427 $this->file->close();
428 $this->isZipped = true;
429 $this->file = new ZipFile($this->archiveName, $this->mode);
430 }
431 else {
432 $this->file->seek(0);
433 }
434 }
435 $this->opened = true;
436 }
437 }
438
439 /**
440 * Closes the opened file.
441 */
442 public function close() {
443 if ($this->opened) {
444 $this->file->close();
445 $this->opened = false;
446 }
447 }
448
449 /**
450 * Returns the table of contents (TOC) list for this tar archive.
451 *
39bea7dd 452 * @return array list of content
158bd3ca
TD
453 */
454 public function getContentList() {
455 if (!$this->read) {
456 $this->open();
457 $this->readContent();
458 }
459 return $this->contentList;
460 }
461
462 /**
463 * Returns an associative array with information
464 * about a specific file in the archive.
465 *
ac52543a
MS
466 * @param mixed $fileIndex index or name of the requested file
467 * @return array
2b770bdd 468 * @throws SystemException
158bd3ca
TD
469 */
470 public function getFileInfo($fileIndex) {
471 if (!is_int($fileIndex)) {
472 $fileIndex = $this->getIndexByFilename($fileIndex);
473 }
474
475 if (!isset($this->contentList[$fileIndex])) {
6286572b 476 throw new SystemException("Tar: could find file '".$fileIndex."' in archive");
158bd3ca
TD
477 }
478 return $this->contentList[$fileIndex];
479 }
480
481 /**
482 * Searchs a file in the tar archive
483 * and returns the numeric fileindex.
484 * Returns false if not found.
485 *
39bea7dd
MS
486 * @param string $filename
487 * @return integer index of the requested file
158bd3ca
TD
488 */
489 public function getIndexByFilename($filename) {
490 foreach ($this->contentList as $index => $file) {
491 if ($file['filename'] == $filename) {
492 return $index;
493 }
494 }
495 return false;
496 }
497
498 /**
499 * Extracts a specific file and returns the content as string.
500 * Returns false if extraction failed.
501 *
39bea7dd
MS
502 * @param mixed $index index or name of the requested file
503 * @return string content of the requested file
158bd3ca
TD
504 */
505 public function extractToString($index) {
506 if (!$this->read) {
507 $this->open();
508 $this->readContent();
509 }
510 $header = $this->getFileInfo($index);
511
512 // can not extract a folder
513 if ($header['type'] != 'file') {
514 return false;
515 }
516
517 // seek to offset
518 $this->file->seek($header['offset']);
519
520 // read data
521 $content = '';
522 $n = floor($header['size'] / 512);
523 for($i = 0; $i < $n; $i++) {
524 $content .= $this->file->read(512);
525 }
526 if(($header['size'] % 512) != 0) {
527 $buffer = $this->file->read(512);
528 $content .= substr($buffer, 0, ($header['size'] % 512));
529 }
530
531 return $content;
532 }
533
534 /**
535 * Extracts a specific file and writes it's content
536 * to the file specified with $destination.
537 *
39bea7dd
MS
538 * @param mixed $index index or name of the requested file
539 * @param string $destination
2b770bdd
MS
540 * @return boolean
541 * @throws SystemException
158bd3ca
TD
542 */
543 public function extract($index, $destination) {
544 if (!$this->read) {
545 $this->open();
546 $this->readContent();
547 }
548 $header = $this->getFileInfo($index);
549
550 // can not extract a folder
551 if ($header['type'] != 'file') {
552 return false;
553 }
554
555 // seek to offset
556 $this->file->seek($header['offset']);
557
558 $targetFile = new File($destination);
559
560 // read data
561 $n = floor($header['size'] / 512);
562 for ($i = 0; $i < $n; $i++) {
563 $content = $this->file->read(512);
564 $targetFile->write($content, 512);
565 }
566 if (($header['size'] % 512) != 0) {
567 $content = $this->file->read(512);
568 $targetFile->write($content, ($header['size'] % 512));
569 }
570
571 $targetFile->close();
1232bce2 572 BasicFileUtil::makeWritable($destination);
158bd3ca
TD
573
574 if ($header['mtime']) {
575 @$targetFile->touch($header['mtime']);
576 }
577
578 // check filesize
579 if (filesize($destination) != $header['size']) {
4fe0b42b 580 throw new SystemException("Could not untar file '".$header['filename']."' to '".$destination."'. Maybe disk quota exceeded in folder '".dirname($destination)."'.");
158bd3ca
TD
581 }
582
583 return true;
584 }
585
586 /**
587 * Reads table of contents (TOC) from tar archive.
588 * This does not get the entire to memory but only parts of it.
589 */
590 protected function readContent() {
591 $this->contentList = array();
592 $this->read = true;
593 $i = 0;
594
595 // Read the 512 bytes header
db8aa273 596 $longFilename = null;
158bd3ca
TD
597 while (strlen($binaryData = $this->file->read(512)) != 0) {
598 // read header
599 $header = $this->readHeader($binaryData);
600 if ($header === false) {
601 continue;
602 }
db8aa273
AE
603
604 // fixes a bug that files with long names aren't correctly
605 // extracted
606 if ($longFilename !== null) {
607 $header['filename'] = $longFilename;
608 $longFilename = null;
609 }
610 if ($header['typeflag'] == 'L') {
611 $format = 'Z'.$header['size'].'filename';
612
613 $fileData = unpack($format, $this->file->read(512));
614 $longFilename = $fileData['filename'];
615 $header['size'] = 0;
616 }
617 // don't include the @LongLink file in the content list
618 else {
619 $this->contentList[$i] = $header;
620 $this->contentList[$i]['index'] = $i;
621 $i++;
622 }
158bd3ca
TD
623
624 $this->file->seek($this->file->tell() + (512 * ceil(($header['size'] / 512))));
625 }
626 }
627
628 /**
629 * Unpacks file header for one file entry.
db8aa273 630 *
39bea7dd
MS
631 * @param string $binaryData
632 * @return array $fileheader
158bd3ca
TD
633 */
634 protected function readHeader($binaryData) {
635 if (strlen($binaryData) != 512) {
636 return false;
637 }
2d63c13c 638
158bd3ca
TD
639 $header = array();
640 $checksum = 0;
641 // First part of the header
642 for ($i = 0; $i < 148; $i++) {
643 $checksum += ord(substr($binaryData, $i, 1));
644 }
645 // Calculate the checksum
646 // Ignore the checksum value and replace it by ' ' (space)
647 for ($i = 148; $i < 156; $i++) {
648 $checksum += ord(' ');
649 }
650 // Last part of the header
651 for ($i = 156; $i < 512; $i++) {
652 $checksum += ord(substr($binaryData, $i, 1));
653 }
2d63c13c 654
db8aa273
AE
655 // extract values
656 $format = 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor/Z155prefix';
32b198a0
AE
657
658 $data = unpack($format, $binaryData);
158bd3ca
TD
659
660 // Extract the properties
379875ee 661 $header['checksum'] = octdec(trim($data['checksum']));
158bd3ca
TD
662 if ($header['checksum'] == $checksum) {
663 $header['filename'] = trim($data['filename']);
379875ee
MS
664 $header['mode'] = octdec(trim($data['mode']));
665 $header['uid'] = octdec(trim($data['uid']));
666 $header['gid'] = octdec(trim($data['gid']));
667 $header['size'] = octdec(trim($data['size']));
668 $header['mtime'] = octdec(trim($data['mtime']));
158bd3ca
TD
669 $header['prefix'] = trim($data['prefix']);
670 if ($header['prefix']) {
671 $header['filename'] = $header['prefix'].'/'.$header['filename'];
672 }
673 if (($header['typeflag'] = $data['typeflag']) == '5') {
674 $header['size'] = 0;
675 $header['type'] = 'folder';
676 }
677 else {
678 $header['type'] = 'file';
679 }
680 $header['offset'] = $this->file->tell();
681
682 return $header;
683 }
684 else {
685 return false;
686 }
687 }
688}
689
690/**
691 * The File class handles all file operations.
692 *
693 * Example:
694 * using php functions:
695 * $fp = fopen('filename', 'wb');
696 * fwrite($fp, '...');
697 * fclose($fp);
698 *
699 * using this class:
700 * $file = new File('filename');
701 * $file->write('...');
702 * $file->close();
703 *
704 * @author Marcel Werk
705 */
706class File {
707 protected $resource = null;
708 protected $filename;
709
710 /**
711 * Opens a new file.
712 *
39bea7dd
MS
713 * @param string $filename
714 * @param string $mode
2b770bdd 715 * @throws SystemException
158bd3ca
TD
716 */
717 public function __construct($filename, $mode = 'wb') {
718 $this->filename = $filename;
719 $this->resource = fopen($filename, $mode);
720 if ($this->resource === false) {
4fe0b42b 721 throw new SystemException('Can not open file ' . $filename);
158bd3ca
TD
722 }
723 }
724
725 /**
726 * Calls the specified function on the open file.
727 * Do not call this function directly. Use $file->write('') instead.
728 *
39bea7dd
MS
729 * @param string $function
730 * @param array $arguments
71952a87 731 * @return mixed
2b770bdd 732 * @throws SystemException
158bd3ca
TD
733 */
734 public function __call($function, $arguments) {
735 if (function_exists('f' . $function)) {
736 array_unshift($arguments, $this->resource);
39bea7dd 737 return call_user_func_array('f' . $function, $arguments);
158bd3ca
TD
738 }
739 else if (function_exists($function)) {
740 array_unshift($arguments, $this->filename);
39bea7dd 741 return call_user_func_array($function, $arguments);
158bd3ca
TD
742 }
743 else {
4fe0b42b 744 throw new SystemException('Can not call file method ' . $function);
158bd3ca
TD
745 }
746 }
747}
748
749/**
750 * The File class handles all file operations on a zipped file.
751 *
752 * @author Marcel Werk
753 */
754class ZipFile extends File {
eedfeca6
AE
755 /**
756 * checks if gz*64 functions are available instead of gz*
757 * https://bugs.php.net/bug.php?id=53829
758 * @var boolean
759 */
760 protected static $gzopen64 = null;
761
158bd3ca
TD
762 /**
763 * Opens a new zipped file.
764 *
39bea7dd
MS
765 * @param string $filename
766 * @param string $mode
2b770bdd 767 * @throws SystemException
158bd3ca
TD
768 */
769 public function __construct($filename, $mode = 'wb') {
eedfeca6
AE
770 if (self::$gzopen64 === null) {
771 self::$gzopen64 = (function_exists('gzopen64'));
772 }
773
158bd3ca 774 $this->filename = $filename;
eedfeca6 775 if (!self::$gzopen64 && !function_exists('gzopen')) {
4fe0b42b 776 throw new SystemException('Can not find functions of the zlib extension');
158bd3ca 777 }
eedfeca6 778 $this->resource = (self::$gzopen64 ? @gzopen64($filename, $mode) : @gzopen($filename, $mode));
158bd3ca 779 if ($this->resource === false) {
4fe0b42b 780 throw new SystemException('Can not open file ' . $filename);
158bd3ca
TD
781 }
782 }
783
784 /**
785 * Calls the specified function on the open file.
786 *
39bea7dd
MS
787 * @param string $function
788 * @param array $arguments
71952a87 789 * @return mixed
2b770bdd 790 * @throws SystemException
158bd3ca
TD
791 */
792 public function __call($function, $arguments) {
eedfeca6
AE
793 if (self::$gzopen64 && function_exists('gz' . $function . '64')) {
794 array_unshift($arguments, $this->resource);
795 return call_user_func_array('gz' . $function . '64', $arguments);
796 }
797 else if (function_exists('gz' . $function)) {
158bd3ca 798 array_unshift($arguments, $this->resource);
39bea7dd 799 return call_user_func_array('gz' . $function, $arguments);
158bd3ca
TD
800 }
801 else if (function_exists($function)) {
802 array_unshift($arguments, $this->filename);
39bea7dd 803 return call_user_func_array($function, $arguments);
158bd3ca
TD
804 }
805 else {
4fe0b42b 806 throw new SystemException('Can not call method ' . $function);
158bd3ca
TD
807 }
808 }
809
810 /**
811 * Returns the filesize of the unzipped file
812 */
813 public function getFileSize() {
814 $byteBlock = 1<<14;
815 $eof = $byteBlock;
816
817 // the correction is for zip files that are too small
818 // to get in the first while loop
819 $correction = 1;
820 while ($this->seek($eof) == 0) {
821 $eof += $byteBlock;
822 $correction = 0;
823 }
824
825 while ($byteBlock > 1) {
826 $byteBlock >>= 1;
827 $eof += $byteBlock * ($this->seek($eof) ? -1 : 1);
828 }
829
830 if ($this->seek($eof) == -1) $eof -= 1;
831
832 $this->rewind();
833 return $eof - $correction;
834 }
835}
836
837// let's go
838// get temp file prefix
839if (isset($_REQUEST['tmpFilePrefix'])) {
840 $prefix = preg_replace('/[^a-f0-9_]+/', '', $_REQUEST['tmpFilePrefix']);
841}
842else {
843 $prefix = substr(sha1(uniqid(microtime())), 0, 8);
844}
845define('TMP_FILE_PREFIX', $prefix);
846
847// try to find the temp folder
99be741e 848define('TMP_DIR', BasicFileUtil::getInstallTempFolder());
158bd3ca 849
7da7f7cc
AE
850/**
851 * Reads a file resource from temp folder.
852 *
853 * @param string $key
854 * @param string $directory
855 */
856function readFileResource($key, $directory) {
ec6e78b9 857 if (preg_match('~[\w\-]+\.(css|jpg|png|svg|eot|woff|ttf)~', $_GET[$key], $match)) {
7da7f7cc
AE
858 switch ($match[1]) {
859 case 'css':
860 header('Content-Type: text/css');
861 break;
862
863 case 'jpg':
864 header('Content-Type: image/jpg');
865 break;
866
867 case 'png':
868 header('Content-Type: image/png');
869 break;
870
871 case 'svg':
872 header('Content-Type: image/svg+xml');
873 break;
ec6e78b9
MW
874
875 case 'eot':
876 header('Content-Type: application/vnd.ms-fontobject');
877 break;
878
879 case 'woff':
880 header('Content-Type: application/font-woff');
881 break;
882
883 case 'ttf':
884 header('Content-Type: application/octet-stream');
885 break;
7da7f7cc
AE
886 }
887
2d9861cd
AE
888 header('Expires: '.gmdate('D, d M Y H:i:s', time() + 3600).' GMT');
889 header('Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT');
890 header('Cache-Control: public, max-age=3600');
891
7da7f7cc 892 readfile($directory . $_GET[$key]);
158bd3ca
TD
893 }
894 exit;
895}
896
7da7f7cc
AE
897// show image from temp folder
898if (isset($_GET['showImage'])) {
899 readFileResource('showImage', TMP_DIR . 'install/files/acp/images/');
900}
901// show icon from temp folder
902if (isset($_GET['showIcon'])) {
903 readFileResource('showIcon', TMP_DIR . 'install/files/icon/');
904}
905// show css from temp folder
906if (isset($_GET['showCSS'])) {
e94c3830 907 readFileResource('showCSS', TMP_DIR . 'install/files/acp/style/setup/');
7da7f7cc 908}
ec6e78b9
MW
909// show fonts from temp folder
910if (isset($_GET['showFont'])) {
911 readFileResource('showFont', TMP_DIR . 'install/files/font/');
912}
7da7f7cc 913
53e00c6b 914// check whether setup files are already unzipped
158bd3ca
TD
915if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) {
916 // try to unzip all setup files into temp folder
917 $tar = new Tar(SETUP_FILE);
918 $contentList = $tar->getContentList();
15fa2802 919 if (empty($contentList)) {
3a4862d3 920 throw new SystemException("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken.");
158bd3ca
TD
921 }
922
923 foreach ($contentList as $file) {
924 foreach ($neededFilesPattern as $pattern) {
925 if (preg_match($pattern, $file['filename'])) {
926 // create directory if not exists
927 $dir = TMP_DIR . dirname($file['filename']);
928 if (!@is_dir($dir)) {
929 @mkdir($dir, 0777, true);
1232bce2 930 BasicFileUtil::makeWritable($dir);
158bd3ca
TD
931 }
932
933 $tar->extract($file['index'], TMP_DIR . $file['filename']);
934 }
935 }
936 }
937 $tar->close();
938
939 // create cache folders
940 @mkdir(TMP_DIR . 'setup/lang/cache/', 0777);
1232bce2 941 BasicFileUtil::makeWritable(TMP_DIR . 'setup/lang/cache/');
158bd3ca
TD
942
943 @mkdir(TMP_DIR . 'setup/template/compiled/', 0777);
1232bce2 944 BasicFileUtil::makeWritable(TMP_DIR . 'setup/template/compiled/');
158bd3ca
TD
945}
946
947if (!class_exists('wcf\system\WCFSetup')) {
3a4862d3 948 throw new SystemException("Cannot find class 'WCFSetup'");
158bd3ca
TD
949}
950
951// start setup
dcb3a44c 952new \wcf\system\WCFSetup();