Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions wcfsetup/install/files/global.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<?php

/**
* @author Marcel Werk
* @author Marcel Werk
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/

// include config
require_once(__DIR__ . '/app.config.inc.php');

// Make the frontend inaccessible until WCFSetup completes.
if (!PACKAGE_ID) {
// Deny access to the frontend until the WCFSetup has completed.
if (defined('PACKAGE_ID') && PACKAGE_ID === 0) {
\http_response_code(500);

exit;
}

// initiate wcf core
require_once(WCF_DIR . 'lib/system/WCF.class.php');
new wcf\system\WCF();
9 changes: 3 additions & 6 deletions wcfsetup/install/files/lib/data/package/Package.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,8 @@ private static function formatVersionForCompare(string $version): string

/**
* Writes the config.inc.php for an application.
*
* @param int $packageID
* @return void
*/
public static function writeConfigFile($packageID)
public static function writeConfigFile(int $packageID): void
{
$package = new self($packageID);
$packageDir = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR . $package->packageDir));
Expand All @@ -447,9 +444,9 @@ public static function writeConfigFile($packageID)
$content = "<?php\n";
$content .= "// {$package->package} (packageID {$packageID})\n";
$content .= "if (!defined('{$prefix}_DIR')) define('{$prefix}_DIR', __DIR__.'/');\n";
$content .= "if (!defined('PACKAGE_ID')) define('PACKAGE_ID', {$packageID});\n";

if ($packageID != 1) {
if ($packageID !== 1) {
$content .= "if (!defined('PACKAGE_ID')) define('PACKAGE_ID', {$packageID});\n";
$content .= "\n";
$content .= "// helper constants for applications\n";
$content .= "if (!defined('RELATIVE_{$prefix}_DIR')) define('RELATIVE_{$prefix}_DIR', '');\n";
Expand Down
23 changes: 22 additions & 1 deletion wcfsetup/install/files/lib/system/WCF.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ public function __construct()
// start initialization
$this->initDB();
$this->loadOptions();
$this->resolveActiveApplication();
$this->initSession();
$this->initLanguage();
$this->initTPL();
Expand Down Expand Up @@ -418,7 +419,7 @@ protected function loadOptions(): void
require($filename);

// check if option file is complete and writable
if (PACKAGE_ID) {
if (!defined('PACKAGE_ID') || \PACKAGE_ID !== 0) {
if (!\is_writable($filename)) {
FileUtil::makeWritable($filename);

Expand Down Expand Up @@ -519,6 +520,26 @@ protected function defineLegacyOptions(): void
\define('ATTACHMENT_IMAGE_AUTOSCALE_QUALITY', 80);
}

/**
* Resolve the active application and the path when using smart URL rewriting.
*
* @since 6.2
*/
protected function resolveActiveApplication(): void
{
if (!isset($_GET['__rewrittenPath']) || \defined('PACKAGE_ID')) {
if (!\defined('PACKAGE_ID')) {
\define('PACKAGE_ID', 1);
}

return;
}

ApplicationHandler::getInstance()->resolveActiveApplication($_GET['__rewrittenPath']);

unset($_GET['__rewrittenPath']);
}

/**
* Starts the session system.
*/
Expand Down
1 change: 1 addition & 0 deletions wcfsetup/install/files/lib/system/WCFACP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function __construct()
// start initialization
$this->initDB();
$this->loadOptions();
$this->resolveActiveApplication();
$this->initSession();
$this->initLanguage();
$this->initTPL();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@
final class ApplicationHandler extends SingletonFactory
{
/**
* application cache
* @var mixed[][]
* @var array{
* abbreviation: array<string, int>,
* application: array<int, Application>,
* rootApplication: ?int,
* sortedPaths: array<int, string>
* }
*/
protected $cache;
private array $cache;

/**
* list of page URLs
* @var string[]
*/
protected array $pageURLs = [];
private array $pageURLs;

/**
* Initializes cache.
Expand Down Expand Up @@ -185,7 +188,7 @@ public function getAbbreviations(): array
*/
public function isInternalURL(string $url): bool
{
if (empty($this->pageURLs)) {
if (!isset($this->pageURLs)) {
$internalHostnames = ArrayUtil::trim(\explode("\n", StringUtil::unifyNewlines(\INTERNAL_HOSTNAMES)));

$this->pageURLs = \array_unique([
Expand Down Expand Up @@ -219,9 +222,7 @@ public function isMultiDomainSetup(): bool
* @since 5.2
* @deprecated 5.5 - This function is a noop. The 'active' status is determined live.
*/
public function rebuildActiveApplication(): void
{
}
public function rebuildActiveApplication(): void {}

/**
* @since 6.0
Expand All @@ -231,6 +232,52 @@ public function getDomainName(): string
return $this->getApplicationByID(1)->domainName;
}

/**
* Resolve the active package id based on the rewritten URL.
*
* @since 6.2
*/
public function resolveActiveApplication(string $path): void
{
$rootApplication = $this->cache['rootApplication'];
\assert($rootApplication !== null);

$path = FileUtil::removeLeadingSlash($path);
$packageID = \array_find_key(
$this->cache['sortedPaths'],
static fn($prefix) => \str_starts_with($path, $prefix),
);

if ($packageID === null) {
\assert($this->cache['rootApplication'] !== null);
$packageID = $this->cache['rootApplication'];
} else {
$prefix = $this->cache['sortedPaths'][$packageID];
$path = \mb_substr($path, \mb_strlen($prefix));
}


RouteHandler::overridePathInfo($path);

if (!\defined('PACKAGE_ID')) {
\define('PACKAGE_ID', $packageID);

if ($packageID !== 1) {
$application = ApplicationHandler::getInstance()->getApplicationByID($packageID);
\assert($application !== null);

// Include the `app.config.inc.php` of the primary app.
$pathname = FileUtil::addTrailingSlash(
FileUtil::getRealPath(
\WCF_DIR . $application->getPackage()->packageDir
)
) . 'app.config.inc.php';

require_once $pathname;
}
}
}

/**
* Rebuilds cookie domain/path for all applications.
*/
Expand Down Expand Up @@ -263,7 +310,7 @@ public static function insertRealDatabaseTableNames(string $string, bool $skipCa
}

if ($skipCache) {
$sql = "SELECT package
$sql = "SELECT package
FROM wcf" . WCF_N . "_package
WHERE isApplication = ?";
$statement = WCF::getDB()->prepareUnmanaged($sql);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,22 @@
/**
* Caches applications.
*
* @author Alexander Ebert
* @author Alexander Ebert
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/
class ApplicationCacheBuilder extends AbstractCacheBuilder
final class ApplicationCacheBuilder extends AbstractCacheBuilder
{
/**
* @inheritDoc
*/
#[\Override]
public function rebuild(array $parameters)
{
$data = [
'abbreviation' => [],
'application' => [],
'rootApplication' => null,
'sortedPaths' => [],
];

// fetch applications
$sql = "SELECT *
FROM wcf" . WCF_N . "_application";
$statement = WCF::getDB()->prepareUnmanaged($sql);
Expand All @@ -34,9 +33,16 @@ public function rebuild(array $parameters)

foreach ($applications as $application) {
$data['application'][$application->packageID] = $application;
$data['sortedPaths'][$application->packageID] = $application->domainPath;
}

\uasort($data['sortedPaths'], static fn($a, $b) => \mb_strlen($b) - \mb_strlen($a));
$data['rootApplication'] = $this->getRootApplication($data['sortedPaths']);

if ($data['rootApplication'] !== null) {
$data['sortedPaths'] = $this->stripCommonPath($data['sortedPaths'], $data['rootApplication']);
}

// fetch abbreviations
$sql = "SELECT packageID, package
FROM wcf" . WCF_N . "_package
WHERE isApplication = ?";
Expand All @@ -49,4 +55,42 @@ public function rebuild(array $parameters)

return $data;
}

/**
* @param array<int, string> $sortedPaths
* @return array<int, string>
* @since 6.2
*/
private function stripCommonPath(array $sortedPaths, int $rootApplication): array
{
$length = \mb_strlen($sortedPaths[$rootApplication]);

return \array_map(
static fn($path) => \mb_substr($path, $length),
$sortedPaths
);
}

/**
* @param array<int, string> $sortedPaths
* @since 6.2
*/
private function getRootApplication(array $sortedPaths): ?int
{
// There are no applications during the setup.
if ($sortedPaths === []) {
return null;
}

$candidate = \array_key_last($sortedPaths);
$shortestPath = $sortedPaths[$candidate];

foreach ($sortedPaths as $path) {
if (!\str_starts_with($path, $shortestPath)) {
return null;
}
}

return $candidate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ protected function init()
*/
public function handle(string $application = 'wcf', bool $isACPRequest = false): void
{
// Override the application when using smart URL rewriting.
if ($application === 'wcf' && \PACKAGE_ID > 1) {
$app = ApplicationHandler::getInstance()->getApplicationByID(\PACKAGE_ID);
\assert($app !== null);
$application = $app->getAbbreviation();
}

try {
$this->isACPRequest = $isACPRequest;

Expand Down
10 changes: 10 additions & 0 deletions wcfsetup/install/files/lib/system/request/RouteHandler.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,14 @@ public static function getPathInfo(): string

return self::$pathInfo;
}

/**
* Overrides the path info as part of the smart URL rewriting feature.
*
* @since 6.2
*/
public static function overridePathInfo(string $pathInfo): void
{
self::$pathInfo = $pathInfo;
}
}