Commit | Line | Data |
---|---|---|
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 | 11 | define('INSTALL_SCRIPT', __FILE__); |
158bd3ca TD |
12 | define('INSTALL_SCRIPT_DIR', dirname(__FILE__).'/'); |
13 | define('SETUP_FILE', INSTALL_SCRIPT_DIR . 'WCFSetup.tar.gz'); | |
14 | define('NO_IMPORTS', 1); | |
15 | ||
16 | // set exception handler | |
17 | set_exception_handler('handleException'); | |
18 | // set php error handler | |
19 | set_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 | */ | |
42 | interface 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 | */ | |
56 | class 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 | 163 | spl_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 | */ |
183 | function 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 |
192 | function 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 | */ |
218 | function 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 | */ | |
239 | class 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 | */ | |
388 | class 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 | */ | |
711 | class 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 | */ | |
760 | class 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 | |
847 | if (isset($_REQUEST['tmpFilePrefix'])) { | |
848 | $prefix = preg_replace('/[^a-f0-9_]+/', '', $_REQUEST['tmpFilePrefix']); | |
849 | } | |
850 | else { | |
851 | $prefix = substr(sha1(uniqid(microtime())), 0, 8); | |
852 | } | |
853 | define('TMP_FILE_PREFIX', $prefix); | |
854 | ||
855 | // try to find the temp folder | |
99be741e | 856 | define('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 | */ | |
864 | function 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 |
906 | if (isset($_GET['showImage'])) { | |
907 | readFileResource('showImage', TMP_DIR . 'install/files/acp/images/'); | |
908 | } | |
909 | // show icon from temp folder | |
910 | if (isset($_GET['showIcon'])) { | |
911 | readFileResource('showIcon', TMP_DIR . 'install/files/icon/'); | |
912 | } | |
913 | // show css from temp folder | |
914 | if (isset($_GET['showCSS'])) { | |
e94c3830 | 915 | readFileResource('showCSS', TMP_DIR . 'install/files/acp/style/setup/'); |
7da7f7cc | 916 | } |
ec6e78b9 MW |
917 | // show fonts from temp folder |
918 | if (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 |
923 | if (!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 | ||
955 | if (!class_exists('wcf\system\WCFSetup')) { | |
3a4862d3 | 956 | throw new SystemException("Cannot find class 'WCFSetup'"); |
158bd3ca TD |
957 | } |
958 | ||
959 | // start setup | |
dcb3a44c | 960 | new \wcf\system\WCFSetup(); |