3 namespace wcf\system\request
;
5 use wcf\system\application\ApplicationHandler
;
6 use wcf\system\box\BoxHandler
;
7 use wcf\system\exception\AJAXException
;
8 use wcf\system\exception\IllegalLinkException
;
9 use wcf\system\exception\NamedUserException
;
10 use wcf\system\exception\SystemException
;
11 use wcf\system\notice\NoticeHandler
;
12 use wcf\system\SingletonFactory
;
14 use wcf\util\FileUtil
;
15 use wcf\util\HeaderUtil
;
18 * Handles http requests.
21 * @copyright 2001-2020 WoltLab GmbH
22 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
23 * @package WoltLabSuite\Core\System\Request
25 class RequestHandler
extends SingletonFactory
28 * active request object
31 protected $activeRequest;
34 * true, if current domain mismatch any known domain
37 protected $inRescueMode = false;
40 * indicates if the request is an acp request
43 protected $isACPRequest = false;
48 protected function init()
50 $this->isACPRequest
= \
class_exists('wcf\system\WCFACP', false);
54 * Handles a http request.
56 * @param string $application
57 * @param bool $isACPRequest
58 * @throws AJAXException
59 * @throws IllegalLinkException
60 * @throws SystemException
62 public function handle($application = 'wcf', $isACPRequest = false)
65 $this->isACPRequest
= $isACPRequest;
67 if (!RouteHandler
::getInstance()->matches()) {
68 if (ENABLE_DEBUG_MODE
) {
69 throw new SystemException("Cannot handle request, no valid route provided.");
71 throw new IllegalLinkException();
76 $this->buildRequest($application);
78 // enforce that certain ACP pages are not available for non-owners in enterprise mode
81 && ENABLE_ENTERPRISE_MODE
82 && \
defined($this->getActiveRequest()->getClassName() . '::BLACKLISTED_IN_ENTERPRISE_MODE')
83 && \
constant($this->getActiveRequest()->getClassName() . '::BLACKLISTED_IN_ENTERPRISE_MODE')
84 && !WCF
::getUser()->hasOwnerAccess()
86 throw new IllegalLinkException();
89 $this->checkOfflineMode();
92 $this->getActiveRequest()->execute();
93 } catch (NamedUserException
$e) {
101 * Builds a new request.
103 * @param string $application
104 * @throws IllegalLinkException
105 * @throws NamedUserException
106 * @throws SystemException
108 protected function buildRequest($application)
111 $routeData = RouteHandler
::getInstance()->getRouteData();
113 // handle landing page for frontend requests
114 if (!$this->isACPRequest()) {
115 $this->handleDefaultController($application, $routeData);
117 // check if accessing from the wrong domain (e.g. "www." omitted but domain was configured with)
118 if (!\
defined('WCF_RUN_MODE') || WCF_RUN_MODE
!== 'embedded') {
119 $applicationObject = ApplicationHandler
::getInstance()->getApplication($application);
120 if ($applicationObject->domainName
!= $_SERVER['HTTP_HOST']) {
121 // build URL, e.g. http://example.net/forum/
122 $url = FileUtil
::addTrailingSlash(
123 RouteHandler
::getProtocol() . $applicationObject->domainName
. RouteHandler
::getPath()
126 // query string, e.g. ?foo=bar
127 if (!empty($_SERVER['QUERY_STRING'])) {
128 $url .= '?' . $_SERVER['QUERY_STRING'];
131 HeaderUtil
::redirect($url, true, false);
136 } elseif (empty($routeData['controller'])) {
137 $routeData['controller'] = 'index';
140 $controller = $routeData['controller'];
142 if (isset($routeData['className'])) {
144 'className' => $routeData['className'],
145 'controller' => $routeData['controller'],
146 'pageType' => $routeData['pageType'],
149 unset($routeData['className']);
150 unset($routeData['controller']);
151 unset($routeData['pageType']);
154 $this->isACPRequest()
155 && ($controller === 'login' ||
$controller === 'index')
156 && $application !== 'wcf'
158 HeaderUtil
::redirect(
159 LinkHandler
::getInstance()->getLink(\
ucfirst($controller)),
167 $classApplication = $application;
169 !empty($routeData['isDefaultController'])
170 && !empty($routeData['application'])
171 && $routeData['application'] !== $application
173 $classApplication = $routeData['application'];
176 $classData = ControllerMap
::getInstance()->resolve(
179 $this->isACPRequest(),
180 RouteHandler
::getInstance()->isRenamedController()
182 if (\
is_string($classData)) {
183 $this->redirect($routeData, $application, $classData);
187 // handle CMS page meta data
188 $metaData = ['isDefaultController' => (!empty($routeData['isDefaultController']))];
189 if (isset($routeData['cmsPageID'])) {
191 'pageID' => $routeData['cmsPageID'],
192 'languageID' => $routeData['cmsPageLanguageID'],
196 $routeData['cmsPageLanguageID']
197 && $routeData['cmsPageLanguageID'] != WCF
::getLanguage()->languageID
199 WCF
::setLanguage($routeData['cmsPageLanguageID']);
202 unset($routeData['cmsPageID']);
203 unset($routeData['cmsPageLanguageID']);
206 $this->activeRequest
= new Request(
207 $classData['className'],
208 $classData['controller'],
209 $classData['pageType'],
213 // check if the controller matches an app that has an expired evaluation date
214 $abbreviation = \
mb_substr($classData['className'], 0, \
mb_strpos($classData['className'], '\\'));
215 if ($abbreviation !== 'wcf') {
216 $applicationObject = ApplicationHandler
::getInstance()->getApplication($abbreviation);
217 $endDate = WCF
::getApplicationObject($applicationObject)->getEvaluationEndDate();
218 if ($endDate && $endDate < TIME_NOW
) {
219 $package = $applicationObject->getPackage();
221 $pluginStoreFileID = WCF
::getApplicationObject($applicationObject)->getEvaluationPluginStoreID();
223 if ($pluginStoreFileID === 0 && \
strpos($package->package
, 'com.woltlab.') === 0) {
227 throw new NamedUserException(WCF
::getLanguage()->getDynamicVariable(
228 'wcf.acp.package.evaluation.expired',
230 'packageName' => $package->getName(),
231 'pluginStoreFileID' => $pluginStoreFileID,
232 'isWoltLab' => $isWoltLab,
238 if (!$this->isACPRequest()) {
239 // determine if current request matches the landing page
240 if (ControllerMap
::getInstance()->isLandingPage($classData, $metaData)) {
241 $this->activeRequest
->setIsLandingPage();
245 ApplicationHandler
::getInstance()->rebuildActiveApplication();
246 } catch (SystemException
$e) {
248 \
defined('ENABLE_DEBUG_MODE')
250 && \
defined('ENABLE_DEVELOPER_TOOLS')
251 && ENABLE_DEVELOPER_TOOLS
256 throw new IllegalLinkException();
260 protected function checkOfflineMode()
262 if (!$this->isACPRequest() && \
defined('OFFLINE') && OFFLINE
) {
264 !WCF
::getSession()->getPermission('admin.general.canViewPageDuringOfflineMode')
265 && !$this->getActiveRequest()->isAvailableDuringOfflineMode()
268 isset($_SERVER['HTTP_X_REQUESTED_WITH'])
269 && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
271 throw new AJAXException(
272 WCF
::getLanguage()->getDynamicVariable('wcf.ajax.error.permissionDenied'),
273 AJAXException
::INSUFFICIENT_PERMISSIONS
276 @\
header('HTTP/1.1 503 Service Unavailable');
277 BoxHandler
::disablePageLayout();
278 NoticeHandler
::disableNotices();
279 WCF
::getTPL()->assign([
280 'templateName' => 'offline',
281 'templateNameApplication' => 'wcf',
283 WCF
::getTPL()->display('offline');
292 * Redirects to the actual URL, e.g. controller has been aliased or mistyped (boardlist instead of board-list).
294 * @param string[] $routeData
295 * @param string $application
296 * @param string $controller
298 protected function redirect(array $routeData, $application, $controller = null)
300 $routeData['application'] = $application;
301 if ($controller !== null) {
302 $routeData['controller'] = $controller;
305 // append the remaining query parameters
306 foreach ($_GET as $key => $value) {
307 if (!empty($value) && $key != 'controller') {
308 $routeData[$key] = $value;
312 $redirectURL = LinkHandler
::getInstance()->getLink($routeData['controller'], $routeData);
313 HeaderUtil
::redirect($redirectURL, true, false);
319 * Checks page access for possible mandatory redirects.
321 * @param string $application
322 * @param string[] $routeData
323 * @throws IllegalLinkException
325 protected function handleDefaultController($application, array &$routeData)
327 if (!RouteHandler
::getInstance()->isDefaultController()) {
331 $data = ControllerMap
::getInstance()->lookupDefaultController($application);
332 if ($data === null) {
333 // handle WCF which does not have a default controller
334 throw new IllegalLinkException();
335 } elseif (!empty($data['redirect'])) {
337 HeaderUtil
::redirect($data['redirect'], true, false);
340 } elseif (!empty($data['application']) && $data['application'] !== $application) {
341 $override = ControllerMap
::getInstance()->getApplicationOverride($application, $data['controller']);
342 if ($application !== $override) {
343 HeaderUtil
::redirect(
344 LinkHandler
::getInstance()->getLink(
345 ControllerMap
::getInstance()->resolve(
346 $data['application'],
350 ['application' => $data['application']]
361 foreach ($data as $key => $value) {
362 $routeData[$key] = $value;
365 $routeData['isDefaultController'] = true;
369 * Returns the active request object.
373 public function getActiveRequest()
375 return $this->activeRequest
;
379 * Returns true if the request is an acp request.
383 public function isACPRequest()
385 return $this->isACPRequest
;
389 * Returns true, if current host mismatches any known domain.
393 public function inRescueMode()
395 return $this->inRescueMode
;