1 | <?php |
||
2 | |||
3 | use Elgg\Filesystem\Directory; |
||
4 | use Elgg\Application; |
||
5 | use Elgg\Project\Paths; |
||
6 | use Elgg\Http\Request; |
||
7 | |||
8 | /** |
||
9 | * Elgg RewriteTester. |
||
10 | * Test if URL rewriting is working. |
||
11 | */ |
||
12 | class ElggRewriteTester { |
||
13 | protected $webserver; |
||
14 | protected $serverSupportsRemoteRead; |
||
15 | protected $rewriteTestPassed; |
||
16 | protected $htaccessIssue; |
||
17 | |||
18 | /** |
||
19 | * Set the webserver as unknown. |
||
20 | */ |
||
21 | public function __construct() { |
||
22 | $this->webserver = 'unknown'; |
||
23 | } |
||
24 | |||
25 | /** |
||
26 | * Run the rewrite test and return a status array |
||
27 | * |
||
28 | * @param string $url URL of rewrite test |
||
29 | * @param string $path Root directory of Elgg with trailing slash |
||
30 | * |
||
31 | * @return array |
||
32 | */ |
||
33 | public function run($url, $path) { |
||
34 | |||
35 | $this->webserver = \ElggRewriteTester::guessWebServer(); |
||
36 | |||
37 | $this->rewriteTestPassed = $this->runRewriteTest($url); |
||
38 | |||
39 | if ($this->rewriteTestPassed == false) { |
||
40 | if ($this->webserver == 'apache' || $this->webserver == 'unknown') { |
||
41 | if ($this->createHtaccess($url, $path)) { |
||
42 | $this->rewriteTestPassed = $this->runRewriteTest($url); |
||
43 | } |
||
44 | } |
||
45 | } |
||
46 | |||
47 | return $this->returnStatus($url); |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Guess the web server from $_SERVER['SERVER_SOFTWARE'] |
||
52 | * |
||
53 | * @return string |
||
54 | */ |
||
55 | public static function guessWebServer() { |
||
56 | if (empty($_SERVER['SERVER_SOFTWARE'])) { |
||
57 | return 'unknown'; |
||
58 | } |
||
59 | |||
60 | $serverString = strtolower($_SERVER['SERVER_SOFTWARE']); |
||
61 | $possibleServers = ['apache', 'nginx', 'lighttpd', 'iis']; |
||
62 | foreach ($possibleServers as $server) { |
||
63 | if (strpos($serverString, $server) !== false) { |
||
64 | return $server; |
||
65 | } |
||
66 | } |
||
67 | return 'unknown'; |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Guess if url contains subdirectory or not. |
||
72 | * |
||
73 | * @param string $url Rewrite test URL |
||
74 | * |
||
75 | * @return string|bool Subdirectory string with beginning and trailing slash or false if were unable to determine subdirectory |
||
76 | * or pointing at root of domain already |
||
77 | */ |
||
78 | public function guessSubdirectory($url) { |
||
79 | $elements = parse_url($url); |
||
80 | if (!$elements || !isset($elements['path'])) { |
||
0 ignored issues
–
show
|
|||
81 | return false; |
||
82 | } |
||
83 | $subdir = trim(dirname($elements['path']), '/'); |
||
84 | if (!$subdir) { |
||
85 | return false; |
||
86 | } else { |
||
87 | return "/$subdir/"; |
||
88 | } |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Hit the rewrite test URL to determine if the rewrite rules are working |
||
93 | * |
||
94 | * @param string $url Rewrite test URL |
||
95 | * |
||
96 | * @return bool |
||
97 | */ |
||
98 | public function runRewriteTest($url) { |
||
99 | $this->serverSupportsRemoteRead = ($this->fetchUrl($url) === Request::REWRITE_TEST_OUTPUT); |
||
100 | return $this->serverSupportsRemoteRead; |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Check whether the site homepage can be fetched via curl |
||
105 | * |
||
106 | * @return boolean |
||
107 | */ |
||
108 | public function runLocalhostAccessTest() { |
||
109 | $url = _elgg_config()->wwwroot; |
||
110 | return (bool) $this->fetchUrl($url); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Fetch a URL |
||
115 | * |
||
116 | * @param string $url The URL |
||
117 | * |
||
118 | * @return string Note that empty string may imply failure in fetching or empty response |
||
119 | */ |
||
120 | private function fetchUrl($url) { |
||
121 | $response = ''; |
||
122 | |||
123 | if (ini_get('allow_url_fopen')) { |
||
124 | $ctx = stream_context_create([ |
||
125 | 'http' => [ |
||
126 | 'follow_location' => 0, |
||
127 | 'timeout' => 5, |
||
128 | ], |
||
129 | ]); |
||
130 | $response = @file_get_contents($url, null, $ctx); |
||
131 | } |
||
132 | |||
133 | if (!$response && function_exists('curl_init')) { |
||
134 | $ch = curl_init(); |
||
135 | curl_setopt($ch, CURLOPT_URL, $url); |
||
136 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||
137 | curl_setopt($ch, CURLOPT_TIMEOUT, 5); |
||
138 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); |
||
139 | $response = curl_exec($ch); |
||
140 | curl_close($ch); |
||
141 | } |
||
142 | |||
143 | return (string) $response; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * Create Elgg's .htaccess file or confirm that it exists |
||
148 | * |
||
149 | * @param string $url URL of rewrite test |
||
150 | * |
||
151 | * @return bool |
||
152 | */ |
||
153 | public function createHtaccess($url) { |
||
154 | $root = Directory\Local::projectRoot(); |
||
155 | $file = $root->getFile(".htaccess"); |
||
156 | |||
157 | if ($file->exists()) { |
||
158 | // check that this is the Elgg .htaccess |
||
159 | $data = $file->getContents(); |
||
160 | if ($data === false) { |
||
161 | // don't have permission to read the file |
||
162 | $this->htaccessIssue = 'read_permission'; |
||
163 | return false; |
||
164 | } |
||
165 | |||
166 | if (strpos($data, 'Elgg') === false) { |
||
167 | $this->htaccessIssue = 'non_elgg_htaccess'; |
||
168 | return false; |
||
169 | } |
||
170 | |||
171 | // check if this is an old Elgg htaccess |
||
172 | if (strpos($data, 'RewriteRule ^rewrite.php$ install.php') == false) { |
||
173 | $this->htaccessIssue = 'old_elgg_htaccess'; |
||
174 | return false; |
||
175 | } |
||
176 | return true; |
||
177 | } |
||
178 | |||
179 | if (!is_writable($root->getPath())) { |
||
180 | $this->htaccessIssue = 'write_permission'; |
||
181 | return false; |
||
182 | } |
||
183 | |||
184 | // create the .htaccess file |
||
185 | $result = copy(Paths::elgg() . "install/config/htaccess.dist", $file->getPath()); |
||
186 | if (!$result) { |
||
187 | $this->htaccessIssue = 'cannot_copy'; |
||
188 | return false; |
||
189 | } |
||
190 | |||
191 | // does default RewriteBase work already? |
||
192 | if (!$this->runRewriteTest($url)) { |
||
193 | //try to rewrite to guessed subdirectory |
||
194 | if ($subdir = $this->guessSubdirectory($url)) { |
||
195 | $contents = $file->getContents(); |
||
196 | $contents = preg_replace("/#RewriteBase \/(\r?\n)/", "RewriteBase $subdir\$1", $contents); |
||
197 | if ($contents) { |
||
198 | $file->putContents($contents); |
||
199 | } |
||
200 | } |
||
201 | } |
||
202 | |||
203 | return true; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Create the status array required by the ElggInstaller |
||
208 | * |
||
209 | * @param string $url Rewrite test URL |
||
210 | * |
||
211 | * @return array |
||
212 | */ |
||
213 | protected function returnStatus($url) { |
||
214 | if ($this->rewriteTestPassed) { |
||
215 | return [ |
||
216 | 'severity' => 'pass', |
||
217 | 'message' => _elgg_services()->translator->translate('install:check:rewrite:success'), |
||
218 | ]; |
||
219 | } |
||
220 | |||
221 | if ($this->serverSupportsRemoteRead == false) { |
||
222 | $msg = _elgg_services()->translator->translate('install:warning:rewrite:unknown', [$url]); |
||
223 | $msg .= elgg_view('install/js_rewrite_check', ['url' => $url]); |
||
224 | |||
225 | return [ |
||
226 | 'severity' => 'warning', |
||
227 | 'message' => $msg, |
||
228 | ]; |
||
229 | } |
||
230 | |||
231 | if ($this->webserver == 'apache') { |
||
232 | $serverString = _elgg_services()->translator->translate('install:error:rewrite:apache'); |
||
233 | $msg = "$serverString\n\n"; |
||
234 | if (!isset($this->htaccessIssue)) { |
||
235 | $msg .= _elgg_services()->translator->translate('install:error:rewrite:allowoverride'); |
||
236 | $msg .= elgg_view('install/js_rewrite_check', ['url' => $url]); |
||
237 | |||
238 | return [ |
||
239 | 'severity' => 'warning', |
||
240 | 'message' => $msg, |
||
241 | ]; |
||
242 | } |
||
243 | $msg .= _elgg_services()->translator->translate("install:error:rewrite:htaccess:{$this->htaccessIssue}"); |
||
244 | return [ |
||
245 | 'severity' => 'warning', |
||
246 | 'message' => $msg, |
||
247 | ]; |
||
248 | } |
||
249 | |||
250 | if ($this->webserver != 'unknown') { |
||
251 | $serverString = _elgg_services()->translator->translate("install:error:rewrite:{$this->webserver}"); |
||
252 | $msg = "$serverString\n\n"; |
||
253 | $msg .= _elgg_services()->translator->translate("install:error:rewrite:altserver"); |
||
254 | return [ |
||
255 | 'severity' => 'warning', |
||
256 | 'message' => $msg, |
||
257 | ]; |
||
258 | } |
||
259 | |||
260 | return [ |
||
261 | 'severity' => 'warning', |
||
262 | 'message' => _elgg_services()->translator->translate('install:error:rewrite:unknown'), |
||
263 | ]; |
||
264 | } |
||
265 | } |
||
266 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.