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