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