Add the new variable `FileProcessorFormField::$bigPreview` with getter and setter.
[GitHub/WoltLab/WCF.git] / wcfsetup / install.php
CommitLineData
a9229942 1<?php // @codingStandardsIgnoreFile
158bd3ca
TD
2/**
3 * This script tries to find the temp folder and unzip all setup files into.
5176cbcb 4 *
158bd3ca 5 * @author Marcel Werk
7b7b9764 6 * @copyright 2001-2019 WoltLab GmbH
158bd3ca
TD
7 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
8 */
a9229942 9
728b9dd6 10define('INSTALL_SCRIPT', __FILE__);
5176cbcb 11define('INSTALL_SCRIPT_DIR', dirname(__FILE__) . '/');
315f043c
TD
12
13define('WCF_DIR', INSTALL_SCRIPT_DIR);
14define('RELATIVE_WCF_DIR', './');
15
158bd3ca 16define('SETUP_FILE', INSTALL_SCRIPT_DIR . 'WCFSetup.tar.gz');
315f043c 17
158bd3ca
TD
18define('NO_IMPORTS', 1);
19
058cbd6a 20$neededFilesPattern = [
158bd3ca 21 '!^setup/.*!',
07937b16 22 '!^install/files/acp/images/woltlabSuite.*!',
e94c3830 23 '!^install/files/acp/style/setup/.*!',
9a73b6dc 24 '!^install/files/lib/data/.*!',
49c6a229 25 '!^install/files/lib/event/.*!',
158bd3ca
TD
26 '!^install/files/lib/system/.*!',
27 '!^install/files/lib/util/.*!',
158bd3ca 28 '!^install/lang/.*!',
01bf429b
TD
29 '!^install/packages/.*!',
30];
1a2817c4 31
5176cbcb
MW
32function sanitizeStacktrace(\Throwable $e, bool $ignorePaths = false)
33{
830e152e
TD
34 $trace = $e->getTrace();
35
36 return array_map(function ($item) use ($ignorePaths) {
37 if (!isset($item['file'])) $item['file'] = '[internal function]';
38 if (!isset($item['line'])) $item['line'] = '?';
39 if (!isset($item['class'])) $item['class'] = '';
40 if (!isset($item['type'])) $item['type'] = '';
41 if (!isset($item['args'])) $item['args'] = [];
42
43 try {
44 if (!empty($item['args'])) {
45 if ($item['class']) {
46 $function = new \ReflectionMethod($item['class'], $item['function']);
5176cbcb 47 } else {
830e152e
TD
48 $function = new \ReflectionFunction($item['function']);
49 }
50
51 $parameters = $function->getParameters();
52 $i = 0;
53 foreach ($parameters as $parameter) {
54 $isSensitive = false;
55 if (
34ad4a15
TD
56 !empty($parameter->getAttributes(\wcf\SensitiveArgument::class))
57 || !empty($parameter->getAttributes(\SensitiveParameter::class))
830e152e
TD
58 ) {
59 $isSensitive = true;
60 }
61 if (\preg_match(
62 '/(?:^(?:password|passphrase|secret)|(?:Password|Passphrase|Secret))/',
63 $parameter->getName()
64 )) {
65 $isSensitive = true;
66 }
67
68 if ($isSensitive && isset($item['args'][$i])) {
69 $item['args'][$i] = '[redacted]';
70 }
71 $i++;
72 }
5176cbcb 73
830e152e
TD
74 // strip database credentials
75 if (
76 preg_match('~\\\\?wcf\\\\system\\\\database\\\\[a-zA-Z]*Database~', $item['class'])
77 || $item['class'] === 'PDO'
78 ) {
79 if ($item['function'] === '__construct') {
80 $item['args'] = array_map(function () {
81 return '[redacted]';
82 }, $item['args']);
83 }
84 }
85 }
86 } catch (\Throwable $e) {
87 $item['args'] = array_map(function () {
88 return '[error_during_sanitization]';
89 }, $item['args']);
90 }
5176cbcb 91
830e152e
TD
92 if (!$ignorePaths) {
93 $item['args'] = array_map(function ($item) {
94 if (!is_string($item)) return $item;
5176cbcb
MW
95
96 if (preg_match('~^(' . preg_quote($_SERVER['DOCUMENT_ROOT'], '~') . '|' . preg_quote(WCF_DIR, '~') . ')~', $item)) {
830e152e
TD
97 $item = sanitizePath($item);
98 }
99
100 return $item;
101 }, $item['args']);
5176cbcb 102
830e152e
TD
103 $item['file'] = sanitizePath($item['file']);
104 }
5176cbcb 105
830e152e
TD
106 return $item;
107 }, $trace);
108}
109
5176cbcb
MW
110function printException($e)
111{
830e152e
TD
112 $exceptionTitle = 'An error has occurred';
113 $exceptionSubtitle = '';
114 $exceptionExplanation = '';
115 $exceptionID = '';
5176cbcb
MW
116?>
117 <!DOCTYPE html>
830e152e 118 <html>
5176cbcb
MW
119
120 <head>
121 <meta charset="utf-8">
122 <?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?>
830e152e 123 <title>Fatal Error: <?php echo htmlspecialchars($e->getMessage()); ?></title>
5176cbcb 124 <?php } else { ?>
830e152e 125 <title>Fatal Error</title>
5176cbcb
MW
126 <?php } ?>
127 <meta name="viewport" content="width=device-width, initial-scale=1">
128 <style>
129 .exceptionBody {
130 background-color: rgb(250, 250, 250);
131 color: rgb(44, 62, 80);
132 margin: 0;
133 padding: 0;
134 }
135
136 .exceptionContainer {
137 box-sizing: border-box;
138 font-family: 'Segoe UI', 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
139 font-size: 14px;
140 padding-bottom: 20px;
141 }
142
143 .exceptionContainer * {
144 box-sizing: inherit;
145 line-height: 1.5em;
146 margin: 0;
147 padding: 0;
148 }
149
150 .exceptionHeader {
151 background-color: rgb(58, 109, 156);
152 padding: 30px 0;
153 }
154
155 .exceptionTitle {
156 color: #fff;
157 font-size: 28px;
158 font-weight: 300;
159 }
160
161 .exceptionErrorCode {
162 color: #fff;
163 margin-top: .5em;
164 }
165
166 .exceptionErrorCode .exceptionInlineCode {
167 background-color: rgb(43, 79, 113);
168 border-radius: 3px;
169 color: #fff;
170 font-family: monospace;
171 padding: 3px 10px;
172 white-space: nowrap;
173 }
174
175 .exceptionSubtitle {
176 border-bottom: 1px solid rgb(238, 238, 238);
177 font-size: 24px;
178 font-weight: 300;
179 margin-bottom: 15px;
180 padding-bottom: 10px;
181 }
182
183 .exceptionContainer>.exceptionBoundary {
184 margin-top: 30px;
185 }
186
187 .exceptionText .exceptionInlineCodeWrapper {
188 border: 1px solid rgb(169, 169, 169);
189 border-radius: 3px;
190 padding: 2px 5px;
191 }
192
193 .exceptionText .exceptionInlineCode {
194 font-family: monospace;
195 white-space: nowrap;
196 }
197
198 .exceptionFieldTitle {
199 color: rgb(59, 109, 169);
200 }
201
202 .exceptionFieldTitle .exceptionColon {
203 /* hide colon in browser, but will be visible after copy & paste */
204 opacity: 0;
205 }
206
207 .exceptionFieldValue {
208 font-size: 18px;
209 min-height: 1.5em;
210 }
211
212 pre.exceptionFieldValue {
213 font-size: 14px;
214 white-space: pre-wrap;
215 }
216
217 .exceptionSystemInformation,
218 .exceptionErrorDetails,
219 .exceptionStacktrace {
220 list-style-type: none;
221 }
222
223 .exceptionSystemInformation>li:not(:first-child),
224 .exceptionErrorDetails>li:not(:first-child) {
225 margin-top: 10px;
226 }
227
228 .exceptionStacktrace {
229 display: block;
230 margin-top: 5px;
231 overflow: auto;
232 padding-bottom: 20px;
233 }
234
235 .exceptionStacktraceFile,
236 .exceptionStacktraceFile span,
237 .exceptionStacktraceCall,
238 .exceptionStacktraceCall span {
239 font-family: monospace !important;
240 white-space: nowrap !important;
241 }
242
243 .exceptionStacktraceCall+.exceptionStacktraceFile {
244 margin-top: 5px;
245 }
246
247 .exceptionStacktraceCall {
248 padding-left: 40px;
249 }
250
251 .exceptionStacktraceCall,
252 .exceptionStacktraceCall span {
253 color: rgb(102, 102, 102) !important;
254 font-size: 13px !important;
255 }
256
257 /* mobile */
258 @media (max-width: 767px) {
259 .exceptionBoundary {
260 min-width: 320px;
261 padding: 0 10px;
830e152e 262 }
5176cbcb
MW
263
264 .exceptionText .exceptionInlineCodeWrapper {
265 display: inline-block;
266 overflow: auto;
830e152e 267 }
5176cbcb 268
830e152e 269 .exceptionErrorCode .exceptionInlineCode {
5176cbcb 270 font-size: 13px;
830e152e
TD
271 padding: 2px 5px;
272 }
5176cbcb
MW
273 }
274
275 /* desktop */
276 @media (min-width: 768px) {
277 .exceptionBoundary {
278 margin: 0 auto;
279 max-width: 1400px;
280 min-width: 1200px;
281 padding: 0 10px;
830e152e 282 }
5176cbcb
MW
283
284 .exceptionSystemInformation {
285 display: flex;
286 flex-wrap: wrap;
830e152e 287 }
5176cbcb
MW
288
289 .exceptionSystemInformation1,
290 .exceptionSystemInformation3,
291 .exceptionSystemInformation5 {
292 flex: 0 0 200px;
293 margin: 0 0 10px 0 !important;
830e152e 294 }
5176cbcb
MW
295
296 .exceptionSystemInformation2,
297 .exceptionSystemInformation4,
298 .exceptionSystemInformation6 {
299 flex: 0 0 calc(100% - 210px);
300 margin: 0 0 10px 10px !important;
301 max-width: calc(100% - 210px);
830e152e 302 }
5176cbcb
MW
303
304 .exceptionSystemInformation1 {
305 order: 1;
830e152e 306 }
5176cbcb
MW
307
308 .exceptionSystemInformation2 {
309 order: 2;
830e152e 310 }
5176cbcb
MW
311
312 .exceptionSystemInformation3 {
313 order: 3;
830e152e 314 }
5176cbcb
MW
315
316 .exceptionSystemInformation4 {
317 order: 4;
830e152e 318 }
5176cbcb
MW
319
320 .exceptionSystemInformation5 {
321 order: 5;
830e152e 322 }
5176cbcb
MW
323
324 .exceptionSystemInformation6 {
325 order: 6;
830e152e 326 }
5176cbcb
MW
327
328 .exceptionSystemInformation .exceptionFieldValue {
329 overflow: hidden;
330 text-overflow: ellipsis;
331 white-space: nowrap;
830e152e 332 }
5176cbcb
MW
333 }
334 </style>
335 </head>
336
337 <body class="exceptionBody">
338 <div class="exceptionContainer">
339 <div class="exceptionHeader">
340 <div class="exceptionBoundary">
341 <p class="exceptionTitle"><?php echo $exceptionTitle; ?></p>
342 <p class="exceptionErrorCode"><?php echo str_replace('{$exceptionID}', $exceptionID, $exceptionSubtitle); ?></p>
830e152e 343 </div>
5176cbcb
MW
344 </div>
345
346 <div class="exceptionBoundary">
347 <?php echo $exceptionExplanation; ?>
348 </div>
349 <?php if (!defined('EXCEPTION_PRIVACY') || EXCEPTION_PRIVACY !== 'private') { ?>
830e152e 350 <div class="exceptionBoundary">
5176cbcb
MW
351 <p class="exceptionSubtitle">System Information</p>
352 <ul class="exceptionSystemInformation">
353 <li class="exceptionSystemInformation1">
354 <p class="exceptionFieldTitle">PHP Version<span class="exceptionColon">:</span></p>
355 <p class="exceptionFieldValue"><?php echo htmlspecialchars(phpversion()); ?></p>
356 </li>
357 <li class="exceptionSystemInformation3">
358 <p class="exceptionFieldTitle">WoltLab Suite Core<span class="exceptionColon">:</span></p>
359 <p class="exceptionFieldValue">n/a</p>
360 </li>
361 <li class="exceptionSystemInformation5">
362 <p class="exceptionFieldTitle">Peak Memory Usage<span class="exceptionColon">:</span></p>
363 <p class="exceptionFieldValue"><?php echo round(memory_get_peak_usage() / 1024 / 1024, 3); ?> MiB</p>
364 </li>
365 <li class="exceptionSystemInformation2">
366 <p class="exceptionFieldTitle">Request URI<span class="exceptionColon">:</span></p>
367 <p class="exceptionFieldValue"><?php if (isset($_SERVER['REQUEST_METHOD'])) echo htmlspecialchars($_SERVER['REQUEST_METHOD']); ?> <?php if (isset($_SERVER['REQUEST_URI'])) echo htmlspecialchars($_SERVER['REQUEST_URI']); ?></p>
368 </li>
369 <li class="exceptionSystemInformation4">
370 <p class="exceptionFieldTitle">Referrer<span class="exceptionColon">:</span></p>
371 <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_REFERER'])) echo htmlspecialchars($_SERVER['HTTP_REFERER']); ?></p>
372 </li>
373 <li class="exceptionSystemInformation6">
374 <p class="exceptionFieldTitle">User Agent<span class="exceptionColon">:</span></p>
375 <p class="exceptionFieldValue"><?php if (isset($_SERVER['HTTP_USER_AGENT'])) echo htmlspecialchars($_SERVER['HTTP_USER_AGENT']); ?></p>
376 </li>
377 </ul>
830e152e 378 </div>
5176cbcb
MW
379
380 <?php
381 $first = true;
382 $exceptions = [];
383 $current = $e;
384 do {
385 $exceptions[] = $current;
386 } while ($current = $current->getPrevious());
387
388 $e = array_pop($exceptions);
389 do {
390 ?>
830e152e 391 <div class="exceptionBoundary">
5176cbcb
MW
392 <p class="exceptionSubtitle"><?php if (!empty($exceptions) && $first) {
393 echo "Original ";
394 } else if (empty($exceptions) && !$first) {
395 echo "Final ";
396 } ?>Error</p>
830e152e
TD
397 <?php if ($e instanceof SystemException && $e->getDescription()) { ?>
398 <p class="exceptionText"><?php echo $e->getDescription(); ?></p>
399 <?php } ?>
400 <ul class="exceptionErrorDetails">
401 <li>
402 <p class="exceptionFieldTitle">Error Type<span class="exceptionColon">:</span></p>
403 <p class="exceptionFieldValue"><?php echo htmlspecialchars(get_class($e)); ?></p>
404 </li>
405 <li>
406 <p class="exceptionFieldTitle">Error Message<span class="exceptionColon">:</span></p>
407 <p class="exceptionFieldValue"><?php echo htmlspecialchars($e->getMessage()); ?></p>
408 </li>
409 <?php if ($e->getCode()) { ?>
410 <li>
411 <p class="exceptionFieldTitle">Error Code<span class="exceptionColon">:</span></p>
412 <p class="exceptionFieldValue"><?php echo htmlspecialchars($e->getCode()); ?></p>
413 </li>
414 <?php } ?>
415 <li>
416 <p class="exceptionFieldTitle">File<span class="exceptionColon">:</span></p>
417 <p class="exceptionFieldValue" style="word-break: break-all"><?php echo htmlspecialchars(($e->getFile())); ?> (<?php echo $e->getLine(); ?>)</p>
418 </li>
5176cbcb 419
830e152e
TD
420 <?php
421 if ($e instanceof SystemException) {
422 ob_start();
423 $e->show();
424 ob_end_clean();
425
426 $reflection = new \ReflectionClass($e);
427 $property = $reflection->getProperty('information');
830e152e
TD
428 if ($property->getValue($e)) {
429 throw new \Exception("Using the 'information' property of SystemException is not supported any more.");
430 }
431 }
432 if ($e instanceof IExtraInformationException) {
433 foreach ($e->getExtraInformation() as list($key, $value)) {
5176cbcb 434 ?>
830e152e
TD
435 <li>
436 <p class="exceptionFieldTitle"><?php echo htmlspecialchars($key); ?><span class="exceptionColon">:</span></p>
437 <p class="exceptionFieldValue"><?php echo htmlspecialchars($value); ?></p>
438 </li>
5176cbcb 439 <?php
830e152e
TD
440 }
441 }
442
443 $templateContextLines = [];
444 if (!empty($templateContextLines)) {
445 ?>
446 <li>
447 <p class="exceptionFieldTitle">Template Context<span class="exceptionColon">:</span></p>
5176cbcb 448 <pre class="exceptionFieldValue"><?php echo htmlspecialchars(implode("", $templateContextLines)); ?></pre>
830e152e 449 </li>
5176cbcb 450 <?php
830e152e
TD
451 }
452 ?>
453 <li>
454 <p class="exceptionFieldTitle">Stack Trace<span class="exceptionColon">:</span></p>
455 <ul class="exceptionStacktrace">
456 <?php
457 $trace = sanitizeStacktrace($e, true);
458 for ($i = 0, $max = count($trace); $i < $max; $i++) {
5176cbcb
MW
459 ?>
460 <li class="exceptionStacktraceFile"><?php echo '#' . $i . ' ' . htmlspecialchars($trace[$i]['file']) . ' (' . $trace[$i]['line'] . ')' . ':'; ?></li>
830e152e
TD
461 <li class="exceptionStacktraceCall">
462 <?php
5176cbcb
MW
463 echo $trace[$i]['class'] . $trace[$i]['type'] . $trace[$i]['function'] . '(';
464 echo implode(', ', array_map(function ($item) {
465 switch (gettype($item)) {
466 case 'integer':
467 case 'double':
468 return $item;
469 case 'NULL':
470 return 'null';
471 case 'string':
472 return "'" . addcslashes(htmlspecialchars($item), "\\'") . "'";
473 case 'boolean':
474 return $item ? 'true' : 'false';
475 case 'array':
476 $keys = array_keys($item);
477 if (count($keys) > 5) return "[ " . count($keys) . " items ]";
478 return '[ ' . implode(', ', array_map(function ($item) {
479 return $item . ' => ';
480 }, $keys)) . ']';
481 case 'object':
482 return get_class($item);
483 case 'resource':
484 return 'resource(' . get_resource_type($item) . ')';
485 case 'resource (closed)':
486 return 'resource (closed)';
487 }
488
489 throw new \LogicException('Unreachable');
490 }, $trace[$i]['args']));
830e152e
TD
491 echo ')</li>';
492 }
5176cbcb 493 ?>
830e152e
TD
494 </ul>
495 </li>
496 </ul>
497 </div>
5176cbcb 498 <?php
830e152e 499 $first = false;
5176cbcb
MW
500 } while ($e = array_pop($exceptions));
501 ?>
502 <?php } ?>
503 </div>
504 </body>
505
830e152e 506 </html>
5176cbcb 507<?php
830e152e
TD
508}
509
01bf429b 510set_exception_handler(static function ($e) {
9a132951 511 try {
830e152e 512 if ($e instanceof \wcf\system\exception\IPrintableException) {
9a132951
M
513 $e->show();
514 exit;
515 }
5176cbcb 516
3353ca3e 517 // repacking
830e152e 518 printException($e);
3353ca3e 519 exit;
5176cbcb
MW
520 } catch (\Throwable $exception) {
521 die("<pre>WCF::handleException() Unhandled exception: " . $exception->getMessage() . "\n\n" . $exception->getTraceAsString());
9a132951 522 }
01bf429b
TD
523});
524set_error_handler(static function ($severity, $message, $file, $line) {
830e152e
TD
525 // this is necessary for the shut-up operator
526 if (!(\error_reporting() & $severity)) {
527 return;
158bd3ca 528 }
830e152e
TD
529
530 throw new ErrorException($message, 0, $severity, $file, $line);
01bf429b
TD
531}, E_ALL);
532
533/** @noinspection PhpMultipleClassesDeclarationsInOneFile */
534/**
535 * A SystemException is thrown when an unexpected error occurs.
536 *
537 * @package com.woltlab.wcf
538 * @author Marcel Werk
539 */
5176cbcb
MW
540class SystemException extends \Exception
541{
01bf429b
TD
542 protected $description;
543 protected $information = '';
544 protected $functions = '';
5176cbcb 545
01bf429b
TD
546 /**
547 * Creates a new SystemException.
548 *
549 * @param string $message error message
550 * @param int $code error code
551 * @param string $description description of the error
552 * @param \Exception $previous repacked Exception
553 */
5176cbcb
MW
554 public function __construct($message = '', $code = 0, $description = '', \Exception $previous = null)
555 {
01bf429b
TD
556 parent::__construct((string) $message, (int) $code, $previous);
557 $this->description = $description;
558 }
5176cbcb 559
01bf429b
TD
560 /**
561 * Returns the description of this exception.
562 *
563 * @return string
564 */
5176cbcb
MW
565 public function getDescription()
566 {
01bf429b
TD
567 return $this->description;
568 }
5176cbcb 569
01bf429b
TD
570 /**
571 * Prints this exception.
572 * This method is called by WCF::handleException().
573 */
5176cbcb
MW
574 public function show()
575 {
01bf429b
TD
576 }
577}
578
579/**
580 * Loads the required classes automatically.
581 */
5176cbcb 582spl_autoload_register(function ($className) {
01bf429b
TD
583 $namespaces = explode('\\', $className);
584 if (count($namespaces) > 1) {
585 // remove 'wcf' component
586 array_shift($namespaces);
5176cbcb 587
01bf429b
TD
588 $className = implode('/', $namespaces);
589 $classPath = TMP_DIR . 'install/files/lib/' . $className . '.class.php';
590 if (file_exists($classPath)) {
591 require_once($classPath);
592 }
593 }
594});
595
596/**
597 * Helper method to output debug data for all passed variables,
598 * uses `print_r()` for arrays and objects, `var_dump()` otherwise.
599 */
5176cbcb
MW
600function wcfDebug()
601{
01bf429b 602 echo "<pre>";
5176cbcb 603
01bf429b
TD
604 $args = func_get_args();
605 $length = count($args);
606 if ($length === 0) {
607 echo "ERROR: No arguments provided.<hr>";
5176cbcb 608 } else {
01bf429b
TD
609 for ($i = 0; $i < $length; $i++) {
610 $arg = $args[$i];
5176cbcb 611
01bf429b 612 echo "<h2>Argument {$i} (" . gettype($arg) . ")</h2>";
5176cbcb 613
01bf429b
TD
614 if (is_array($arg) || is_object($arg)) {
615 print_r($arg);
5176cbcb 616 } else {
01bf429b
TD
617 var_dump($arg);
618 }
5176cbcb 619
01bf429b
TD
620 echo "<hr>";
621 }
622 }
5176cbcb 623
01bf429b 624 $backtrace = debug_backtrace();
5176cbcb 625
01bf429b
TD
626 // output call location to help finding these debug outputs again
627 echo "wcfDebug() called in {$backtrace[0]['file']} on line {$backtrace[0]['line']}";
5176cbcb 628
01bf429b 629 echo "</pre>";
5176cbcb 630
01bf429b 631 exit;
158bd3ca
TD
632}
633
3e823cfa 634/** @noinspection PhpMultipleClassesDeclarationsInOneFile */
158bd3ca
TD
635/**
636 * BasicFileUtil contains file-related functions.
637 *
f4f05aa5 638 * @package com.woltlab.wcf
158bd3ca
TD
639 * @author Marcel Werk
640 */
5176cbcb
MW
641class BasicFileUtil
642{
d8fa09e0
AE
643 /**
644 * chmod mode
090b71e5 645 * @var int
d8fa09e0
AE
646 */
647 protected static $mode = null;
5176cbcb 648
1232bce2
AE
649 /**
650 * Tries to make a file or directory writable. It starts of with the least
d8fa09e0 651 * permissions and goes up until 0666 for files and 0777 for directories.
1232bce2
AE
652 *
653 * @param string $filename
830e152e 654 * @throws \Exception
1232bce2 655 */
5176cbcb
MW
656 public static function makeWritable($filename)
657 {
043b049d 658 if (!file_exists($filename)) {
1232bce2 659 return;
158bd3ca 660 }
5176cbcb 661
d8fa09e0
AE
662 // determine mode
663 if (self::$mode === null) {
664 // do not use PHP_OS here, as this represents the system it was built on != running on
5176cbcb 665 // php_uname() is forbidden on some strange hosts; PHP_EOL is reliable
0436b618
AE
666 if (PHP_EOL == "\r\n") {
667 // Windows
d8fa09e0 668 self::$mode = 0777;
5176cbcb 669 } else {
0436b618 670 // anything but Windows
adbd8054 671 clearstatcache();
5176cbcb 672
d8fa09e0 673 self::$mode = 0666;
5176cbcb
MW
674
675 $tmpFilename = '__permissions_' . sha1(time()) . '.txt';
0c1810be 676 @touch($tmpFilename);
5176cbcb 677
d8fa09e0
AE
678 // create a new file and check the file owner, if it is the same
679 // as this file (uploaded through FTP), we can safely grant write
680 // permissions exclusively to the owner rather than everyone
0c1810be 681 if (file_exists($tmpFilename)) {
d8fa09e0 682 $scriptOwner = fileowner(__FILE__);
0c1810be 683 $fileOwner = fileowner($tmpFilename);
5176cbcb 684
d8fa09e0
AE
685 if ($scriptOwner === $fileOwner) {
686 self::$mode = 0644;
687 }
5176cbcb 688
0c1810be 689 @unlink($tmpFilename);
d8fa09e0
AE
690 }
691 }
692 }
5176cbcb 693
1232bce2 694 if (is_dir($filename)) {
d8fa09e0 695 if (self::$mode == 0644) {
7fe5312d 696 @chmod($filename, 0755);
5176cbcb 697 } else {
7fe5312d 698 @chmod($filename, 0777);
1232bce2 699 }
5176cbcb 700 } else {
7fe5312d 701 @chmod($filename, self::$mode);
d8fa09e0 702 }
5176cbcb 703
d8fa09e0 704 if (!is_writable($filename)) {
5176cbcb 705 throw new \Exception("Unable to make '" . $filename . "' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider.");
d8fa09e0 706 }
158bd3ca 707 }
5176cbcb 708
e5521c73
TD
709 /**
710 * Adds a trailing slash to the given path.
5176cbcb 711 *
e5521c73 712 * @param string $path
e5521c73 713 */
5176cbcb
MW
714 public static function addTrailingSlash($path): string
715 {
716 return rtrim($path, '/') . '/';
e5521c73 717 }
5176cbcb 718
e5521c73
TD
719 /**
720 * Creates a path on the local filesystem and returns true on success.
721 * Parent directories do not need to exists as they will be created if
722 * necessary.
5176cbcb 723 *
e5521c73 724 * @param string $path
e5521c73 725 */
5176cbcb
MW
726 public static function makePath($path): bool
727 {
e5521c73
TD
728 // directory already exists, abort
729 if (file_exists($path)) {
730 return false;
731 }
5176cbcb 732
e5521c73
TD
733 // check if parent directory exists
734 $parent = dirname($path);
735 if ($parent != $path) {
736 // parent directory does not exist either
737 // we have to create the parent directory first
738 $parent = self::addTrailingSlash($parent);
739 if (!@file_exists($parent)) {
740 // could not create parent directory either => abort
741 if (!self::makePath($parent)) {
742 return false;
743 }
744 }
5176cbcb 745
e5521c73
TD
746 // well, the parent directory exists or has been created
747 // lets create this path
748 if (!@mkdir($path)) {
749 return false;
750 }
5176cbcb 751
e5521c73 752 self::makeWritable($path);
5176cbcb 753
e5521c73
TD
754 return true;
755 }
5176cbcb 756
e5521c73
TD
757 return false;
758 }
158bd3ca
TD
759}
760
3e823cfa 761/** @noinspection PhpMultipleClassesDeclarationsInOneFile */
158bd3ca
TD
762/**
763 * Opens tar or tar.gz archives.
764 *
765 * Usage:
766 * ------
767 * $tar = new Tar('archive.tar');
768 * $contentList = $tar->getContentList();
769 * foreach ($contentList as $key => $val) {
770 * $tar->extract($key, DESTINATION);
771 * }
772 */
5176cbcb
MW
773class Tar
774{
e5521c73
TD
775 /**
776 * name of the archive
777 * @var string
778 */
158bd3ca 779 protected $archiveName = '';
5176cbcb 780
e5521c73
TD
781 /**
782 * content of the tar file
783 * @var array
784 */
058cbd6a 785 protected $contentList = [];
5176cbcb 786
e5521c73
TD
787 /**
788 * indicates if tar file is opened
1524f8c0 789 * @var bool
e5521c73 790 */
158bd3ca 791 protected $opened = false;
5176cbcb 792
e5521c73
TD
793 /**
794 * indicates if file content has been read
1524f8c0 795 * @var bool
e5521c73 796 */
158bd3ca 797 protected $read = false;
5176cbcb 798
e5521c73
TD
799 /**
800 * file object
801 * @var File
802 */
158bd3ca 803 protected $file = null;
5176cbcb 804
e5521c73
TD
805 /**
806 * indicates if the tar file is (g)zipped
1524f8c0 807 * @var bool
e5521c73 808 */
158bd3ca 809 protected $isZipped = false;
5176cbcb 810
e5521c73
TD
811 /**
812 * file access mode
813 * @var string
814 */
158bd3ca 815 protected $mode = 'rb';
5176cbcb 816
e5521c73
TD
817 /**
818 * chunk size for extracting
090b71e5 819 * @var int
e5521c73
TD
820 */
821 const CHUNK_SIZE = 8192;
300833a0
TD
822
823 private static array $asciiMap;
5176cbcb 824
158bd3ca
TD
825 /**
826 * Creates a new Tar object.
827 * archiveName must be tarball or gzipped tarball
5176cbcb 828 *
39bea7dd 829 * @param string $archiveName
2b770bdd 830 * @throws SystemException
158bd3ca 831 */
5176cbcb
MW
832 public function __construct($archiveName)
833 {
158bd3ca 834 if (!is_file($archiveName)) {
5176cbcb 835 throw new SystemException("unable to find tar archive '" . $archiveName . "'");
158bd3ca 836 }
300833a0
TD
837
838 if (!isset(self::$asciiMap)) {
839 self::$asciiMap = [];
840 for ($i = 0; $i <= 0xFF; $i++) {
841 self::$asciiMap[\chr($i)] = $i;
842 }
843 }
5176cbcb 844
158bd3ca
TD
845 $this->archiveName = $archiveName;
846 $this->open();
847 $this->readContent();
848 }
5176cbcb 849
158bd3ca
TD
850 /**
851 * Destructor of this class, closes tar archive.
852 */
5176cbcb
MW
853 public function __destruct()
854 {
158bd3ca
TD
855 $this->close();
856 }
5176cbcb 857
158bd3ca
TD
858 /**
859 * Opens the tar archive and stores filehandle.
860 */
5176cbcb
MW
861 public function open()
862 {
158bd3ca 863 if (!$this->opened) {
e5521c73 864 if ($this->isZipped) $this->file = new GZipFile($this->archiveName, $this->mode);
158bd3ca
TD
865 else {
866 // test compression
867 $this->file = new File($this->archiveName, $this->mode);
868 if ($this->file->read(2) == "\37\213") {
869 $this->file->close();
870 $this->isZipped = true;
e5521c73 871 $this->file = new GZipFile($this->archiveName, $this->mode);
5176cbcb 872 } else {
158bd3ca
TD
873 $this->file->seek(0);
874 }
875 }
876 $this->opened = true;
877 }
878 }
5176cbcb 879
158bd3ca
TD
880 /**
881 * Closes the opened file.
882 */
5176cbcb
MW
883 public function close()
884 {
158bd3ca
TD
885 if ($this->opened) {
886 $this->file->close();
887 $this->opened = false;
888 }
889 }
5176cbcb 890
158bd3ca 891 /**
e5521c73 892 * @inheritDoc
158bd3ca 893 */
5176cbcb
MW
894 public function getContentList()
895 {
158bd3ca
TD
896 if (!$this->read) {
897 $this->open();
898 $this->readContent();
899 }
900 return $this->contentList;
901 }
5176cbcb 902
158bd3ca 903 /**
e5521c73 904 * @inheritDoc
158bd3ca 905 */
5176cbcb
MW
906 public function getFileInfo($fileIndex)
907 {
158bd3ca
TD
908 if (!is_int($fileIndex)) {
909 $fileIndex = $this->getIndexByFilename($fileIndex);
910 }
5176cbcb 911
158bd3ca 912 if (!isset($this->contentList[$fileIndex])) {
5176cbcb 913 throw new SystemException("Tar: could find file '" . $fileIndex . "' in archive");
158bd3ca
TD
914 }
915 return $this->contentList[$fileIndex];
916 }
5176cbcb 917
158bd3ca 918 /**
e5521c73 919 * @inheritDoc
158bd3ca 920 */
5176cbcb
MW
921 public function getIndexByFilename($filename)
922 {
158bd3ca
TD
923 foreach ($this->contentList as $index => $file) {
924 if ($file['filename'] == $filename) {
925 return $index;
926 }
927 }
928 return false;
929 }
5176cbcb 930
158bd3ca 931 /**
e5521c73 932 * @inheritDoc
158bd3ca 933 */
5176cbcb
MW
934 public function extractToString($index)
935 {
158bd3ca
TD
936 if (!$this->read) {
937 $this->open();
938 $this->readContent();
939 }
940 $header = $this->getFileInfo($index);
5176cbcb 941
158bd3ca
TD
942 // can not extract a folder
943 if ($header['type'] != 'file') {
944 return false;
945 }
5176cbcb 946
158bd3ca
TD
947 // seek to offset
948 $this->file->seek($header['offset']);
5176cbcb 949
158bd3ca 950 // read data
12ed33df 951 $content = $this->file->read($header['size']);
5176cbcb 952
12ed33df 953 if (strlen($content) != $header['size']) {
5176cbcb 954 throw new SystemException("Could not untar file '" . $header['filename'] . "' to string. Maybe the archive is truncated?");
158bd3ca 955 }
5176cbcb 956
158bd3ca
TD
957 return $content;
958 }
5176cbcb 959
158bd3ca 960 /**
e5521c73 961 * @inheritDoc
158bd3ca 962 */
5176cbcb
MW
963 public function extract($index, $destination)
964 {
158bd3ca
TD
965 if (!$this->read) {
966 $this->open();
967 $this->readContent();
968 }
969 $header = $this->getFileInfo($index);
5176cbcb 970
e5521c73
TD
971 BasicFileUtil::makePath(dirname($destination));
972 if ($header['type'] === 'folder') {
973 BasicFileUtil::makePath($destination);
974 return;
975 }
976 if ($header['type'] === 'symlink') {
977 // skip symlinks
978 return;
158bd3ca 979 }
5176cbcb 980
158bd3ca
TD
981 // seek to offset
982 $this->file->seek($header['offset']);
5176cbcb 983
158bd3ca 984 $targetFile = new File($destination);
5176cbcb 985
e5521c73
TD
986 // read and write data
987 if ($header['size']) {
988 $buffer = $this->file->read($header['size']);
989 $targetFile->write($buffer);
158bd3ca 990 }
158bd3ca 991 $targetFile->close();
5176cbcb 992
1232bce2 993 BasicFileUtil::makeWritable($destination);
5176cbcb 994
158bd3ca
TD
995 if ($header['mtime']) {
996 @$targetFile->touch($header['mtime']);
997 }
5176cbcb 998
158bd3ca
TD
999 // check filesize
1000 if (filesize($destination) != $header['size']) {
5176cbcb 1001 throw new SystemException("Could not untar file '" . $header['filename'] . "' to '" . $destination . "'. Maybe disk quota exceeded in folder '" . dirname($destination) . "'.");
158bd3ca 1002 }
5176cbcb 1003
158bd3ca
TD
1004 return true;
1005 }
5176cbcb 1006
158bd3ca
TD
1007 /**
1008 * Reads table of contents (TOC) from tar archive.
1009 * This does not get the entire to memory but only parts of it.
1010 */
5176cbcb
MW
1011 protected function readContent()
1012 {
058cbd6a 1013 $this->contentList = [];
158bd3ca
TD
1014 $this->read = true;
1015 $i = 0;
5176cbcb 1016
158bd3ca 1017 // Read the 512 bytes header
db8aa273 1018 $longFilename = null;
158bd3ca
TD
1019 while (strlen($binaryData = $this->file->read(512)) != 0) {
1020 // read header
1021 $header = $this->readHeader($binaryData);
1022 if ($header === false) {
1023 continue;
1024 }
5176cbcb 1025
db8aa273
AE
1026 // fixes a bug that files with long names aren't correctly
1027 // extracted
1028 if ($longFilename !== null) {
1029 $header['filename'] = $longFilename;
1030 $longFilename = null;
1031 }
1032 if ($header['typeflag'] == 'L') {
5176cbcb
MW
1033 $format = 'Z' . $header['size'] . 'filename';
1034
db8aa273
AE
1035 $fileData = unpack($format, $this->file->read(512));
1036 $longFilename = $fileData['filename'];
1037 $header['size'] = 0;
1038 }
1039 // don't include the @LongLink file in the content list
1040 else {
1041 $this->contentList[$i] = $header;
1042 $this->contentList[$i]['index'] = $i;
1043 $i++;
1044 }
5176cbcb 1045
63b9817b 1046 $this->file->seek($this->file->tell() + (512 * ceil($header['size'] / 512)));
158bd3ca
TD
1047 }
1048 }
5176cbcb 1049
158bd3ca
TD
1050 /**
1051 * Unpacks file header for one file entry.
5176cbcb 1052 *
39bea7dd 1053 * @param string $binaryData
1524f8c0 1054 * @return array|bool
158bd3ca 1055 */
5176cbcb
MW
1056 protected function readHeader($binaryData)
1057 {
158bd3ca
TD
1058 if (strlen($binaryData) != 512) {
1059 return false;
1060 }
5176cbcb 1061
058cbd6a 1062 $header = [];
158bd3ca
TD
1063 $checksum = 0;
1064 // First part of the header
1065 for ($i = 0; $i < 148; $i++) {
300833a0 1066 $checksum += self::$asciiMap[$binaryData[$i]];
158bd3ca
TD
1067 }
1068 // Calculate the checksum
1069 // Ignore the checksum value and replace it by ' ' (space)
1070 for ($i = 148; $i < 156; $i++) {
300833a0 1071 $checksum += self::$asciiMap[' '];
158bd3ca
TD
1072 }
1073 // Last part of the header
1074 for ($i = 156; $i < 512; $i++) {
300833a0 1075 $checksum += self::$asciiMap[$binaryData[$i]];
158bd3ca 1076 }
5176cbcb 1077
db8aa273
AE
1078 // extract values
1079 $format = 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor/Z155prefix';
5176cbcb 1080
32b198a0 1081 $data = unpack($format, $binaryData);
5176cbcb 1082
158bd3ca 1083 // Extract the properties
379875ee 1084 $header['checksum'] = octdec(trim($data['checksum']));
158bd3ca
TD
1085 if ($header['checksum'] == $checksum) {
1086 $header['filename'] = trim($data['filename']);
379875ee
MS
1087 $header['mode'] = octdec(trim($data['mode']));
1088 $header['uid'] = octdec(trim($data['uid']));
1089 $header['gid'] = octdec(trim($data['gid']));
1090 $header['size'] = octdec(trim($data['size']));
1091 $header['mtime'] = octdec(trim($data['mtime']));
158bd3ca
TD
1092 $header['prefix'] = trim($data['prefix']);
1093 if ($header['prefix']) {
5176cbcb 1094 $header['filename'] = $header['prefix'] . '/' . $header['filename'];
158bd3ca 1095 }
e5521c73
TD
1096 $header['typeflag'] = $data['typeflag'];
1097 if ($header['typeflag'] == '5') {
158bd3ca
TD
1098 $header['size'] = 0;
1099 $header['type'] = 'folder';
5176cbcb 1100 } else if ($header['typeflag'] == '2') {
e5521c73
TD
1101 $header['type'] = 'symlink';
1102 $header['target'] = $data['link'];
5176cbcb 1103 } else {
158bd3ca
TD
1104 $header['type'] = 'file';
1105 }
1106 $header['offset'] = $this->file->tell();
5176cbcb 1107
158bd3ca 1108 return $header;
5176cbcb 1109 } else {
158bd3ca
TD
1110 return false;
1111 }
1112 }
5176cbcb 1113
e5521c73
TD
1114 /**
1115 * Returns true if this tar is (g)zipped.
5176cbcb 1116 *
1524f8c0 1117 * @return bool
e5521c73 1118 */
5176cbcb
MW
1119 public function isZipped()
1120 {
e5521c73
TD
1121 return $this->isZipped;
1122 }
158bd3ca
TD
1123}
1124
3e823cfa 1125/** @noinspection PhpMultipleClassesDeclarationsInOneFile */
158bd3ca
TD
1126/**
1127 * The File class handles all file operations.
1128 *
1129 * Example:
1130 * using php functions:
1131 * $fp = fopen('filename', 'wb');
1132 * fwrite($fp, '...');
1133 * fclose($fp);
1134 *
1135 * using this class:
1136 * $file = new File('filename');
1137 * $file->write('...');
1138 * $file->close();
1139 *
1140 * @author Marcel Werk
1141 */
5176cbcb
MW
1142class File
1143{
158bd3ca
TD
1144 protected $resource = null;
1145 protected $filename;
5176cbcb 1146
158bd3ca
TD
1147 /**
1148 * Opens a new file.
1149 *
39bea7dd
MS
1150 * @param string $filename
1151 * @param string $mode
2b770bdd 1152 * @throws SystemException
158bd3ca 1153 */
5176cbcb
MW
1154 public function __construct($filename, $mode = 'wb')
1155 {
158bd3ca
TD
1156 $this->filename = $filename;
1157 $this->resource = fopen($filename, $mode);
1158 if ($this->resource === false) {
4fe0b42b 1159 throw new SystemException('Can not open file ' . $filename);
158bd3ca
TD
1160 }
1161 }
5176cbcb 1162
158bd3ca
TD
1163 /**
1164 * Calls the specified function on the open file.
1165 * Do not call this function directly. Use $file->write('') instead.
1166 *
39bea7dd
MS
1167 * @param string $function
1168 * @param array $arguments
71952a87 1169 * @return mixed
2b770bdd 1170 * @throws SystemException
158bd3ca 1171 */
5176cbcb
MW
1172 public function __call($function, $arguments)
1173 {
158bd3ca
TD
1174 if (function_exists('f' . $function)) {
1175 array_unshift($arguments, $this->resource);
39bea7dd 1176 return call_user_func_array('f' . $function, $arguments);
5176cbcb 1177 } else if (function_exists($function)) {
158bd3ca 1178 array_unshift($arguments, $this->filename);
39bea7dd 1179 return call_user_func_array($function, $arguments);
5176cbcb 1180 } else {
4fe0b42b 1181 throw new SystemException('Can not call file method ' . $function);
158bd3ca
TD
1182 }
1183 }
1184}
1185
3e823cfa 1186/** @noinspection PhpMultipleClassesDeclarationsInOneFile */
158bd3ca
TD
1187/**
1188 * The File class handles all file operations on a zipped file.
1189 *
1190 * @author Marcel Werk
1191 */
5176cbcb
MW
1192final class GZipFile extends File
1193{
e4bda351 1194 /** @noinspection PhpMissingParentConstructorInspection */
158bd3ca 1195 /**
e5521c73 1196 * Opens a gzip file.
5176cbcb 1197 *
39bea7dd
MS
1198 * @param string $filename
1199 * @param string $mode
2b770bdd 1200 * @throws SystemException
158bd3ca 1201 */
5176cbcb
MW
1202 public function __construct($filename, $mode = 'wb')
1203 {
158bd3ca 1204 $this->filename = $filename;
1a78cf8d 1205 $this->resource = gzopen($filename, $mode);
158bd3ca 1206 if ($this->resource === false) {
4fe0b42b 1207 throw new SystemException('Can not open file ' . $filename);
158bd3ca
TD
1208 }
1209 }
5176cbcb 1210
158bd3ca
TD
1211 /**
1212 * Calls the specified function on the open file.
5176cbcb 1213 *
39bea7dd
MS
1214 * @param string $function
1215 * @param array $arguments
71952a87 1216 * @return mixed
2b770bdd 1217 * @throws SystemException
158bd3ca 1218 */
5176cbcb
MW
1219 public function __call($function, $arguments)
1220 {
1a78cf8d 1221 if (function_exists('gz' . $function)) {
158bd3ca 1222 array_unshift($arguments, $this->resource);
39bea7dd 1223 return call_user_func_array('gz' . $function, $arguments);
5176cbcb 1224 } else if (function_exists($function)) {
158bd3ca 1225 array_unshift($arguments, $this->filename);
39bea7dd 1226 return call_user_func_array($function, $arguments);
5176cbcb 1227 } else {
4fe0b42b 1228 throw new SystemException('Can not call method ' . $function);
158bd3ca
TD
1229 }
1230 }
d78dc6e5
TD
1231
1232 /**
1233 * @see \gzread()
1234 */
1235 public function read(int $length): string|false
1236 {
1237 return \gzread($this->resource, $length);
1238 }
1239
1240 /**
1241 * @see \gztell()
1242 */
1243 public function tell(): int|false
1244 {
1245 return \gztell($this->resource);
1246 }
1247
1248 /**
1249 * @see \gzseek()
1250 */
1251 public function seek(int $offset, int $whence = \SEEK_SET): int
1252 {
1253 return \gzseek($this->resource, $offset, $whence);
1254 }
5176cbcb 1255
158bd3ca 1256 /**
e5521c73 1257 * Returns the filesize of the unzipped file.
5176cbcb 1258 *
090b71e5 1259 * @return int
158bd3ca 1260 */
5176cbcb
MW
1261 public function getFileSize()
1262 {
1263 $byteBlock = 1 << 14;
158bd3ca 1264 $eof = $byteBlock;
5176cbcb 1265
158bd3ca
TD
1266 // the correction is for zip files that are too small
1267 // to get in the first while loop
1268 $correction = 1;
1269 while ($this->seek($eof) == 0) {
1270 $eof += $byteBlock;
1271 $correction = 0;
1272 }
5176cbcb 1273
158bd3ca
TD
1274 while ($byteBlock > 1) {
1275 $byteBlock >>= 1;
1276 $eof += $byteBlock * ($this->seek($eof) ? -1 : 1);
1277 }
5176cbcb 1278
e5521c73 1279 if ($this->seek($eof) == -1) $eof--;
5176cbcb 1280
158bd3ca
TD
1281 $this->rewind();
1282 return $eof - $correction;
1283 }
1284}
1285
598d1783
TD
1286// Bootstrap Setup.
1287
e7ffd8b6
TD
1288$prefix = null;
1289if (isset($_POST['tmpFilePrefix'])) {
1290 $inputPrefix = \preg_replace('/[^a-f0-9_]+/', '', $_POST['tmpFilePrefix']);
1291
1292 if (\is_dir(INSTALL_SCRIPT_DIR . "/WCFSetup-{$inputPrefix}/")) {
1293 // We accept the input prefix if a corresponding directory exists.
1294 $prefix = $inputPrefix;
1295 }
1296}
1297
1298// If no trusted prefix was provided, we generate a random prefix and corresponding directory.
1299if ($prefix === null) {
1300 $prefix = \bin2hex(\random_bytes(8));
1301 $dir = INSTALL_SCRIPT_DIR . "/WCFSetup-{$prefix}/";
1302 \mkdir($dir);
1303 BasicFileUtil::makeWritable($dir);
e96e2457 1304 \file_put_contents($dir . 'lastStep', '0');
e7ffd8b6 1305}
158bd3ca 1306
e7ffd8b6
TD
1307\define('TMP_FILE_PREFIX', $prefix);
1308\define('TMP_DIR', INSTALL_SCRIPT_DIR . "/WCFSetup-{$prefix}/");
158bd3ca 1309
53e00c6b 1310// check whether setup files are already unzipped
158bd3ca
TD
1311if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) {
1312 // try to unzip all setup files into temp folder
1313 $tar = new Tar(SETUP_FILE);
1314 $contentList = $tar->getContentList();
15fa2802 1315 if (empty($contentList)) {
830e152e 1316 throw new \Exception("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken.");
158bd3ca 1317 }
5176cbcb 1318
158bd3ca
TD
1319 foreach ($contentList as $file) {
1320 foreach ($neededFilesPattern as $pattern) {
1321 if (preg_match($pattern, $file['filename'])) {
1322 // create directory if not exists
1323 $dir = TMP_DIR . dirname($file['filename']);
1324 if (!@is_dir($dir)) {
1325 @mkdir($dir, 0777, true);
1232bce2 1326 BasicFileUtil::makeWritable($dir);
158bd3ca 1327 }
5176cbcb 1328
158bd3ca
TD
1329 $tar->extract($file['index'], TMP_DIR . $file['filename']);
1330 }
1331 }
1332 }
1333 $tar->close();
5176cbcb 1334
158bd3ca 1335 @mkdir(TMP_DIR . 'setup/template/compiled/', 0777);
1232bce2 1336 BasicFileUtil::makeWritable(TMP_DIR . 'setup/template/compiled/');
158bd3ca
TD
1337}
1338
830e152e
TD
1339if (!class_exists(\wcf\system\WCFSetup::class)) {
1340 throw new \Exception(\sprintf(
1341 "Cannot find class '%s'",
1342 \wcf\system\WCFSetup::class
1343 ));
158bd3ca
TD
1344}
1345
1346// start setup
dcb3a44c 1347new \wcf\system\WCFSetup();