b601080418776e3f5c6f8ad6a2556f9cea59f519
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / request / RequestHandler.class.php
1 <?php
2 namespace wcf\system\request;
3 use wcf\system\application\ApplicationHandler;
4 use wcf\system\exception\AJAXException;
5 use wcf\system\exception\IllegalLinkException;
6 use wcf\system\exception\NamedUserException;
7 use wcf\system\exception\SystemException;
8 use wcf\system\menu\page\PageMenu;
9 use wcf\system\SingletonFactory;
10 use wcf\system\WCF;
11 use wcf\util\FileUtil;
12 use wcf\util\HeaderUtil;
13
14 /**
15 * Handles http requests.
16 *
17 * @author Marcel Werk
18 * @copyright 2001-2015 WoltLab GmbH
19 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
20 * @package com.woltlab.wcf
21 * @subpackage system.request
22 * @category Community Framework
23 */
24 class RequestHandler extends SingletonFactory {
25 /**
26 * active request object
27 * @var Request
28 */
29 protected $activeRequest;
30
31 /**
32 * true, if current domain mismatch any known domain
33 * @var boolean
34 */
35 protected $inRescueMode = false;
36
37 /**
38 * indicates if the request is an acp request
39 * @var boolean
40 */
41 protected $isACPRequest = false;
42
43 /**
44 * @see \wcf\system\SingletonFactory::init()
45 */
46 protected function init() {
47 $this->isACPRequest = class_exists('wcf\system\WCFACP', false);
48 }
49
50 /**
51 * Handles a http request.
52 *
53 * @param string $application
54 * @param boolean $isACPRequest
55 * @throws AJAXException
56 * @throws IllegalLinkException
57 * @throws SystemException
58 */
59 public function handle($application = 'wcf', $isACPRequest = false) {
60 try {
61 $this->isACPRequest = $isACPRequest;
62
63 if (!RouteHandler::getInstance()->matches()) {
64 if (ENABLE_DEBUG_MODE) {
65 throw new SystemException("Cannot handle request, no valid route provided.");
66 }
67 else {
68 throw new IllegalLinkException();
69 }
70 }
71
72 // build request
73 $this->buildRequest($application);
74
75 // handle offline mode
76 if (!$isACPRequest && defined('OFFLINE') && OFFLINE) {
77 if (!WCF::getSession()->getPermission('admin.general.canViewPageDuringOfflineMode') && !$this->activeRequest->isAvailableDuringOfflineMode()) {
78 if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')) {
79 throw new AJAXException(WCF::getLanguage()->get('wcf.ajax.error.permissionDenied'), AJAXException::INSUFFICIENT_PERMISSIONS);
80 }
81 else {
82 @header('HTTP/1.1 503 Service Unavailable');
83 WCF::getTPL()->assign(array(
84 'templateName' => 'offline',
85 'templateNameApplication' => 'wcf'
86 ));
87 WCF::getTPL()->display('offline');
88 }
89
90 exit;
91 }
92 }
93
94 // start request
95 $this->activeRequest->execute();
96 }
97 catch (NamedUserException $e) {
98 $e->show();
99 exit;
100 }
101 }
102
103 /**
104 * Builds a new request.
105 *
106 * @param string $application
107 * @throws IllegalLinkException
108 */
109 protected function buildRequest($application) {
110 try {
111 $routeData = RouteHandler::getInstance()->getRouteData();
112
113 // handle landing page for frontend requests
114 if (!$this->isACPRequest()) {
115 $this->handleDefaultController($application, $routeData);
116
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(RouteHandler::getProtocol() . $applicationObject->domainName . RouteHandler::getPath());
123
124 // query string, e.g. ?foo=bar
125 if (!empty($_SERVER['QUERY_STRING'])) {
126 $url .= '?' . $_SERVER['QUERY_STRING'];
127 }
128
129 HeaderUtil::redirect($url, true);
130 exit;
131 }
132 }
133 }
134 else if (empty($routeData['controller'])) {
135 $routeData['controller'] = 'index';
136 }
137
138 $controller = $routeData['controller'];
139
140 if (isset($routeData['className'])) {
141 $classData = [
142 'className' => $routeData['className'],
143 'controller' => $routeData['controller'],
144 'pageType' => $routeData['pageType']
145 ];
146
147 unset($routeData['className']);
148 unset($routeData['controller']);
149 unset($routeData['pageType']);
150 }
151 else {
152 $classData = ControllerMap::getInstance()->resolve($application, $controller, $this->isACPRequest());
153 if (is_string($classData)) {
154 $this->redirect($routeData, $application, $classData);
155 }
156 }
157
158 // handle CMS page meta data
159 $metaData = [];
160 if (isset($routeData['cmsPageID'])) {
161 $metaData['cms'] = [
162 'pageID' => $routeData['cmsPageID'],
163 'languageID' => $routeData['cmsPageLanguageID']
164 ];
165
166 unset($routeData['cmsPageID']);
167 unset($routeData['cmsPageLanguageID']);
168 }
169
170 $this->activeRequest = new Request($classData['className'], $classData['controller'], $classData['pageType'], $metaData);
171 }
172 catch (SystemException $e) {
173 throw new IllegalLinkException();
174 }
175 }
176
177 /**
178 * Redirects to the actual URL, e.g. controller has been aliased or mistyped (boardlist instead of board-list).
179 *
180 * @param string[] $routeData
181 * @param string $application
182 * @param string $controller
183 */
184 protected function redirect(array $routeData, $application, $controller = null) {
185 $routeData['application'] = $application;
186 if ($controller !== null) $routeData['controller'] = $controller;
187
188 // append the remaining query parameters
189 foreach ($_GET as $key => $value) {
190 if (!empty($value) && $key != 'controller') {
191 $routeData[$key] = $value;
192 }
193 }
194
195 $redirectURL = LinkHandler::getInstance()->getLink($routeData['controller'], $routeData);
196 HeaderUtil::redirect($redirectURL, true);
197 exit;
198 }
199
200 /**
201 * Checks page access for possible mandatory redirects.
202 *
203 * @param string $application
204 * @param string[] $routeData
205 */
206 protected function handleDefaultController($application, array &$routeData) {
207 if (!RouteHandler::getInstance()->isDefaultController()) {
208 return;
209 }
210
211 $data = ControllerMap::getInstance()->lookupDefaultController($application);
212 if ($data === null) {
213 // handle WCF which does not have a default controller
214 throw new IllegalLinkException();
215 }
216 else if (!empty($data['redirect'])) {
217 // force a redirect
218 HeaderUtil::redirect($data['redirect'], true);
219 }
220
221 // copy route data
222 foreach ($data as $key => $value) {
223 $routeData[$key] = $value;
224 }
225 }
226
227 /**
228 * Returns the active request object.
229 *
230 * @return Request
231 */
232 public function getActiveRequest() {
233 return $this->activeRequest;
234 }
235
236 /**
237 * Returns true if the request is an acp request.
238 *
239 * @return boolean
240 */
241 public function isACPRequest() {
242 return $this->isACPRequest;
243 }
244
245 /**
246 * Returns true, if current host mismatches any known domain.
247 *
248 * @return boolean
249 */
250 public function inRescueMode() {
251 return $this->inRescueMode;
252 }
253 }