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