Merge branch 'master' into next
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / WCF.class.php
index f292150e330de0f9441b9d693f1bcb2e3e7b4339..3ab7dc1ac55791383949331b1eece7c1f01bcd72 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 namespace wcf\system;
 use wcf\data\application\Application;
 use wcf\data\option\OptionEditor;
@@ -25,6 +26,7 @@ use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\SystemException;
 use wcf\system\language\LanguageFactory;
 use wcf\system\package\PackageInstallationDispatcher;
+use wcf\system\registry\RegistryHandler;
 use wcf\system\request\Request;
 use wcf\system\request\RequestHandler;
 use wcf\system\session\SessionFactory;
@@ -47,7 +49,10 @@ if (!@ini_get('date.timezone')) {
 }
 
 // define current woltlab suite version
-define('WCF_VERSION', '3.0.13');
+define('WCF_VERSION', '3.1.2 pl 2');
+
+// define current API version
+define('WSC_API_VERSION', 2018);
 
 // define current unix timestamp
 define('TIME_NOW', time());
@@ -63,11 +68,17 @@ if (!defined('NO_IMPORTS')) {
  * It holds the database connection, access to template and language engine.
  * 
  * @author     Marcel Werk
- * @copyright  2001-2017 WoltLab GmbH
+ * @copyright  2001-2018 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\System
  */
 class WCF {
+       /**
+        * list of supported legacy API versions
+        * @var integer[]
+        */
+       private static $supportedLegacyApiVersions = [2017];
+       
        /**
         * list of currently loaded applications
         * @var Application[]
@@ -196,7 +207,8 @@ class WCF {
                                }
                        }
                        
-                       // execute shutdown actions of user storage handler
+                       // execute shutdown actions of storage handlers
+                       RegistryHandler::getInstance()->shutdown();
                        UserStorageHandler::getInstance()->shutdown();
                }
                catch (\Exception $exception) {
@@ -255,24 +267,46 @@ class WCF {
         * @param       \Exception      $e
         */
        public static final function handleException($e) {
+               // backwards compatibility
+               if ($e instanceof IPrintableException) {
+                       $e->show();
+                       exit;
+               }
+               
                if (ob_get_level()) {
-                       // discard any output generated before the exception occured, prevents exception
+                       // discard any output generated before the exception occurred, prevents exception
                        // being hidden inside HTML elements and therefore not visible in browser output
                        // 
                        // ob_get_level() can return values > 1, if the PHP setting `output_buffering` is on
                        while (ob_get_level()) ob_end_clean();
                        
-                       // `identity` is the default "encoding" and basically means that the client
-                       // must treat the content as if the header did not appear in first place, this
-                       // also overrules the gzip header if present
-                       @header('Content-Encoding: identity');
-                       HeaderUtil::exceptionDisableGzip();
-               }
-               
-               // backwards compatibility
-               if ($e instanceof IPrintableException) {
-                       $e->show();
-                       exit;
+                       // Some webservers are broken and will apply gzip encoding at all cost, but they fail
+                       // to set a proper `Content-Encoding` HTTP header and mess things up even more.
+                       // Especially the `identity` value appears to be unrecognized by some of them, hence
+                       // we'll just gzip the output of the exception to prevent them from tampering.
+                       // This part is copied from `HeaderUtil` in order to isolate the exception handler!
+                       if (defined('HTTP_ENABLE_GZIP') && HTTP_ENABLE_GZIP && !defined('HTTP_DISABLE_GZIP')) {
+                               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) {
+                                       if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false) {
+                                               @header('Content-Encoding: x-gzip');
+                                       }
+                                       else {
+                                               @header('Content-Encoding: gzip');
+                                       }
+                                       
+                                       ob_start(function($output) {
+                                               $size = strlen($output);
+                                               $crc = crc32($output);
+                                               
+                                               $newOutput = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff";
+                                               $newOutput .= substr(gzcompress($output, 1), 2, -4);
+                                               $newOutput .= pack('V', $crc);
+                                               $newOutput .= pack('V', $size);
+                                               
+                                               return $newOutput;
+                                       });
+                               }
+                       }
                }
                
                @header('HTTP/1.1 503 Service Unavailable');
@@ -287,14 +321,6 @@ class WCF {
                        echo "\n\nwas handled.</pre>";
                        exit;
                }
-               catch (\Exception $e2) {
-                       echo "<pre>An Exception was thrown while handling an Exception:\n\n";
-                       echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e2);
-                       echo "\n\nwas thrown while:\n\n";
-                       echo preg_replace('/Database->__construct\(.*\)/', 'Database->__construct(...)', $e);
-                       echo "\n\nwas handled.</pre>";
-                       exit;
-               }
        }
        
        /**
@@ -554,6 +580,26 @@ class WCF {
                self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
                
                $className = $abbreviation.'\system\\'.strtoupper($abbreviation).'Core';
+               
+               // class was not found, possibly the app was moved, but `packageDir` has not been adjusted
+               if (!class_exists($className)) {
+                       // check if both the Core and the app are on the same domain
+                       $coreApp = ApplicationHandler::getInstance()->getApplicationByID(1);
+                       if ($coreApp->domainName === $application->domainName) {
+                               // resolve the relative path and use it to construct the autoload directory
+                               $relativePath = FileUtil::getRelativePath($coreApp->domainPath, $application->domainPath);
+                               if ($relativePath !== './') {
+                                       $packageDir = FileUtil::getRealPath(WCF_DIR.$relativePath);
+                                       self::$autoloadDirectories[$abbreviation] = $packageDir . 'lib/';
+                                       
+                                       if (class_exists($className)) {
+                                               // the class can now be found, update the `packageDir` value
+                                               (new PackageEditor($package))->update(['packageDir' => $relativePath]);
+                                       }
+                               }
+                       }
+               }
+               
                if (class_exists($className) && is_subclass_of($className, IApplication::class)) {
                        // include config file
                        $configPath = $packageDir . PackageInstallationDispatcher::CONFIG_FILE;
@@ -614,6 +660,16 @@ class WCF {
                return null;
        }
        
+       /**
+        * Returns the invoked application.
+        * 
+        * @return      Application
+        * @since       3.1
+        */
+       public static function getActiveApplication() {
+               return ApplicationHandler::getInstance()->getActiveApplication();
+       }
+       
        /**
         * Loads an application on runtime, do not use this outside the package installation.
         * 
@@ -976,16 +1032,41 @@ class WCF {
        public function getFavicon() {
                $activeApplication = ApplicationHandler::getInstance()->getActiveApplication();
                $wcf = ApplicationHandler::getInstance()->getWCF();
+               $favicon = StyleHandler::getInstance()->getStyle()->getRelativeFavicon();
                
                if ($activeApplication->domainName !== $wcf->domainName) {
-                       if (file_exists(WCF_DIR.'images/favicon.ico')) {
-                               $favicon = file_get_contents(WCF_DIR.'images/favicon.ico');
+                       if (file_exists(WCF_DIR.$favicon)) {
+                               $favicon = file_get_contents(WCF_DIR.$favicon);
                                
                                return 'data:image/x-icon;base64,' . base64_encode($favicon);
                        }
                }
                
-               return self::getPath() . 'images/favicon.ico';
+               return self::getPath() . $favicon;
+       }
+       
+       /**
+        * Returns true if the desktop notifications should be enabled.
+        * 
+        * @return      boolean
+        */
+       public function useDesktopNotifications() {
+               if (!ENABLE_DESKTOP_NOTIFICATIONS) {
+                       return false;
+               }
+               else if (ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+                       $application = ApplicationHandler::getInstance()->getApplicationByID(DESKTOP_NOTIFICATION_PACKAGE_ID);
+                       // mismatch, default to Core
+                       if ($application === null) $application = ApplicationHandler::getInstance()->getApplicationByID(1);
+                       
+                       $currentApplication = ApplicationHandler::getInstance()->getActiveApplication();
+                       if ($currentApplication->domainName != $application->domainName) {
+                               // different domain
+                               return false;
+                       }
+               }
+               
+               return true;
        }
        
        /**
@@ -1001,6 +1082,25 @@ class WCF {
                return self::getActiveRequest()->isLandingPage();
        }
        
+       /**
+        * Returns true if the given API version is currently supported.
+        * 
+        * @param       integer         $apiVersion
+        * @return      boolean
+        */
+       public static function isSupportedApiVersion($apiVersion) {
+               return ($apiVersion == WSC_API_VERSION) || in_array($apiVersion, self::$supportedLegacyApiVersions);
+       }
+       
+       /**
+        * Returns the list of supported legacy API versions.
+        * 
+        * @return      integer[]
+        */
+       public static function getSupportedLegacyApiVersions() {
+               return self::$supportedLegacyApiVersions;
+       }
+       
        /**
         * Initialises the cronjobs.
         */