Commit | Line | Data |
---|---|---|
d335fa16 AE |
1 | <?php |
2 | namespace wcf\system; | |
3 | use wcf\data\application\Application; | |
4 | use wcf\data\option\OptionEditor; | |
5 | use wcf\data\package\Package; | |
6 | use wcf\data\package\PackageCache; | |
7 | use wcf\data\package\PackageEditor; | |
12a2ff01 AE |
8 | use wcf\data\page\Page; |
9 | use wcf\data\page\PageCache; | |
5346b183 | 10 | use wcf\page\CmsPage; |
d335fa16 | 11 | use wcf\system\application\ApplicationHandler; |
f341086b | 12 | use wcf\system\application\IApplication; |
55b402a0 | 13 | use wcf\system\box\BoxHandler; |
d335fa16 AE |
14 | use wcf\system\cache\builder\CoreObjectCacheBuilder; |
15 | use wcf\system\cache\builder\PackageUpdateCacheBuilder; | |
16 | use wcf\system\cronjob\CronjobScheduler; | |
157054c9 | 17 | use wcf\system\database\MySQLDatabase; |
d335fa16 AE |
18 | use wcf\system\event\EventHandler; |
19 | use wcf\system\exception\AJAXException; | |
b25ad8f3 | 20 | use wcf\system\exception\ErrorException; |
d335fa16 AE |
21 | use wcf\system\exception\IPrintableException; |
22 | use wcf\system\exception\NamedUserException; | |
7b9ff46b | 23 | use wcf\system\exception\ParentClassException; |
d335fa16 AE |
24 | use wcf\system\exception\PermissionDeniedException; |
25 | use wcf\system\exception\SystemException; | |
26 | use wcf\system\language\LanguageFactory; | |
27 | use wcf\system\package\PackageInstallationDispatcher; | |
11117cd5 | 28 | use wcf\system\registry\RegistryHandler; |
12a2ff01 | 29 | use wcf\system\request\Request; |
a80873d5 | 30 | use wcf\system\request\RequestHandler; |
d335fa16 AE |
31 | use wcf\system\session\SessionFactory; |
32 | use wcf\system\session\SessionHandler; | |
33 | use wcf\system\style\StyleHandler; | |
15621401 | 34 | use wcf\system\template\EmailTemplateEngine; |
d335fa16 AE |
35 | use wcf\system\template\TemplateEngine; |
36 | use wcf\system\user\storage\UserStorageHandler; | |
66d9b64b | 37 | use wcf\util\DirectoryUtil; |
d335fa16 | 38 | use wcf\util\FileUtil; |
8418bed7 | 39 | use wcf\util\HeaderUtil; |
d335fa16 | 40 | use wcf\util\StringUtil; |
dc0015ab | 41 | use wcf\util\UserUtil; |
d335fa16 AE |
42 | |
43 | // try to set a time-limit to infinite | |
44 | @set_time_limit(0); | |
45 | ||
46 | // fix timezone warning issue | |
47 | if (!@ini_get('date.timezone')) { | |
48 | @date_default_timezone_set('Europe/London'); | |
49 | } | |
50 | ||
359841c3 | 51 | // define current woltlab suite version |
766f40fe | 52 | define('WCF_VERSION', '5.3.0 RC 1'); |
d335fa16 | 53 | |
89484ba0 | 54 | // define current API version |
0e8af3ef | 55 | // @deprecated 5.2 |
04acbf0b | 56 | define('WSC_API_VERSION', 2019); |
89484ba0 | 57 | |
d335fa16 AE |
58 | // define current unix timestamp |
59 | define('TIME_NOW', time()); | |
60 | ||
61 | // wcf imports | |
62 | if (!defined('NO_IMPORTS')) { | |
63 | require_once(WCF_DIR.'lib/core.functions.php'); | |
d11a8c9e | 64 | require_once(WCF_DIR.'lib/system/api/autoload.php'); |
d335fa16 AE |
65 | } |
66 | ||
67 | /** | |
e71525e4 | 68 | * WCF is the central class for the WoltLab Suite Core. |
d335fa16 AE |
69 | * It holds the database connection, access to template and language engine. |
70 | * | |
71 | * @author Marcel Werk | |
7b7b9764 | 72 | * @copyright 2001-2019 WoltLab GmbH |
d335fa16 | 73 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 | 74 | * @package WoltLabSuite\Core\System |
d335fa16 AE |
75 | */ |
76 | class WCF { | |
89484ba0 AE |
77 | /** |
78 | * list of supported legacy API versions | |
79 | * @var integer[] | |
0e8af3ef | 80 | * @deprecated 5.2 |
89484ba0 | 81 | */ |
04acbf0b | 82 | private static $supportedLegacyApiVersions = [2017, 2018]; |
89484ba0 | 83 | |
d335fa16 AE |
84 | /** |
85 | * list of currently loaded applications | |
893aace3 | 86 | * @var Application[] |
d335fa16 | 87 | */ |
058cbd6a | 88 | protected static $applications = []; |
d335fa16 AE |
89 | |
90 | /** | |
91 | * list of currently loaded application objects | |
7a23a706 | 92 | * @var IApplication[] |
d335fa16 | 93 | */ |
058cbd6a | 94 | protected static $applicationObjects = []; |
d335fa16 AE |
95 | |
96 | /** | |
97 | * list of autoload directories | |
98 | * @var array | |
99 | */ | |
058cbd6a | 100 | protected static $autoloadDirectories = []; |
d335fa16 AE |
101 | |
102 | /** | |
103 | * list of unique instances of each core object | |
7a23a706 | 104 | * @var SingletonFactory[] |
d335fa16 | 105 | */ |
058cbd6a | 106 | protected static $coreObject = []; |
d335fa16 AE |
107 | |
108 | /** | |
109 | * list of cached core objects | |
7a23a706 | 110 | * @var string[] |
d335fa16 | 111 | */ |
058cbd6a | 112 | protected static $coreObjectCache = []; |
d335fa16 AE |
113 | |
114 | /** | |
115 | * database object | |
4e25add7 | 116 | * @var MySQLDatabase |
d335fa16 | 117 | */ |
85099417 | 118 | protected static $dbObj; |
d335fa16 AE |
119 | |
120 | /** | |
121 | * language object | |
a59497a6 | 122 | * @var \wcf\data\language\Language |
d335fa16 | 123 | */ |
85099417 | 124 | protected static $languageObj; |
d335fa16 AE |
125 | |
126 | /** | |
127 | * overrides disabled debug mode | |
128 | * @var boolean | |
129 | */ | |
130 | protected static $overrideDebugMode = false; | |
131 | ||
132 | /** | |
133 | * session object | |
4e25add7 | 134 | * @var SessionHandler |
d335fa16 | 135 | */ |
85099417 | 136 | protected static $sessionObj; |
d335fa16 AE |
137 | |
138 | /** | |
139 | * template object | |
4e25add7 | 140 | * @var TemplateEngine |
d335fa16 | 141 | */ |
85099417 | 142 | protected static $tplObj; |
d335fa16 AE |
143 | |
144 | /** | |
145 | * true if Zend Opcache is loaded and enabled | |
146 | * @var boolean | |
147 | */ | |
85099417 | 148 | protected static $zendOpcacheEnabled; |
d335fa16 | 149 | |
ab84d9ca AE |
150 | /** |
151 | * force logout during destructor call | |
152 | * @var boolean | |
153 | */ | |
154 | protected static $forceLogout = false; | |
155 | ||
d335fa16 AE |
156 | /** |
157 | * Calls all init functions of the WCF class. | |
158 | */ | |
159 | public function __construct() { | |
160 | // add autoload directory | |
161 | self::$autoloadDirectories['wcf'] = WCF_DIR . 'lib/'; | |
162 | ||
163 | // define tmp directory | |
164 | if (!defined('TMP_DIR')) define('TMP_DIR', FileUtil::getTempFolder()); | |
165 | ||
166 | // start initialization | |
d335fa16 AE |
167 | $this->initDB(); |
168 | $this->loadOptions(); | |
169 | $this->initSession(); | |
170 | $this->initLanguage(); | |
171 | $this->initTPL(); | |
172 | $this->initCronjobs(); | |
173 | $this->initCoreObjects(); | |
174 | $this->initApplications(); | |
175 | $this->initBlacklist(); | |
176 | ||
177 | EventHandler::getInstance()->fireAction($this, 'initialized'); | |
178 | } | |
179 | ||
180 | /** | |
3ba197fa TD |
181 | * Flushes the output, closes the session, performs background tasks and more. |
182 | * | |
183 | * You *must* not create output in here under normal circumstances, as it might get eaten | |
184 | * when gzip is enabled. | |
d335fa16 AE |
185 | */ |
186 | public static function destruct() { | |
187 | try { | |
188 | // database has to be initialized | |
189 | if (!is_object(self::$dbObj)) return; | |
190 | ||
3ba197fa TD |
191 | $debug = self::debugModeIsEnabled(true); |
192 | if (!$debug) { | |
193 | // flush output | |
194 | if (ob_get_level()) ob_end_flush(); | |
d335fa16 | 195 | flush(); |
3ba197fa TD |
196 | |
197 | // close connection if using FPM | |
198 | if (function_exists('fastcgi_finish_request')) fastcgi_finish_request(); | |
d335fa16 AE |
199 | } |
200 | ||
201 | // update session | |
202 | if (is_object(self::getSession())) { | |
ab84d9ca AE |
203 | if (self::$forceLogout) { |
204 | // do logout | |
1eb97b3a | 205 | self::getSession()->delete(); |
ab84d9ca AE |
206 | } |
207 | else { | |
208 | self::getSession()->update(); | |
209 | } | |
d335fa16 AE |
210 | } |
211 | ||
11117cd5 AE |
212 | // execute shutdown actions of storage handlers |
213 | RegistryHandler::getInstance()->shutdown(); | |
d335fa16 AE |
214 | UserStorageHandler::getInstance()->shutdown(); |
215 | } | |
216 | catch (\Exception $exception) { | |
217 | die("<pre>WCF::destruct() Unhandled exception: ".$exception->getMessage()."\n\n".$exception->getTraceAsString()); | |
218 | } | |
219 | } | |
220 | ||
d335fa16 AE |
221 | /** |
222 | * Returns the database object. | |
223 | * | |
224 | * @return \wcf\system\database\Database | |
225 | */ | |
226 | public static final function getDB() { | |
227 | return self::$dbObj; | |
228 | } | |
229 | ||
230 | /** | |
231 | * Returns the session object. | |
232 | * | |
4e25add7 | 233 | * @return SessionHandler |
d335fa16 AE |
234 | */ |
235 | public static final function getSession() { | |
236 | return self::$sessionObj; | |
237 | } | |
238 | ||
239 | /** | |
240 | * Returns the user object. | |
241 | * | |
242 | * @return \wcf\data\user\User | |
243 | */ | |
244 | public static final function getUser() { | |
245 | return self::getSession()->getUser(); | |
246 | } | |
247 | ||
248 | /** | |
249 | * Returns the language object. | |
250 | * | |
251 | * @return \wcf\data\language\Language | |
252 | */ | |
253 | public static final function getLanguage() { | |
254 | return self::$languageObj; | |
255 | } | |
256 | ||
257 | /** | |
258 | * Returns the template object. | |
259 | * | |
4e25add7 | 260 | * @return TemplateEngine |
d335fa16 AE |
261 | */ |
262 | public static final function getTPL() { | |
263 | return self::$tplObj; | |
264 | } | |
265 | ||
266 | /** | |
267 | * Calls the show method on the given exception. | |
268 | * | |
269 | * @param \Exception $e | |
270 | */ | |
e09c7044 | 271 | public static final function handleException($e) { |
cbf81afa AE |
272 | // backwards compatibility |
273 | if ($e instanceof IPrintableException) { | |
274 | $e->show(); | |
275 | exit; | |
276 | } | |
277 | ||
f8434d25 | 278 | if (ob_get_level()) { |
1615fc2e | 279 | // discard any output generated before the exception occurred, prevents exception |
f8434d25 | 280 | // being hidden inside HTML elements and therefore not visible in browser output |
f46ac60e AE |
281 | // |
282 | // ob_get_level() can return values > 1, if the PHP setting `output_buffering` is on | |
283 | while (ob_get_level()) ob_end_clean(); | |
5f2c3ab4 | 284 | |
fef5bd1f AE |
285 | // Some webservers are broken and will apply gzip encoding at all cost, but they fail |
286 | // to set a proper `Content-Encoding` HTTP header and mess things up even more. | |
287 | // Especially the `identity` value appears to be unrecognized by some of them, hence | |
288 | // we'll just gzip the output of the exception to prevent them from tampering. | |
289 | // This part is copied from `HeaderUtil` in order to isolate the exception handler! | |
4d7a0b73 | 290 | if (defined('HTTP_ENABLE_GZIP') && HTTP_ENABLE_GZIP && !defined('HTTP_DISABLE_GZIP')) { |
ac8615e4 LK |
291 | if (function_exists('gzcompress') && !@ini_get('zlib.output_compression') && !@ini_get('output_handler') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) { |
292 | if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false) { | |
fef5bd1f AE |
293 | @header('Content-Encoding: x-gzip'); |
294 | } | |
295 | else { | |
296 | @header('Content-Encoding: gzip'); | |
297 | } | |
298 | ||
299 | ob_start(function($output) { | |
300 | $size = strlen($output); | |
301 | $crc = crc32($output); | |
302 | ||
303 | $newOutput = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff"; | |
304 | $newOutput .= substr(gzcompress($output, 1), 2, -4); | |
305 | $newOutput .= pack('V', $crc); | |
306 | $newOutput .= pack('V', $size); | |
307 | ||
308 | return $newOutput; | |
309 | }); | |
310 | } | |
311 | } | |
f8434d25 AE |
312 | } |
313 | ||
5bc5a7e6 | 314 | @header('HTTP/1.1 503 Service Unavailable'); |
d335fa16 | 315 | try { |
5bc5a7e6 | 316 | \wcf\functions\exception\printThrowable($e); |
d335fa16 | 317 | } |
5bc5a7e6 TD |
318 | catch (\Throwable $e2) { |
319 | echo "<pre>An Exception was thrown while handling an Exception:\n\n"; | |
6b7d6653 | 320 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e2); |
5bc5a7e6 | 321 | echo "\n\nwas thrown while:\n\n"; |
6b7d6653 | 322 | echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e); |
5bc5a7e6 TD |
323 | echo "\n\nwas handled.</pre>"; |
324 | exit; | |
e09c7044 | 325 | } |
d335fa16 AE |
326 | } |
327 | ||
328 | /** | |
b25ad8f3 | 329 | * Turns PHP errors into an ErrorException. |
d335fa16 | 330 | * |
ac52543a | 331 | * @param integer $severity |
d335fa16 | 332 | * @param string $message |
ac52543a MS |
333 | * @param string $file |
334 | * @param integer $line | |
2b770bdd | 335 | * @throws ErrorException |
d335fa16 | 336 | */ |
b25ad8f3 | 337 | public static final function handleError($severity, $message, $file, $line) { |
b2aa772d | 338 | // this is necessary for the shut-up operator |
0267fa9a | 339 | if (!(error_reporting() & $severity)) return; |
b25ad8f3 TD |
340 | |
341 | throw new ErrorException($message, 0, $severity, $file, $line); | |
d335fa16 AE |
342 | } |
343 | ||
344 | /** | |
345 | * Loads the database configuration and creates a new connection to the database. | |
346 | */ | |
347 | protected function initDB() { | |
348 | // get configuration | |
349 | $dbHost = $dbUser = $dbPassword = $dbName = ''; | |
350 | $dbPort = 0; | |
20e541d6 | 351 | $defaultDriverOptions = []; |
d335fa16 AE |
352 | require(WCF_DIR.'config.inc.php'); |
353 | ||
354 | // create database connection | |
20e541d6 | 355 | self::$dbObj = new MySQLDatabase($dbHost, $dbUser, $dbPassword, $dbName, $dbPort, false, false, $defaultDriverOptions); |
d335fa16 AE |
356 | } |
357 | ||
358 | /** | |
359 | * Loads the options file, automatically created if not exists. | |
360 | */ | |
361 | protected function loadOptions() { | |
7b0c5bf1 | 362 | // The attachment module is always enabled since 5.2. |
85099417 AE |
363 | // https://github.com/WoltLab/WCF/issues/2531 |
364 | define('MODULE_ATTACHMENT', 1); | |
365 | ||
7b0c5bf1 AE |
366 | // Users cannot react to their own content since 5.2. |
367 | // https://github.com/WoltLab/WCF/issues/2975 | |
368 | define('LIKE_ALLOW_FOR_OWN_CONTENT', 0); | |
369 | define('LIKE_ENABLE_DISLIKE', 0); | |
370 | ||
86eabb3e | 371 | // Thumbnails for attachments are already enabled since 5.3. |
2cf67fd4 | 372 | // https://github.com/WoltLab/WCF/pull/3444 |
86eabb3e AE |
373 | define('ATTACHMENT_ENABLE_THUMBNAILS', 1); |
374 | ||
e6fa5c10 MS |
375 | // User markings are always applied in sidebars since 5.3. |
376 | // https://github.com/WoltLab/WCF/issues/3330 | |
377 | define('MESSAGE_SIDEBAR_ENABLE_USER_ONLINE_MARKING', 1); | |
378 | ||
3e842842 TD |
379 | // Password strength configuration is deprecated since 5.3. |
380 | define('REGISTER_ENABLE_PASSWORD_SECURITY_CHECK', 0); | |
381 | define('REGISTER_PASSWORD_MIN_LENGTH', 0); | |
382 | define('REGISTER_PASSWORD_MUST_CONTAIN_LOWER_CASE', 8); | |
383 | define('REGISTER_PASSWORD_MUST_CONTAIN_UPPER_CASE', 0); | |
384 | define('REGISTER_PASSWORD_MUST_CONTAIN_DIGIT', 0); | |
385 | define('REGISTER_PASSWORD_MUST_CONTAIN_SPECIAL_CHAR', 0); | |
88beebcf | 386 | |
533948aa MW |
387 | // rel=nofollow is always applied to external link since 5.3 |
388 | // https://github.com/WoltLab/WCF/issues/3339 | |
389 | define('EXTERNAL_LINK_REL_NOFOLLOW', 1); | |
3e842842 | 390 | |
dbaf3d3e TD |
391 | // Session validation is removed since 5.4. |
392 | // https://github.com/WoltLab/WCF/pull/3583 | |
393 | define('SESSION_VALIDATE_IP_ADDRESS', 0); | |
8b0284c9 | 394 | define('SESSION_VALIDATE_USER_AGENT', 0); |
dbaf3d3e | 395 | |
a1211f27 TD |
396 | // Virtual sessions no longer exist since 5.4. |
397 | define('SESSION_ENABLE_VIRTUALIZATION', 1); | |
398 | ||
6096fe15 TD |
399 | // The session timeout is fully managed since 5.4. |
400 | define('SESSION_TIMEOUT', 3600); | |
401 | ||
d335fa16 AE |
402 | $filename = WCF_DIR.'options.inc.php'; |
403 | ||
404 | // create options file if doesn't exist | |
405 | if (!file_exists($filename) || filemtime($filename) <= 1) { | |
406 | OptionEditor::rebuild(); | |
407 | } | |
11beb0a0 | 408 | require($filename); |
b842faa1 AE |
409 | |
410 | // check if option file is complete and writable | |
411 | if (PACKAGE_ID) { | |
412 | if (!is_writable($filename)) { | |
413 | FileUtil::makeWritable($filename); | |
414 | ||
415 | if (!is_writable($filename)) { | |
416 | throw new SystemException("The option file '" . $filename . "' is not writable."); | |
417 | } | |
418 | } | |
419 | ||
420 | // check if a previous write operation was incomplete and force rebuilding | |
421 | if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) { | |
422 | OptionEditor::rebuild(); | |
423 | ||
11beb0a0 | 424 | require($filename); |
b842faa1 | 425 | } |
34c330f8 AE |
426 | |
427 | if (ENABLE_DEBUG_MODE) { | |
428 | self::$dbObj->enableDebugMode(); | |
429 | } | |
b842faa1 | 430 | } |
d335fa16 AE |
431 | } |
432 | ||
433 | /** | |
434 | * Starts the session system. | |
435 | */ | |
436 | protected function initSession() { | |
f341086b | 437 | $factory = new SessionFactory(); |
d335fa16 AE |
438 | $factory->load(); |
439 | ||
f341086b | 440 | self::$sessionObj = SessionHandler::getInstance(); |
d335fa16 AE |
441 | } |
442 | ||
443 | /** | |
444 | * Initialises the language engine. | |
445 | */ | |
446 | protected function initLanguage() { | |
447 | if (isset($_GET['l']) && !self::getUser()->userID) { | |
448 | self::getSession()->setLanguageID(intval($_GET['l'])); | |
449 | } | |
450 | ||
451 | // set mb settings | |
452 | mb_internal_encoding('UTF-8'); | |
453 | if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8'); | |
454 | mb_language('uni'); | |
455 | ||
456 | // get language | |
f341086b | 457 | self::$languageObj = LanguageFactory::getInstance()->getUserLanguage(self::getSession()->getLanguageID()); |
d335fa16 AE |
458 | } |
459 | ||
460 | /** | |
461 | * Initialises the template engine. | |
462 | */ | |
463 | protected function initTPL() { | |
f341086b | 464 | self::$tplObj = TemplateEngine::getInstance(); |
d335fa16 AE |
465 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); |
466 | $this->assignDefaultTemplateVariables(); | |
467 | ||
468 | $this->initStyle(); | |
469 | } | |
470 | ||
471 | /** | |
472 | * Initializes the user's style. | |
473 | */ | |
474 | protected function initStyle() { | |
475 | if (isset($_REQUEST['styleID'])) { | |
476 | self::getSession()->setStyleID(intval($_REQUEST['styleID'])); | |
477 | } | |
478 | ||
f341086b | 479 | $styleHandler = StyleHandler::getInstance(); |
d11a8c9e | 480 | $styleHandler->changeStyle(self::getSession()->getStyleID()); |
d335fa16 AE |
481 | } |
482 | ||
483 | /** | |
484 | * Executes the blacklist. | |
485 | */ | |
486 | protected function initBlacklist() { | |
fa75ccfb AE |
487 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); |
488 | ||
d335fa16 | 489 | if (defined('BLACKLIST_IP_ADDRESSES') && BLACKLIST_IP_ADDRESSES != '') { |
39c7ff4f | 490 | if (!StringUtil::executeWordFilter(UserUtil::convertIPv6To4(UserUtil::getIpAddress()), BLACKLIST_IP_ADDRESSES)) { |
fa75ccfb | 491 | if ($isAjax) { |
a1721f86 | 492 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
493 | } |
494 | else { | |
495 | throw new PermissionDeniedException(); | |
496 | } | |
dc0015ab | 497 | } |
39c7ff4f | 498 | else if (!StringUtil::executeWordFilter(UserUtil::getIpAddress(), BLACKLIST_IP_ADDRESSES)) { |
fa75ccfb | 499 | if ($isAjax) { |
a1721f86 | 500 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
501 | } |
502 | else { | |
503 | throw new PermissionDeniedException(); | |
504 | } | |
d335fa16 AE |
505 | } |
506 | } | |
507 | if (defined('BLACKLIST_USER_AGENTS') && BLACKLIST_USER_AGENTS != '') { | |
ce64787b | 508 | if (!StringUtil::executeWordFilter(UserUtil::getUserAgent(), BLACKLIST_USER_AGENTS)) { |
fa75ccfb | 509 | if ($isAjax) { |
a1721f86 | 510 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
511 | } |
512 | else { | |
513 | throw new PermissionDeniedException(); | |
514 | } | |
d335fa16 AE |
515 | } |
516 | } | |
517 | if (defined('BLACKLIST_HOSTNAMES') && BLACKLIST_HOSTNAMES != '') { | |
39c7ff4f | 518 | if (!StringUtil::executeWordFilter(@gethostbyaddr(UserUtil::getIpAddress()), BLACKLIST_HOSTNAMES)) { |
fa75ccfb | 519 | if ($isAjax) { |
a1721f86 | 520 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
521 | } |
522 | else { | |
523 | throw new PermissionDeniedException(); | |
524 | } | |
d335fa16 AE |
525 | } |
526 | } | |
527 | ||
528 | // handle banned users | |
d0ebece3 | 529 | if (self::getUser()->userID && self::getUser()->banned && !self::getUser()->hasOwnerAccess()) { |
fa75ccfb | 530 | if ($isAjax) { |
d335fa16 AE |
531 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'), AJAXException::INSUFFICIENT_PERMISSIONS); |
532 | } | |
533 | else { | |
ab84d9ca AE |
534 | self::$forceLogout = true; |
535 | ||
536 | // remove cookies | |
537 | if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) { | |
538 | HeaderUtil::setCookie('userID', 0); | |
539 | } | |
540 | if (isset($_COOKIE[COOKIE_PREFIX.'password'])) { | |
541 | HeaderUtil::setCookie('password', ''); | |
542 | } | |
543 | ||
d335fa16 AE |
544 | throw new NamedUserException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned')); |
545 | } | |
546 | } | |
547 | } | |
548 | ||
549 | /** | |
550 | * Initializes applications. | |
551 | */ | |
552 | protected function initApplications() { | |
553 | // step 1) load all applications | |
058cbd6a | 554 | $loadedApplications = []; |
d335fa16 AE |
555 | |
556 | // register WCF as application | |
39abe192 | 557 | self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1); |
d335fa16 | 558 | |
39abe192 | 559 | if (!class_exists(WCFACP::class, false)) { |
b2b2c26b | 560 | static::getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL()); |
39abe192 AE |
561 | } |
562 | ||
d335fa16 AE |
563 | // start main application |
564 | $application = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 AE |
565 | if ($application->packageID != 1) { |
566 | $loadedApplications[] = $this->loadApplication($application); | |
567 | ||
568 | // register primary application | |
569 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); | |
570 | self::$applications[$abbreviation] = $application; | |
571 | } | |
d335fa16 AE |
572 | |
573 | // start dependent applications | |
574 | $applications = ApplicationHandler::getInstance()->getDependentApplications(); | |
575 | foreach ($applications as $application) { | |
39abe192 AE |
576 | if ($application->packageID == 1) { |
577 | // ignore WCF | |
578 | continue; | |
579 | } | |
f4eafe37 AE |
580 | else if ($application->isTainted) { |
581 | // ignore apps flagged for uninstallation | |
582 | continue; | |
583 | } | |
39abe192 | 584 | |
d335fa16 AE |
585 | $loadedApplications[] = $this->loadApplication($application, true); |
586 | } | |
587 | ||
588 | // step 2) run each application | |
589 | if (!class_exists('wcf\system\WCFACP', false)) { | |
f341086b | 590 | /** @var IApplication $application */ |
d335fa16 AE |
591 | foreach ($loadedApplications as $application) { |
592 | $application->__run(); | |
593 | } | |
594 | ||
96bd8977 TD |
595 | /** @deprecated The below variable is deprecated. */ |
596 | self::getTPL()->assign('__sessionKeepAlive', 59 * 60); | |
d335fa16 AE |
597 | } |
598 | } | |
599 | ||
600 | /** | |
601 | * Loads an application. | |
602 | * | |
2b770bdd MS |
603 | * @param Application $application |
604 | * @param boolean $isDependentApplication | |
605 | * @return IApplication | |
606 | * @throws SystemException | |
d335fa16 AE |
607 | */ |
608 | protected function loadApplication(Application $application, $isDependentApplication = false) { | |
d335fa16 AE |
609 | $package = PackageCache::getInstance()->getPackage($application->packageID); |
610 | // package cache might be outdated | |
611 | if ($package === null) { | |
612 | $package = new Package($application->packageID); | |
613 | ||
614 | // package cache is outdated, discard cache | |
615 | if ($package->packageID) { | |
616 | PackageEditor::resetCache(); | |
617 | } | |
618 | else { | |
619 | // package id is invalid | |
620 | throw new SystemException("application identified by package id '".$application->packageID."' is unknown"); | |
621 | } | |
622 | } | |
e54191ea | 623 | |
d335fa16 AE |
624 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); |
625 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
626 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
627 | ||
628 | $className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core'; | |
59162cf3 AE |
629 | |
630 | // class was not found, possibly the app was moved, but `packageDir` has not been adjusted | |
631 | if (!class_exists($className)) { | |
632 | // check if both the Core and the app are on the same domain | |
633 | $coreApp = ApplicationHandler::getInstance()->getApplicationByID(1); | |
634 | if ($coreApp->domainName === $application->domainName) { | |
635 | // resolve the relative path and use it to construct the autoload directory | |
636 | $relativePath = FileUtil::getRelativePath($coreApp->domainPath, $application->domainPath); | |
637 | if ($relativePath !== './') { | |
638 | $packageDir = FileUtil::getRealPath(WCF_DIR.$relativePath); | |
639 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
640 | ||
641 | if (class_exists($className)) { | |
642 | // the class can now be found, update the `packageDir` value | |
643 | (new PackageEditor($package))->update(['packageDir' => $relativePath]); | |
644 | } | |
645 | } | |
646 | } | |
647 | } | |
648 | ||
157054c9 | 649 | if (class_exists($className) && is_subclass_of($className, IApplication::class)) { |
d335fa16 AE |
650 | // include config file |
651 | $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE; | |
b842faa1 AE |
652 | if (!file_exists($configPath)) { |
653 | Package::writeConfigFile($package->packageID); | |
654 | } | |
655 | ||
d335fa16 AE |
656 | if (file_exists($configPath)) { |
657 | require_once($configPath); | |
658 | } | |
659 | else { | |
660 | throw new SystemException('Unable to load configuration for '.$package->package); | |
661 | } | |
662 | ||
663 | // register template path if not within ACP | |
664 | if (!class_exists('wcf\system\WCFACP', false)) { | |
665 | // add template path and abbreviation | |
b2b2c26b | 666 | static::getTPL()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 | 667 | } |
15621401 | 668 | EmailTemplateEngine::getInstance()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 AE |
669 | |
670 | // init application and assign it as template variable | |
058cbd6a | 671 | self::$applicationObjects[$application->packageID] = call_user_func([$className, 'getInstance']); |
b2b2c26b | 672 | static::getTPL()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
15621401 | 673 | EmailTemplateEngine::getInstance()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
d335fa16 AE |
674 | } |
675 | else { | |
676 | unset(self::$autoloadDirectories[$abbreviation]); | |
157054c9 | 677 | throw new SystemException("Unable to run '".$package->package."', '".$className."' is missing or does not implement '".IApplication::class."'."); |
d335fa16 AE |
678 | } |
679 | ||
680 | // register template path in ACP | |
681 | if (class_exists('wcf\system\WCFACP', false)) { | |
b2b2c26b | 682 | static::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); |
d335fa16 AE |
683 | } |
684 | else if (!$isDependentApplication) { | |
685 | // assign base tag | |
b2b2c26b | 686 | static::getTPL()->assign('baseHref', $application->getPageURL()); |
d335fa16 AE |
687 | } |
688 | ||
689 | // register application | |
690 | self::$applications[$abbreviation] = $application; | |
691 | ||
692 | return self::$applicationObjects[$application->packageID]; | |
693 | } | |
694 | ||
695 | /** | |
696 | * Returns the corresponding application object. Does not support the 'wcf' pseudo application. | |
697 | * | |
4e25add7 MS |
698 | * @param Application $application |
699 | * @return IApplication | |
d335fa16 AE |
700 | */ |
701 | public static function getApplicationObject(Application $application) { | |
702 | if (isset(self::$applicationObjects[$application->packageID])) { | |
703 | return self::$applicationObjects[$application->packageID]; | |
704 | } | |
705 | ||
706 | return null; | |
707 | } | |
708 | ||
dbf41d8a AE |
709 | /** |
710 | * Returns the invoked application. | |
711 | * | |
712 | * @return Application | |
8e152e07 | 713 | * @since 3.1 |
dbf41d8a AE |
714 | */ |
715 | public static function getActiveApplication() { | |
716 | return ApplicationHandler::getInstance()->getActiveApplication(); | |
717 | } | |
718 | ||
d335fa16 AE |
719 | /** |
720 | * Loads an application on runtime, do not use this outside the package installation. | |
721 | * | |
722 | * @param integer $packageID | |
723 | */ | |
724 | public static function loadRuntimeApplication($packageID) { | |
725 | $package = new Package($packageID); | |
726 | $application = new Application($packageID); | |
727 | ||
728 | $abbreviation = Package::getAbbreviation($package->package); | |
729 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
730 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
731 | self::$applications[$abbreviation] = $application; | |
732 | self::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); | |
733 | } | |
734 | ||
735 | /** | |
736 | * Initializes core object cache. | |
737 | */ | |
738 | protected function initCoreObjects() { | |
739 | // ignore core objects if installing WCF | |
740 | if (PACKAGE_ID == 0) { | |
741 | return; | |
742 | } | |
743 | ||
744 | self::$coreObjectCache = CoreObjectCacheBuilder::getInstance()->getData(); | |
745 | } | |
746 | ||
747 | /** | |
748 | * Assigns some default variables to the template engine. | |
749 | */ | |
750 | protected function assignDefaultTemplateVariables() { | |
50b7b362 MS |
751 | $wcf = $this; |
752 | ||
753 | if (ENABLE_ENTERPRISE_MODE) { | |
754 | $wcf = new TemplateScriptingCore($wcf); | |
755 | } | |
756 | ||
e2e8c155 | 757 | self::getTPL()->registerPrefilter(['event', 'hascontent', 'lang', 'jslang']); |
058cbd6a | 758 | self::getTPL()->assign([ |
50b7b362 | 759 | '__wcf' => $wcf, |
e71525e4 | 760 | '__wcfVersion' => LAST_UPDATE_TIME // @deprecated 2.1, use LAST_UPDATE_TIME directly |
058cbd6a | 761 | ]); |
54de7a5f TD |
762 | |
763 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); | |
764 | // Execute background queue in this request, if it was requested and AJAX isn't used. | |
765 | if (!$isAjax) { | |
766 | if (self::getSession()->getVar('forceBackgroundQueuePerform')) { | |
767 | self::getTPL()->assign([ | |
768 | 'forceBackgroundQueuePerform' => true | |
769 | ]); | |
770 | ||
771 | self::getSession()->unregister('forceBackgroundQueuePerform'); | |
772 | } | |
773 | } | |
774 | ||
e2e8c155 | 775 | EmailTemplateEngine::getInstance()->registerPrefilter(['event', 'hascontent', 'lang', 'jslang']); |
15621401 | 776 | EmailTemplateEngine::getInstance()->assign([ |
50b7b362 | 777 | '__wcf' => $wcf |
15621401 | 778 | ]); |
d335fa16 AE |
779 | } |
780 | ||
781 | /** | |
782 | * Wrapper for the getter methods of this class. | |
783 | * | |
784 | * @param string $name | |
785 | * @return mixed value | |
2b770bdd | 786 | * @throws SystemException |
d335fa16 AE |
787 | */ |
788 | public function __get($name) { | |
789 | $method = 'get'.ucfirst($name); | |
790 | if (method_exists($this, $method)) { | |
791 | return $this->$method(); | |
792 | } | |
793 | ||
794 | throw new SystemException("method '".$method."' does not exist in class WCF"); | |
795 | } | |
796 | ||
487db634 MW |
797 | /** |
798 | * Returns true if current application (WCF) is treated as active and was invoked directly. | |
799 | * | |
800 | * @return boolean | |
801 | */ | |
802 | public function isActiveApplication() { | |
803 | return (ApplicationHandler::getInstance()->getActiveApplication()->packageID == 1); | |
804 | } | |
805 | ||
d335fa16 AE |
806 | /** |
807 | * Changes the active language. | |
808 | * | |
809 | * @param integer $languageID | |
810 | */ | |
811 | public static final function setLanguage($languageID) { | |
5b8eae2c MW |
812 | if (!$languageID || LanguageFactory::getInstance()->getLanguage($languageID) === null) { |
813 | $languageID = LanguageFactory::getInstance()->getDefaultLanguageID(); | |
814 | } | |
2d90d60f | 815 | |
d335fa16 | 816 | self::$languageObj = LanguageFactory::getInstance()->getLanguage($languageID); |
b372bd53 AE |
817 | |
818 | // the template engine may not be available yet, usually happens when | |
819 | // changing the user (and thus the language id) during session init | |
820 | if (self::$tplObj !== null) { | |
821 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); | |
822 | EmailTemplateEngine::getInstance()->setLanguageID(self::getLanguage()->languageID); | |
823 | } | |
d335fa16 AE |
824 | } |
825 | ||
826 | /** | |
827 | * Includes the required util or exception classes automatically. | |
828 | * | |
829 | * @param string $className | |
830 | * @see spl_autoload_register() | |
831 | */ | |
832 | public static final function autoload($className) { | |
833 | $namespaces = explode('\\', $className); | |
834 | if (count($namespaces) > 1) { | |
835 | $applicationPrefix = array_shift($namespaces); | |
836 | if ($applicationPrefix === '') { | |
837 | $applicationPrefix = array_shift($namespaces); | |
838 | } | |
839 | if (isset(self::$autoloadDirectories[$applicationPrefix])) { | |
840 | $classPath = self::$autoloadDirectories[$applicationPrefix] . implode('/', $namespaces) . '.class.php'; | |
6f609fec AE |
841 | |
842 | // PHP will implicitly check if the file exists when including it, which means that we can save a | |
843 | // redundant syscall/fs access by not checking for existence ourselves. Do not use require_once()! | |
844 | @include_once($classPath); | |
d335fa16 AE |
845 | } |
846 | } | |
847 | } | |
848 | ||
849 | /** | |
0fcfe5f6 | 850 | * @inheritDoc |
d335fa16 AE |
851 | */ |
852 | public final function __call($name, array $arguments) { | |
853 | // bug fix to avoid php crash, see http://bugs.php.net/bug.php?id=55020 | |
854 | if (!method_exists($this, $name)) { | |
855 | return self::__callStatic($name, $arguments); | |
856 | } | |
857 | ||
9b31ab91 | 858 | throw new \BadMethodCallException("Call to undefined method WCF::{$name}()."); |
d335fa16 AE |
859 | } |
860 | ||
861 | /** | |
862 | * Returns dynamically loaded core objects. | |
863 | * | |
864 | * @param string $name | |
865 | * @param array $arguments | |
71952a87 | 866 | * @return object |
2b770bdd | 867 | * @throws SystemException |
d335fa16 AE |
868 | */ |
869 | public static final function __callStatic($name, array $arguments) { | |
870 | $className = preg_replace('~^get~', '', $name); | |
871 | ||
872 | if (isset(self::$coreObject[$className])) { | |
873 | return self::$coreObject[$className]; | |
874 | } | |
875 | ||
876 | $objectName = self::getCoreObject($className); | |
877 | if ($objectName === null) { | |
878 | throw new SystemException("Core object '".$className."' is unknown."); | |
879 | } | |
880 | ||
881 | if (class_exists($objectName)) { | |
63b9817b | 882 | if (!is_subclass_of($objectName, SingletonFactory::class)) { |
7b9ff46b | 883 | throw new ParentClassException($objectName, SingletonFactory::class); |
d335fa16 AE |
884 | } |
885 | ||
058cbd6a | 886 | self::$coreObject[$className] = call_user_func([$objectName, 'getInstance']); |
d335fa16 AE |
887 | return self::$coreObject[$className]; |
888 | } | |
889 | } | |
890 | ||
891 | /** | |
892 | * Searches for cached core object definition. | |
893 | * | |
894 | * @param string $className | |
895 | * @return string | |
896 | */ | |
897 | protected static final function getCoreObject($className) { | |
898 | if (isset(self::$coreObjectCache[$className])) { | |
899 | return self::$coreObjectCache[$className]; | |
900 | } | |
901 | ||
902 | return null; | |
903 | } | |
904 | ||
905 | /** | |
906 | * Returns true if the debug mode is enabled, otherwise false. | |
907 | * | |
908 | * @param boolean $ignoreACP | |
909 | * @return boolean | |
910 | */ | |
911 | public static function debugModeIsEnabled($ignoreACP = false) { | |
912 | // ACP override | |
913 | if (!$ignoreACP && self::$overrideDebugMode) { | |
914 | return true; | |
915 | } | |
916 | else if (defined('ENABLE_DEBUG_MODE') && ENABLE_DEBUG_MODE) { | |
917 | return true; | |
918 | } | |
919 | ||
920 | return false; | |
921 | } | |
922 | ||
923 | /** | |
924 | * Returns true if benchmarking is enabled, otherwise false. | |
925 | * | |
926 | * @return boolean | |
927 | */ | |
928 | public static function benchmarkIsEnabled() { | |
929 | // benchmarking is enabled by default | |
930 | if (!defined('ENABLE_BENCHMARK') || ENABLE_BENCHMARK) return true; | |
931 | return false; | |
932 | } | |
933 | ||
934 | /** | |
935 | * Returns domain path for given application. | |
936 | * | |
937 | * @param string $abbreviation | |
938 | * @return string | |
939 | */ | |
940 | public static function getPath($abbreviation = 'wcf') { | |
941 | // workaround during WCFSetup | |
942 | if (!PACKAGE_ID) { | |
943 | return '../'; | |
944 | } | |
945 | ||
946 | if (!isset(self::$applications[$abbreviation])) { | |
947 | $abbreviation = 'wcf'; | |
948 | } | |
949 | ||
950 | return self::$applications[$abbreviation]->getPageURL(); | |
951 | } | |
952 | ||
5d1c96bc AE |
953 | /** |
954 | * Returns the domain path for the currently active application, | |
955 | * used to avoid CORS requests. | |
956 | * | |
957 | * @return string | |
958 | */ | |
959 | public static function getActivePath() { | |
960 | if (!PACKAGE_ID) { | |
961 | return self::getPath(); | |
962 | } | |
963 | ||
12a7c542 AE |
964 | // We cannot rely on the ApplicationHandler's `getActiveApplication()` because |
965 | // it uses the requested controller to determine the namespace. However, starting | |
966 | // with WoltLab Suite 5.2, system pages can be virtually assigned to a different | |
967 | // app, resolving against the target app without changing the namespace. | |
968 | return self::getPath(ApplicationHandler::getInstance()->getAbbreviation(PACKAGE_ID)); | |
5d1c96bc AE |
969 | } |
970 | ||
d335fa16 AE |
971 | /** |
972 | * Returns a fully qualified anchor for current page. | |
973 | * | |
974 | * @param string $fragment | |
975 | * @return string | |
976 | */ | |
977 | public function getAnchor($fragment) { | |
694f6545 | 978 | return StringUtil::encodeHTML(self::getRequestURI() . '#' . $fragment); |
d335fa16 AE |
979 | } |
980 | ||
12a2ff01 AE |
981 | /** |
982 | * Returns the currently active page or null if unknown. | |
983 | * | |
b212ba66 | 984 | * @return Page|null |
12a2ff01 AE |
985 | */ |
986 | public static function getActivePage() { | |
a039ebd2 AE |
987 | if (self::getActiveRequest() === null) { |
988 | return null; | |
989 | } | |
990 | ||
5346b183 AE |
991 | if (self::getActiveRequest()->getClassName() === CmsPage::class) { |
992 | $metaData = self::getActiveRequest()->getMetaData(); | |
b212ba66 MS |
993 | if (isset($metaData['cms'])) { |
994 | return PageCache::getInstance()->getPage($metaData['cms']['pageID']); | |
995 | } | |
996 | ||
997 | return null; | |
5346b183 AE |
998 | } |
999 | ||
12a2ff01 AE |
1000 | return PageCache::getInstance()->getPageByController(self::getActiveRequest()->getClassName()); |
1001 | } | |
1002 | ||
1003 | /** | |
1004 | * Returns the currently active request. | |
1005 | * | |
1006 | * @return Request | |
1007 | */ | |
1008 | public static function getActiveRequest() { | |
1009 | return RequestHandler::getInstance()->getActiveRequest(); | |
1010 | } | |
1011 | ||
d335fa16 AE |
1012 | /** |
1013 | * Returns the URI of the current page. | |
1014 | * | |
1015 | * @return string | |
1016 | */ | |
1017 | public static function getRequestURI() { | |
c68f6161 | 1018 | return preg_replace('~^(https?://[^/]+)(?:/.*)?$~', '$1', self::getTPL()->get('baseHref')) . $_SERVER['REQUEST_URI']; |
d335fa16 AE |
1019 | } |
1020 | ||
1021 | /** | |
1022 | * Resets Zend Opcache cache if installed and enabled. | |
1023 | * | |
1024 | * @param string $script | |
1025 | */ | |
1026 | public static function resetZendOpcache($script = '') { | |
1027 | if (self::$zendOpcacheEnabled === null) { | |
1028 | self::$zendOpcacheEnabled = false; | |
1029 | ||
1030 | if (extension_loaded('Zend Opcache') && @ini_get('opcache.enable')) { | |
1031 | self::$zendOpcacheEnabled = true; | |
1032 | } | |
1033 | ||
1034 | } | |
1035 | ||
1036 | if (self::$zendOpcacheEnabled) { | |
1037 | if (empty($script)) { | |
d631fa86 | 1038 | \opcache_reset(); |
d335fa16 AE |
1039 | } |
1040 | else { | |
d631fa86 | 1041 | \opcache_invalidate($script, true); |
d335fa16 AE |
1042 | } |
1043 | } | |
1044 | } | |
1045 | ||
1046 | /** | |
1047 | * Returns style handler. | |
1048 | * | |
4e25add7 | 1049 | * @return StyleHandler |
d335fa16 AE |
1050 | */ |
1051 | public function getStyleHandler() { | |
1052 | return StyleHandler::getInstance(); | |
1053 | } | |
1054 | ||
55b402a0 MW |
1055 | /** |
1056 | * Returns box handler. | |
1057 | * | |
1058 | * @return BoxHandler | |
e71525e4 | 1059 | * @since 3.0 |
55b402a0 MW |
1060 | */ |
1061 | public function getBoxHandler() { | |
1062 | return BoxHandler::getInstance(); | |
1063 | } | |
1064 | ||
d335fa16 AE |
1065 | /** |
1066 | * Returns number of available updates. | |
1067 | * | |
1068 | * @return integer | |
1069 | */ | |
1070 | public function getAvailableUpdates() { | |
1071 | $data = PackageUpdateCacheBuilder::getInstance()->getData(); | |
1072 | return $data['updates']; | |
1073 | } | |
1074 | ||
1075 | /** | |
1076 | * Returns a 8 character prefix for editor autosave. | |
1077 | * | |
1078 | * @return string | |
1079 | */ | |
1080 | public function getAutosavePrefix() { | |
1081 | return substr(sha1(preg_replace('~^https~', 'http', self::getPath())), 0, 8); | |
1082 | } | |
1083 | ||
1084 | /** | |
1085 | * Returns the favicon URL or a base64 encoded image. | |
1086 | * | |
1087 | * @return string | |
1088 | */ | |
1089 | public function getFavicon() { | |
1090 | $activeApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 | 1091 | $wcf = ApplicationHandler::getInstance()->getWCF(); |
aef117b5 | 1092 | $favicon = StyleHandler::getInstance()->getStyle()->getRelativeFavicon(); |
d335fa16 | 1093 | |
39abe192 | 1094 | if ($activeApplication->domainName !== $wcf->domainName) { |
aef117b5 AE |
1095 | if (file_exists(WCF_DIR.$favicon)) { |
1096 | $favicon = file_get_contents(WCF_DIR.$favicon); | |
d335fa16 AE |
1097 | |
1098 | return 'data:image/x-icon;base64,' . base64_encode($favicon); | |
1099 | } | |
1100 | } | |
1101 | ||
aef117b5 | 1102 | return self::getPath() . $favicon; |
d335fa16 AE |
1103 | } |
1104 | ||
6e4aa12f AE |
1105 | /** |
1106 | * Returns true if the desktop notifications should be enabled. | |
1107 | * | |
1108 | * @return boolean | |
1109 | */ | |
1110 | public function useDesktopNotifications() { | |
1111 | if (!ENABLE_DESKTOP_NOTIFICATIONS) { | |
1112 | return false; | |
1113 | } | |
1114 | else if (ApplicationHandler::getInstance()->isMultiDomainSetup()) { | |
1115 | $application = ApplicationHandler::getInstance()->getApplicationByID(DESKTOP_NOTIFICATION_PACKAGE_ID); | |
1116 | // mismatch, default to Core | |
1117 | if ($application === null) $application = ApplicationHandler::getInstance()->getApplicationByID(1); | |
1118 | ||
1119 | $currentApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
1120 | if ($currentApplication->domainName != $application->domainName) { | |
1121 | // different domain | |
1122 | return false; | |
1123 | } | |
1124 | } | |
1125 | ||
1126 | return true; | |
1127 | } | |
1128 | ||
a80873d5 AE |
1129 | /** |
1130 | * Returns true if currently active request represents the landing page. | |
1131 | * | |
8ff2cd79 | 1132 | * @return boolean |
a80873d5 | 1133 | */ |
0fdd7f96 | 1134 | public static function isLandingPage() { |
a039ebd2 AE |
1135 | if (self::getActiveRequest() === null) { |
1136 | return false; | |
1137 | } | |
1138 | ||
1139 | return self::getActiveRequest()->isLandingPage(); | |
a80873d5 AE |
1140 | } |
1141 | ||
89484ba0 AE |
1142 | /** |
1143 | * Returns true if the given API version is currently supported. | |
1144 | * | |
1145 | * @param integer $apiVersion | |
1146 | * @return boolean | |
0e8af3ef | 1147 | * @deprecated 5.2 |
89484ba0 AE |
1148 | */ |
1149 | public static function isSupportedApiVersion($apiVersion) { | |
1150 | return ($apiVersion == WSC_API_VERSION) || in_array($apiVersion, self::$supportedLegacyApiVersions); | |
1151 | } | |
1152 | ||
1153 | /** | |
1154 | * Returns the list of supported legacy API versions. | |
1155 | * | |
1156 | * @return integer[] | |
0e8af3ef | 1157 | * @deprecated 5.2 |
89484ba0 AE |
1158 | */ |
1159 | public static function getSupportedLegacyApiVersions() { | |
1160 | return self::$supportedLegacyApiVersions; | |
1161 | } | |
1162 | ||
d335fa16 AE |
1163 | /** |
1164 | * Initialises the cronjobs. | |
1165 | */ | |
1166 | protected function initCronjobs() { | |
1167 | if (PACKAGE_ID) { | |
63b9817b | 1168 | self::getTPL()->assign('executeCronjobs', CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE); |
d335fa16 AE |
1169 | } |
1170 | } | |
66d9b64b MS |
1171 | |
1172 | /** | |
1173 | * Checks recursively that the most important system files of `com.woltlab.wcf` are writable. | |
1174 | * | |
1175 | * @throws \RuntimeException if any relevant file or directory is not writable | |
1176 | */ | |
1177 | public static function checkWritability() { | |
1178 | $nonWritablePaths = []; | |
1179 | ||
1180 | $nonRecursiveDirectories = [ | |
1181 | '', | |
1182 | 'acp/', | |
1183 | 'tmp/' | |
1184 | ]; | |
1185 | foreach ($nonRecursiveDirectories as $directory) { | |
1186 | $path = WCF_DIR . $directory; | |
1187 | if ($path === 'tmp/' && !is_dir($path)) { | |
1188 | continue; | |
1189 | } | |
1190 | ||
1191 | if (!is_writable($path)) { | |
7f754c71 | 1192 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path); |
66d9b64b MS |
1193 | continue; |
1194 | } | |
1195 | ||
1196 | DirectoryUtil::getInstance($path, false)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { | |
1197 | if ($fileInfo instanceof \DirectoryIterator) { | |
1198 | if (!is_writable($fileInfo->getPath())) { | |
7f754c71 | 1199 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()); |
66d9b64b MS |
1200 | } |
1201 | } | |
1202 | else if (!is_writable($fileInfo->getRealPath())) { | |
7f754c71 | 1203 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()) . $fileInfo->getFilename(); |
66d9b64b MS |
1204 | } |
1205 | }); | |
1206 | } | |
1207 | ||
1208 | $recursiveDirectories = [ | |
1209 | 'acp/js/', | |
1210 | 'acp/style/', | |
1211 | 'acp/templates/', | |
1212 | 'acp/uninstall/', | |
1213 | 'js/', | |
1214 | 'lib/', | |
1215 | 'log/', | |
1216 | 'style/', | |
1217 | 'templates/' | |
1218 | ]; | |
1219 | foreach ($recursiveDirectories as $directory) { | |
1220 | $path = WCF_DIR . $directory; | |
1221 | ||
1222 | if (!is_writable($path)) { | |
7f754c71 | 1223 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path); |
66d9b64b MS |
1224 | continue; |
1225 | } | |
1226 | ||
1227 | DirectoryUtil::getInstance($path)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { | |
1228 | if ($fileInfo instanceof \DirectoryIterator) { | |
1229 | if (!is_writable($fileInfo->getPath())) { | |
7f754c71 | 1230 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()); |
66d9b64b MS |
1231 | } |
1232 | } | |
1233 | else if (!is_writable($fileInfo->getRealPath())) { | |
7f754c71 | 1234 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()) . $fileInfo->getFilename(); |
66d9b64b MS |
1235 | } |
1236 | }); | |
1237 | } | |
1238 | ||
1239 | if (!empty($nonWritablePaths)) { | |
1240 | $maxPaths = 10; | |
cf23b193 | 1241 | throw new \RuntimeException('The following paths are not writable: ' . implode(',', array_slice($nonWritablePaths, 0, $maxPaths)) . (count($nonWritablePaths) > $maxPaths ? ',' . StringUtil::HELLIP : '')); |
66d9b64b MS |
1242 | } |
1243 | } | |
d335fa16 | 1244 | } |