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 |
fe00e13d | 52 | define('WCF_VERSION', '5.3.0 Alpha 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 | |
d335fa16 AE |
391 | $filename = WCF_DIR.'options.inc.php'; |
392 | ||
393 | // create options file if doesn't exist | |
394 | if (!file_exists($filename) || filemtime($filename) <= 1) { | |
395 | OptionEditor::rebuild(); | |
396 | } | |
11beb0a0 | 397 | require($filename); |
b842faa1 AE |
398 | |
399 | // check if option file is complete and writable | |
400 | if (PACKAGE_ID) { | |
401 | if (!is_writable($filename)) { | |
402 | FileUtil::makeWritable($filename); | |
403 | ||
404 | if (!is_writable($filename)) { | |
405 | throw new SystemException("The option file '" . $filename . "' is not writable."); | |
406 | } | |
407 | } | |
408 | ||
409 | // check if a previous write operation was incomplete and force rebuilding | |
410 | if (!defined('WCF_OPTION_INC_PHP_SUCCESS')) { | |
411 | OptionEditor::rebuild(); | |
412 | ||
11beb0a0 | 413 | require($filename); |
b842faa1 | 414 | } |
34c330f8 AE |
415 | |
416 | if (ENABLE_DEBUG_MODE) { | |
417 | self::$dbObj->enableDebugMode(); | |
418 | } | |
b842faa1 | 419 | } |
d335fa16 AE |
420 | } |
421 | ||
422 | /** | |
423 | * Starts the session system. | |
424 | */ | |
425 | protected function initSession() { | |
f341086b | 426 | $factory = new SessionFactory(); |
d335fa16 AE |
427 | $factory->load(); |
428 | ||
f341086b | 429 | self::$sessionObj = SessionHandler::getInstance(); |
91082aee | 430 | self::$sessionObj->setHasValidCookie($factory->hasValidCookie()); |
d335fa16 AE |
431 | } |
432 | ||
433 | /** | |
434 | * Initialises the language engine. | |
435 | */ | |
436 | protected function initLanguage() { | |
437 | if (isset($_GET['l']) && !self::getUser()->userID) { | |
438 | self::getSession()->setLanguageID(intval($_GET['l'])); | |
439 | } | |
440 | ||
441 | // set mb settings | |
442 | mb_internal_encoding('UTF-8'); | |
443 | if (function_exists('mb_regex_encoding')) mb_regex_encoding('UTF-8'); | |
444 | mb_language('uni'); | |
445 | ||
446 | // get language | |
f341086b | 447 | self::$languageObj = LanguageFactory::getInstance()->getUserLanguage(self::getSession()->getLanguageID()); |
d335fa16 AE |
448 | } |
449 | ||
450 | /** | |
451 | * Initialises the template engine. | |
452 | */ | |
453 | protected function initTPL() { | |
f341086b | 454 | self::$tplObj = TemplateEngine::getInstance(); |
d335fa16 AE |
455 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); |
456 | $this->assignDefaultTemplateVariables(); | |
457 | ||
458 | $this->initStyle(); | |
459 | } | |
460 | ||
461 | /** | |
462 | * Initializes the user's style. | |
463 | */ | |
464 | protected function initStyle() { | |
465 | if (isset($_REQUEST['styleID'])) { | |
466 | self::getSession()->setStyleID(intval($_REQUEST['styleID'])); | |
467 | } | |
468 | ||
f341086b | 469 | $styleHandler = StyleHandler::getInstance(); |
d11a8c9e | 470 | $styleHandler->changeStyle(self::getSession()->getStyleID()); |
d335fa16 AE |
471 | } |
472 | ||
473 | /** | |
474 | * Executes the blacklist. | |
475 | */ | |
476 | protected function initBlacklist() { | |
fa75ccfb AE |
477 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); |
478 | ||
d335fa16 | 479 | if (defined('BLACKLIST_IP_ADDRESSES') && BLACKLIST_IP_ADDRESSES != '') { |
dc0015ab | 480 | if (!StringUtil::executeWordFilter(UserUtil::convertIPv6To4(self::getSession()->ipAddress), BLACKLIST_IP_ADDRESSES)) { |
fa75ccfb | 481 | if ($isAjax) { |
a1721f86 | 482 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
483 | } |
484 | else { | |
485 | throw new PermissionDeniedException(); | |
486 | } | |
dc0015ab AE |
487 | } |
488 | else if (!StringUtil::executeWordFilter(self::getSession()->ipAddress, BLACKLIST_IP_ADDRESSES)) { | |
fa75ccfb | 489 | if ($isAjax) { |
a1721f86 | 490 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS); |
fa75ccfb AE |
491 | } |
492 | else { | |
493 | throw new PermissionDeniedException(); | |
494 | } | |
d335fa16 AE |
495 | } |
496 | } | |
497 | if (defined('BLACKLIST_USER_AGENTS') && BLACKLIST_USER_AGENTS != '') { | |
498 | if (!StringUtil::executeWordFilter(self::getSession()->userAgent, BLACKLIST_USER_AGENTS)) { | |
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_HOSTNAMES') && BLACKLIST_HOSTNAMES != '') { | |
508 | if (!StringUtil::executeWordFilter(@gethostbyaddr(self::getSession()->ipAddress), BLACKLIST_HOSTNAMES)) { | |
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 | ||
518 | // handle banned users | |
d0ebece3 | 519 | if (self::getUser()->userID && self::getUser()->banned && !self::getUser()->hasOwnerAccess()) { |
fa75ccfb | 520 | if ($isAjax) { |
d335fa16 AE |
521 | throw new AJAXException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned'), AJAXException::INSUFFICIENT_PERMISSIONS); |
522 | } | |
523 | else { | |
ab84d9ca AE |
524 | self::$forceLogout = true; |
525 | ||
526 | // remove cookies | |
527 | if (isset($_COOKIE[COOKIE_PREFIX.'userID'])) { | |
528 | HeaderUtil::setCookie('userID', 0); | |
529 | } | |
530 | if (isset($_COOKIE[COOKIE_PREFIX.'password'])) { | |
531 | HeaderUtil::setCookie('password', ''); | |
532 | } | |
533 | ||
d335fa16 AE |
534 | throw new NamedUserException(self::getLanguage()->getDynamicVariable('wcf.user.error.isBanned')); |
535 | } | |
536 | } | |
537 | } | |
538 | ||
539 | /** | |
540 | * Initializes applications. | |
541 | */ | |
542 | protected function initApplications() { | |
543 | // step 1) load all applications | |
058cbd6a | 544 | $loadedApplications = []; |
d335fa16 AE |
545 | |
546 | // register WCF as application | |
39abe192 | 547 | self::$applications['wcf'] = ApplicationHandler::getInstance()->getApplicationByID(1); |
d335fa16 | 548 | |
39abe192 | 549 | if (!class_exists(WCFACP::class, false)) { |
b2b2c26b | 550 | static::getTPL()->assign('baseHref', self::$applications['wcf']->getPageURL()); |
39abe192 AE |
551 | } |
552 | ||
d335fa16 AE |
553 | // start main application |
554 | $application = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 AE |
555 | if ($application->packageID != 1) { |
556 | $loadedApplications[] = $this->loadApplication($application); | |
557 | ||
558 | // register primary application | |
559 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); | |
560 | self::$applications[$abbreviation] = $application; | |
561 | } | |
d335fa16 AE |
562 | |
563 | // start dependent applications | |
564 | $applications = ApplicationHandler::getInstance()->getDependentApplications(); | |
565 | foreach ($applications as $application) { | |
39abe192 AE |
566 | if ($application->packageID == 1) { |
567 | // ignore WCF | |
568 | continue; | |
569 | } | |
f4eafe37 AE |
570 | else if ($application->isTainted) { |
571 | // ignore apps flagged for uninstallation | |
572 | continue; | |
573 | } | |
39abe192 | 574 | |
d335fa16 AE |
575 | $loadedApplications[] = $this->loadApplication($application, true); |
576 | } | |
577 | ||
578 | // step 2) run each application | |
579 | if (!class_exists('wcf\system\WCFACP', false)) { | |
f341086b | 580 | /** @var IApplication $application */ |
d335fa16 AE |
581 | foreach ($loadedApplications as $application) { |
582 | $application->__run(); | |
583 | } | |
584 | ||
585 | // refresh the session 1 minute before it expires | |
63b9817b | 586 | self::getTPL()->assign('__sessionKeepAlive', SESSION_TIMEOUT - 60); |
d335fa16 AE |
587 | } |
588 | } | |
589 | ||
590 | /** | |
591 | * Loads an application. | |
592 | * | |
2b770bdd MS |
593 | * @param Application $application |
594 | * @param boolean $isDependentApplication | |
595 | * @return IApplication | |
596 | * @throws SystemException | |
d335fa16 AE |
597 | */ |
598 | protected function loadApplication(Application $application, $isDependentApplication = false) { | |
d335fa16 AE |
599 | $package = PackageCache::getInstance()->getPackage($application->packageID); |
600 | // package cache might be outdated | |
601 | if ($package === null) { | |
602 | $package = new Package($application->packageID); | |
603 | ||
604 | // package cache is outdated, discard cache | |
605 | if ($package->packageID) { | |
606 | PackageEditor::resetCache(); | |
607 | } | |
608 | else { | |
609 | // package id is invalid | |
610 | throw new SystemException("application identified by package id '".$application->packageID."' is unknown"); | |
611 | } | |
612 | } | |
e54191ea | 613 | |
d335fa16 AE |
614 | $abbreviation = ApplicationHandler::getInstance()->getAbbreviation($application->packageID); |
615 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
616 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
617 | ||
618 | $className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core'; | |
59162cf3 AE |
619 | |
620 | // class was not found, possibly the app was moved, but `packageDir` has not been adjusted | |
621 | if (!class_exists($className)) { | |
622 | // check if both the Core and the app are on the same domain | |
623 | $coreApp = ApplicationHandler::getInstance()->getApplicationByID(1); | |
624 | if ($coreApp->domainName === $application->domainName) { | |
625 | // resolve the relative path and use it to construct the autoload directory | |
626 | $relativePath = FileUtil::getRelativePath($coreApp->domainPath, $application->domainPath); | |
627 | if ($relativePath !== './') { | |
628 | $packageDir = FileUtil::getRealPath(WCF_DIR.$relativePath); | |
629 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
630 | ||
631 | if (class_exists($className)) { | |
632 | // the class can now be found, update the `packageDir` value | |
633 | (new PackageEditor($package))->update(['packageDir' => $relativePath]); | |
634 | } | |
635 | } | |
636 | } | |
637 | } | |
638 | ||
157054c9 | 639 | if (class_exists($className) && is_subclass_of($className, IApplication::class)) { |
d335fa16 AE |
640 | // include config file |
641 | $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE; | |
b842faa1 AE |
642 | if (!file_exists($configPath)) { |
643 | Package::writeConfigFile($package->packageID); | |
644 | } | |
645 | ||
d335fa16 AE |
646 | if (file_exists($configPath)) { |
647 | require_once($configPath); | |
648 | } | |
649 | else { | |
650 | throw new SystemException('Unable to load configuration for '.$package->package); | |
651 | } | |
652 | ||
653 | // register template path if not within ACP | |
654 | if (!class_exists('wcf\system\WCFACP', false)) { | |
655 | // add template path and abbreviation | |
b2b2c26b | 656 | static::getTPL()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 | 657 | } |
15621401 | 658 | EmailTemplateEngine::getInstance()->addApplication($abbreviation, $packageDir . 'templates/'); |
d335fa16 AE |
659 | |
660 | // init application and assign it as template variable | |
058cbd6a | 661 | self::$applicationObjects[$application->packageID] = call_user_func([$className, 'getInstance']); |
b2b2c26b | 662 | static::getTPL()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
15621401 | 663 | EmailTemplateEngine::getInstance()->assign('__'.$abbreviation, self::$applicationObjects[$application->packageID]); |
d335fa16 AE |
664 | } |
665 | else { | |
666 | unset(self::$autoloadDirectories[$abbreviation]); | |
157054c9 | 667 | throw new SystemException("Unable to run '".$package->package."', '".$className."' is missing or does not implement '".IApplication::class."'."); |
d335fa16 AE |
668 | } |
669 | ||
670 | // register template path in ACP | |
671 | if (class_exists('wcf\system\WCFACP', false)) { | |
b2b2c26b | 672 | static::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); |
d335fa16 AE |
673 | } |
674 | else if (!$isDependentApplication) { | |
675 | // assign base tag | |
b2b2c26b | 676 | static::getTPL()->assign('baseHref', $application->getPageURL()); |
d335fa16 AE |
677 | } |
678 | ||
679 | // register application | |
680 | self::$applications[$abbreviation] = $application; | |
681 | ||
682 | return self::$applicationObjects[$application->packageID]; | |
683 | } | |
684 | ||
685 | /** | |
686 | * Returns the corresponding application object. Does not support the 'wcf' pseudo application. | |
687 | * | |
4e25add7 MS |
688 | * @param Application $application |
689 | * @return IApplication | |
d335fa16 AE |
690 | */ |
691 | public static function getApplicationObject(Application $application) { | |
692 | if (isset(self::$applicationObjects[$application->packageID])) { | |
693 | return self::$applicationObjects[$application->packageID]; | |
694 | } | |
695 | ||
696 | return null; | |
697 | } | |
698 | ||
dbf41d8a AE |
699 | /** |
700 | * Returns the invoked application. | |
701 | * | |
702 | * @return Application | |
8e152e07 | 703 | * @since 3.1 |
dbf41d8a AE |
704 | */ |
705 | public static function getActiveApplication() { | |
706 | return ApplicationHandler::getInstance()->getActiveApplication(); | |
707 | } | |
708 | ||
d335fa16 AE |
709 | /** |
710 | * Loads an application on runtime, do not use this outside the package installation. | |
711 | * | |
712 | * @param integer $packageID | |
713 | */ | |
714 | public static function loadRuntimeApplication($packageID) { | |
715 | $package = new Package($packageID); | |
716 | $application = new Application($packageID); | |
717 | ||
718 | $abbreviation = Package::getAbbreviation($package->package); | |
719 | $packageDir = FileUtil::getRealPath(WCF_DIR.$package->packageDir); | |
720 | self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/'; | |
721 | self::$applications[$abbreviation] = $application; | |
722 | self::getTPL()->addApplication($abbreviation, $packageDir . 'acp/templates/'); | |
723 | } | |
724 | ||
725 | /** | |
726 | * Initializes core object cache. | |
727 | */ | |
728 | protected function initCoreObjects() { | |
729 | // ignore core objects if installing WCF | |
730 | if (PACKAGE_ID == 0) { | |
731 | return; | |
732 | } | |
733 | ||
734 | self::$coreObjectCache = CoreObjectCacheBuilder::getInstance()->getData(); | |
735 | } | |
736 | ||
737 | /** | |
738 | * Assigns some default variables to the template engine. | |
739 | */ | |
740 | protected function assignDefaultTemplateVariables() { | |
50b7b362 MS |
741 | $wcf = $this; |
742 | ||
743 | if (ENABLE_ENTERPRISE_MODE) { | |
744 | $wcf = new TemplateScriptingCore($wcf); | |
745 | } | |
746 | ||
058cbd6a MS |
747 | self::getTPL()->registerPrefilter(['event', 'hascontent', 'lang']); |
748 | self::getTPL()->assign([ | |
50b7b362 | 749 | '__wcf' => $wcf, |
e71525e4 | 750 | '__wcfVersion' => LAST_UPDATE_TIME // @deprecated 2.1, use LAST_UPDATE_TIME directly |
058cbd6a | 751 | ]); |
54de7a5f TD |
752 | |
753 | $isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); | |
754 | // Execute background queue in this request, if it was requested and AJAX isn't used. | |
755 | if (!$isAjax) { | |
756 | if (self::getSession()->getVar('forceBackgroundQueuePerform')) { | |
757 | self::getTPL()->assign([ | |
758 | 'forceBackgroundQueuePerform' => true | |
759 | ]); | |
760 | ||
761 | self::getSession()->unregister('forceBackgroundQueuePerform'); | |
762 | } | |
763 | } | |
764 | ||
15621401 TD |
765 | EmailTemplateEngine::getInstance()->registerPrefilter(['event', 'hascontent', 'lang']); |
766 | EmailTemplateEngine::getInstance()->assign([ | |
50b7b362 | 767 | '__wcf' => $wcf |
15621401 | 768 | ]); |
d335fa16 AE |
769 | } |
770 | ||
771 | /** | |
772 | * Wrapper for the getter methods of this class. | |
773 | * | |
774 | * @param string $name | |
775 | * @return mixed value | |
2b770bdd | 776 | * @throws SystemException |
d335fa16 AE |
777 | */ |
778 | public function __get($name) { | |
779 | $method = 'get'.ucfirst($name); | |
780 | if (method_exists($this, $method)) { | |
781 | return $this->$method(); | |
782 | } | |
783 | ||
784 | throw new SystemException("method '".$method."' does not exist in class WCF"); | |
785 | } | |
786 | ||
487db634 MW |
787 | /** |
788 | * Returns true if current application (WCF) is treated as active and was invoked directly. | |
789 | * | |
790 | * @return boolean | |
791 | */ | |
792 | public function isActiveApplication() { | |
793 | return (ApplicationHandler::getInstance()->getActiveApplication()->packageID == 1); | |
794 | } | |
795 | ||
d335fa16 AE |
796 | /** |
797 | * Changes the active language. | |
798 | * | |
799 | * @param integer $languageID | |
800 | */ | |
801 | public static final function setLanguage($languageID) { | |
5b8eae2c MW |
802 | if (!$languageID || LanguageFactory::getInstance()->getLanguage($languageID) === null) { |
803 | $languageID = LanguageFactory::getInstance()->getDefaultLanguageID(); | |
804 | } | |
2d90d60f | 805 | |
d335fa16 | 806 | self::$languageObj = LanguageFactory::getInstance()->getLanguage($languageID); |
b372bd53 AE |
807 | |
808 | // the template engine may not be available yet, usually happens when | |
809 | // changing the user (and thus the language id) during session init | |
810 | if (self::$tplObj !== null) { | |
811 | self::getTPL()->setLanguageID(self::getLanguage()->languageID); | |
812 | EmailTemplateEngine::getInstance()->setLanguageID(self::getLanguage()->languageID); | |
813 | } | |
d335fa16 AE |
814 | } |
815 | ||
816 | /** | |
817 | * Includes the required util or exception classes automatically. | |
818 | * | |
819 | * @param string $className | |
820 | * @see spl_autoload_register() | |
821 | */ | |
822 | public static final function autoload($className) { | |
823 | $namespaces = explode('\\', $className); | |
824 | if (count($namespaces) > 1) { | |
825 | $applicationPrefix = array_shift($namespaces); | |
826 | if ($applicationPrefix === '') { | |
827 | $applicationPrefix = array_shift($namespaces); | |
828 | } | |
829 | if (isset(self::$autoloadDirectories[$applicationPrefix])) { | |
830 | $classPath = self::$autoloadDirectories[$applicationPrefix] . implode('/', $namespaces) . '.class.php'; | |
6f609fec AE |
831 | |
832 | // PHP will implicitly check if the file exists when including it, which means that we can save a | |
833 | // redundant syscall/fs access by not checking for existence ourselves. Do not use require_once()! | |
834 | @include_once($classPath); | |
d335fa16 AE |
835 | } |
836 | } | |
837 | } | |
838 | ||
839 | /** | |
0fcfe5f6 | 840 | * @inheritDoc |
d335fa16 AE |
841 | */ |
842 | public final function __call($name, array $arguments) { | |
843 | // bug fix to avoid php crash, see http://bugs.php.net/bug.php?id=55020 | |
844 | if (!method_exists($this, $name)) { | |
845 | return self::__callStatic($name, $arguments); | |
846 | } | |
847 | ||
9b31ab91 | 848 | throw new \BadMethodCallException("Call to undefined method WCF::{$name}()."); |
d335fa16 AE |
849 | } |
850 | ||
851 | /** | |
852 | * Returns dynamically loaded core objects. | |
853 | * | |
854 | * @param string $name | |
855 | * @param array $arguments | |
71952a87 | 856 | * @return object |
2b770bdd | 857 | * @throws SystemException |
d335fa16 AE |
858 | */ |
859 | public static final function __callStatic($name, array $arguments) { | |
860 | $className = preg_replace('~^get~', '', $name); | |
861 | ||
862 | if (isset(self::$coreObject[$className])) { | |
863 | return self::$coreObject[$className]; | |
864 | } | |
865 | ||
866 | $objectName = self::getCoreObject($className); | |
867 | if ($objectName === null) { | |
868 | throw new SystemException("Core object '".$className."' is unknown."); | |
869 | } | |
870 | ||
871 | if (class_exists($objectName)) { | |
63b9817b | 872 | if (!is_subclass_of($objectName, SingletonFactory::class)) { |
7b9ff46b | 873 | throw new ParentClassException($objectName, SingletonFactory::class); |
d335fa16 AE |
874 | } |
875 | ||
058cbd6a | 876 | self::$coreObject[$className] = call_user_func([$objectName, 'getInstance']); |
d335fa16 AE |
877 | return self::$coreObject[$className]; |
878 | } | |
879 | } | |
880 | ||
881 | /** | |
882 | * Searches for cached core object definition. | |
883 | * | |
884 | * @param string $className | |
885 | * @return string | |
886 | */ | |
887 | protected static final function getCoreObject($className) { | |
888 | if (isset(self::$coreObjectCache[$className])) { | |
889 | return self::$coreObjectCache[$className]; | |
890 | } | |
891 | ||
892 | return null; | |
893 | } | |
894 | ||
895 | /** | |
896 | * Returns true if the debug mode is enabled, otherwise false. | |
897 | * | |
898 | * @param boolean $ignoreACP | |
899 | * @return boolean | |
900 | */ | |
901 | public static function debugModeIsEnabled($ignoreACP = false) { | |
902 | // ACP override | |
903 | if (!$ignoreACP && self::$overrideDebugMode) { | |
904 | return true; | |
905 | } | |
906 | else if (defined('ENABLE_DEBUG_MODE') && ENABLE_DEBUG_MODE) { | |
907 | return true; | |
908 | } | |
909 | ||
910 | return false; | |
911 | } | |
912 | ||
913 | /** | |
914 | * Returns true if benchmarking is enabled, otherwise false. | |
915 | * | |
916 | * @return boolean | |
917 | */ | |
918 | public static function benchmarkIsEnabled() { | |
919 | // benchmarking is enabled by default | |
920 | if (!defined('ENABLE_BENCHMARK') || ENABLE_BENCHMARK) return true; | |
921 | return false; | |
922 | } | |
923 | ||
924 | /** | |
925 | * Returns domain path for given application. | |
926 | * | |
927 | * @param string $abbreviation | |
928 | * @return string | |
929 | */ | |
930 | public static function getPath($abbreviation = 'wcf') { | |
931 | // workaround during WCFSetup | |
932 | if (!PACKAGE_ID) { | |
933 | return '../'; | |
934 | } | |
935 | ||
936 | if (!isset(self::$applications[$abbreviation])) { | |
937 | $abbreviation = 'wcf'; | |
938 | } | |
939 | ||
940 | return self::$applications[$abbreviation]->getPageURL(); | |
941 | } | |
942 | ||
5d1c96bc AE |
943 | /** |
944 | * Returns the domain path for the currently active application, | |
945 | * used to avoid CORS requests. | |
946 | * | |
947 | * @return string | |
948 | */ | |
949 | public static function getActivePath() { | |
950 | if (!PACKAGE_ID) { | |
951 | return self::getPath(); | |
952 | } | |
953 | ||
12a7c542 AE |
954 | // We cannot rely on the ApplicationHandler's `getActiveApplication()` because |
955 | // it uses the requested controller to determine the namespace. However, starting | |
956 | // with WoltLab Suite 5.2, system pages can be virtually assigned to a different | |
957 | // app, resolving against the target app without changing the namespace. | |
958 | return self::getPath(ApplicationHandler::getInstance()->getAbbreviation(PACKAGE_ID)); | |
5d1c96bc AE |
959 | } |
960 | ||
d335fa16 AE |
961 | /** |
962 | * Returns a fully qualified anchor for current page. | |
963 | * | |
964 | * @param string $fragment | |
965 | * @return string | |
966 | */ | |
967 | public function getAnchor($fragment) { | |
694f6545 | 968 | return StringUtil::encodeHTML(self::getRequestURI() . '#' . $fragment); |
d335fa16 AE |
969 | } |
970 | ||
12a2ff01 AE |
971 | /** |
972 | * Returns the currently active page or null if unknown. | |
973 | * | |
b212ba66 | 974 | * @return Page|null |
12a2ff01 AE |
975 | */ |
976 | public static function getActivePage() { | |
a039ebd2 AE |
977 | if (self::getActiveRequest() === null) { |
978 | return null; | |
979 | } | |
980 | ||
5346b183 AE |
981 | if (self::getActiveRequest()->getClassName() === CmsPage::class) { |
982 | $metaData = self::getActiveRequest()->getMetaData(); | |
b212ba66 MS |
983 | if (isset($metaData['cms'])) { |
984 | return PageCache::getInstance()->getPage($metaData['cms']['pageID']); | |
985 | } | |
986 | ||
987 | return null; | |
5346b183 AE |
988 | } |
989 | ||
12a2ff01 AE |
990 | return PageCache::getInstance()->getPageByController(self::getActiveRequest()->getClassName()); |
991 | } | |
992 | ||
993 | /** | |
994 | * Returns the currently active request. | |
995 | * | |
996 | * @return Request | |
997 | */ | |
998 | public static function getActiveRequest() { | |
999 | return RequestHandler::getInstance()->getActiveRequest(); | |
1000 | } | |
1001 | ||
d335fa16 AE |
1002 | /** |
1003 | * Returns the URI of the current page. | |
1004 | * | |
1005 | * @return string | |
1006 | */ | |
1007 | public static function getRequestURI() { | |
c68f6161 | 1008 | return preg_replace('~^(https?://[^/]+)(?:/.*)?$~', '$1', self::getTPL()->get('baseHref')) . $_SERVER['REQUEST_URI']; |
d335fa16 AE |
1009 | } |
1010 | ||
1011 | /** | |
1012 | * Resets Zend Opcache cache if installed and enabled. | |
1013 | * | |
1014 | * @param string $script | |
1015 | */ | |
1016 | public static function resetZendOpcache($script = '') { | |
1017 | if (self::$zendOpcacheEnabled === null) { | |
1018 | self::$zendOpcacheEnabled = false; | |
1019 | ||
1020 | if (extension_loaded('Zend Opcache') && @ini_get('opcache.enable')) { | |
1021 | self::$zendOpcacheEnabled = true; | |
1022 | } | |
1023 | ||
1024 | } | |
1025 | ||
1026 | if (self::$zendOpcacheEnabled) { | |
1027 | if (empty($script)) { | |
d631fa86 | 1028 | \opcache_reset(); |
d335fa16 AE |
1029 | } |
1030 | else { | |
d631fa86 | 1031 | \opcache_invalidate($script, true); |
d335fa16 AE |
1032 | } |
1033 | } | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Returns style handler. | |
1038 | * | |
4e25add7 | 1039 | * @return StyleHandler |
d335fa16 AE |
1040 | */ |
1041 | public function getStyleHandler() { | |
1042 | return StyleHandler::getInstance(); | |
1043 | } | |
1044 | ||
55b402a0 MW |
1045 | /** |
1046 | * Returns box handler. | |
1047 | * | |
1048 | * @return BoxHandler | |
e71525e4 | 1049 | * @since 3.0 |
55b402a0 MW |
1050 | */ |
1051 | public function getBoxHandler() { | |
1052 | return BoxHandler::getInstance(); | |
1053 | } | |
1054 | ||
d335fa16 AE |
1055 | /** |
1056 | * Returns number of available updates. | |
1057 | * | |
1058 | * @return integer | |
1059 | */ | |
1060 | public function getAvailableUpdates() { | |
1061 | $data = PackageUpdateCacheBuilder::getInstance()->getData(); | |
1062 | return $data['updates']; | |
1063 | } | |
1064 | ||
1065 | /** | |
1066 | * Returns a 8 character prefix for editor autosave. | |
1067 | * | |
1068 | * @return string | |
1069 | */ | |
1070 | public function getAutosavePrefix() { | |
1071 | return substr(sha1(preg_replace('~^https~', 'http', self::getPath())), 0, 8); | |
1072 | } | |
1073 | ||
1074 | /** | |
1075 | * Returns the favicon URL or a base64 encoded image. | |
1076 | * | |
1077 | * @return string | |
1078 | */ | |
1079 | public function getFavicon() { | |
1080 | $activeApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
39abe192 | 1081 | $wcf = ApplicationHandler::getInstance()->getWCF(); |
aef117b5 | 1082 | $favicon = StyleHandler::getInstance()->getStyle()->getRelativeFavicon(); |
d335fa16 | 1083 | |
39abe192 | 1084 | if ($activeApplication->domainName !== $wcf->domainName) { |
aef117b5 AE |
1085 | if (file_exists(WCF_DIR.$favicon)) { |
1086 | $favicon = file_get_contents(WCF_DIR.$favicon); | |
d335fa16 AE |
1087 | |
1088 | return 'data:image/x-icon;base64,' . base64_encode($favicon); | |
1089 | } | |
1090 | } | |
1091 | ||
aef117b5 | 1092 | return self::getPath() . $favicon; |
d335fa16 AE |
1093 | } |
1094 | ||
6e4aa12f AE |
1095 | /** |
1096 | * Returns true if the desktop notifications should be enabled. | |
1097 | * | |
1098 | * @return boolean | |
1099 | */ | |
1100 | public function useDesktopNotifications() { | |
1101 | if (!ENABLE_DESKTOP_NOTIFICATIONS) { | |
1102 | return false; | |
1103 | } | |
1104 | else if (ApplicationHandler::getInstance()->isMultiDomainSetup()) { | |
1105 | $application = ApplicationHandler::getInstance()->getApplicationByID(DESKTOP_NOTIFICATION_PACKAGE_ID); | |
1106 | // mismatch, default to Core | |
1107 | if ($application === null) $application = ApplicationHandler::getInstance()->getApplicationByID(1); | |
1108 | ||
1109 | $currentApplication = ApplicationHandler::getInstance()->getActiveApplication(); | |
1110 | if ($currentApplication->domainName != $application->domainName) { | |
1111 | // different domain | |
1112 | return false; | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | return true; | |
1117 | } | |
1118 | ||
a80873d5 AE |
1119 | /** |
1120 | * Returns true if currently active request represents the landing page. | |
1121 | * | |
8ff2cd79 | 1122 | * @return boolean |
a80873d5 | 1123 | */ |
0fdd7f96 | 1124 | public static function isLandingPage() { |
a039ebd2 AE |
1125 | if (self::getActiveRequest() === null) { |
1126 | return false; | |
1127 | } | |
1128 | ||
1129 | return self::getActiveRequest()->isLandingPage(); | |
a80873d5 AE |
1130 | } |
1131 | ||
89484ba0 AE |
1132 | /** |
1133 | * Returns true if the given API version is currently supported. | |
1134 | * | |
1135 | * @param integer $apiVersion | |
1136 | * @return boolean | |
0e8af3ef | 1137 | * @deprecated 5.2 |
89484ba0 AE |
1138 | */ |
1139 | public static function isSupportedApiVersion($apiVersion) { | |
1140 | return ($apiVersion == WSC_API_VERSION) || in_array($apiVersion, self::$supportedLegacyApiVersions); | |
1141 | } | |
1142 | ||
1143 | /** | |
1144 | * Returns the list of supported legacy API versions. | |
1145 | * | |
1146 | * @return integer[] | |
0e8af3ef | 1147 | * @deprecated 5.2 |
89484ba0 AE |
1148 | */ |
1149 | public static function getSupportedLegacyApiVersions() { | |
1150 | return self::$supportedLegacyApiVersions; | |
1151 | } | |
1152 | ||
d335fa16 AE |
1153 | /** |
1154 | * Initialises the cronjobs. | |
1155 | */ | |
1156 | protected function initCronjobs() { | |
1157 | if (PACKAGE_ID) { | |
63b9817b | 1158 | self::getTPL()->assign('executeCronjobs', CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE); |
d335fa16 AE |
1159 | } |
1160 | } | |
66d9b64b MS |
1161 | |
1162 | /** | |
1163 | * Checks recursively that the most important system files of `com.woltlab.wcf` are writable. | |
1164 | * | |
1165 | * @throws \RuntimeException if any relevant file or directory is not writable | |
1166 | */ | |
1167 | public static function checkWritability() { | |
1168 | $nonWritablePaths = []; | |
1169 | ||
1170 | $nonRecursiveDirectories = [ | |
1171 | '', | |
1172 | 'acp/', | |
1173 | 'tmp/' | |
1174 | ]; | |
1175 | foreach ($nonRecursiveDirectories as $directory) { | |
1176 | $path = WCF_DIR . $directory; | |
1177 | if ($path === 'tmp/' && !is_dir($path)) { | |
1178 | continue; | |
1179 | } | |
1180 | ||
1181 | if (!is_writable($path)) { | |
7f754c71 | 1182 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path); |
66d9b64b MS |
1183 | continue; |
1184 | } | |
1185 | ||
1186 | DirectoryUtil::getInstance($path, false)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { | |
1187 | if ($fileInfo instanceof \DirectoryIterator) { | |
1188 | if (!is_writable($fileInfo->getPath())) { | |
7f754c71 | 1189 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()); |
66d9b64b MS |
1190 | } |
1191 | } | |
1192 | else if (!is_writable($fileInfo->getRealPath())) { | |
7f754c71 | 1193 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()) . $fileInfo->getFilename(); |
66d9b64b MS |
1194 | } |
1195 | }); | |
1196 | } | |
1197 | ||
1198 | $recursiveDirectories = [ | |
1199 | 'acp/js/', | |
1200 | 'acp/style/', | |
1201 | 'acp/templates/', | |
1202 | 'acp/uninstall/', | |
1203 | 'js/', | |
1204 | 'lib/', | |
1205 | 'log/', | |
1206 | 'style/', | |
1207 | 'templates/' | |
1208 | ]; | |
1209 | foreach ($recursiveDirectories as $directory) { | |
1210 | $path = WCF_DIR . $directory; | |
1211 | ||
1212 | if (!is_writable($path)) { | |
7f754c71 | 1213 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $path); |
66d9b64b MS |
1214 | continue; |
1215 | } | |
1216 | ||
1217 | DirectoryUtil::getInstance($path)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { | |
1218 | if ($fileInfo instanceof \DirectoryIterator) { | |
1219 | if (!is_writable($fileInfo->getPath())) { | |
7f754c71 | 1220 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()); |
66d9b64b MS |
1221 | } |
1222 | } | |
1223 | else if (!is_writable($fileInfo->getRealPath())) { | |
7f754c71 | 1224 | $nonWritablePaths[] = FileUtil::getRelativePath($_SERVER['DOCUMENT_ROOT'], $fileInfo->getPath()) . $fileInfo->getFilename(); |
66d9b64b MS |
1225 | } |
1226 | }); | |
1227 | } | |
1228 | ||
1229 | if (!empty($nonWritablePaths)) { | |
1230 | $maxPaths = 10; | |
cf23b193 | 1231 | throw new \RuntimeException('The following paths are not writable: ' . implode(',', array_slice($nonWritablePaths, 0, $maxPaths)) . (count($nonWritablePaths) > $maxPaths ? ',' . StringUtil::HELLIP : '')); |
66d9b64b MS |
1232 | } |
1233 | } | |
d335fa16 | 1234 | } |