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