1 | <?php |
||||
2 | |||||
3 | namespace Sunnysideup\TemplateOverview\Control; |
||||
4 | |||||
5 | use GuzzleHttp\Client; |
||||
6 | use GuzzleHttp\Cookie\CookieJar; |
||||
7 | use GuzzleHttp\Exception\RequestException; |
||||
8 | use GuzzleHttp\Psr7; |
||||
9 | use Psr\SimpleCache\CacheInterface; |
||||
10 | use SebastianBergmann\Diff\Differ; |
||||
11 | use SilverStripe\Control\Controller; |
||||
12 | use SilverStripe\Control\Director; |
||||
13 | use SilverStripe\Control\HTTPRequest; |
||||
14 | use SilverStripe\Core\Config\Config; |
||||
15 | use SilverStripe\Core\Convert; |
||||
16 | use SilverStripe\Core\Flushable; |
||||
17 | use SilverStripe\Core\Injector\Injector; |
||||
18 | use SilverStripe\Security\DefaultAdminService; |
||||
19 | use SilverStripe\Security\Member; |
||||
20 | use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator; |
||||
21 | use SilverStripe\Security\Permission; |
||||
22 | use Sunnysideup\TemplateOverview\Api\AllLinks; |
||||
23 | use Sunnysideup\TemplateOverview\Api\W3cValidateApi; |
||||
24 | |||||
25 | /** |
||||
26 | * Class \Sunnysideup\TemplateOverview\Control\CheckAllTemplatesResponseController |
||||
27 | * |
||||
28 | */ |
||||
29 | class CheckAllTemplatesResponseController extends Controller implements Flushable |
||||
30 | { |
||||
31 | /** |
||||
32 | * Defines methods that can be called directly. |
||||
33 | * |
||||
34 | * @var array |
||||
35 | */ |
||||
36 | private static $allowed_actions = [ |
||||
37 | 'testone' => 'ADMIN', |
||||
38 | ]; |
||||
39 | |||||
40 | private static $use_default_admin = true; |
||||
41 | |||||
42 | private static $username = ''; |
||||
43 | |||||
44 | private static $password = ''; |
||||
45 | |||||
46 | private static $url_segment = 'admin/templateoverviewsmoketestresponse'; |
||||
47 | |||||
48 | private static $use_w3_validation = false; |
||||
49 | |||||
50 | private static $create_diff = false; |
||||
51 | |||||
52 | private $guzzleCookieJar; |
||||
53 | |||||
54 | private $guzzleClient; |
||||
55 | |||||
56 | private $guzzleHasError = false; |
||||
57 | |||||
58 | private $isSuccess = false; |
||||
59 | |||||
60 | /** |
||||
61 | * temporary Admin used to log in. |
||||
62 | * |
||||
63 | * @var Member |
||||
64 | */ |
||||
65 | private $member; |
||||
66 | |||||
67 | private $rawResponse = ''; |
||||
68 | |||||
69 | /** |
||||
70 | * @var bool |
||||
71 | */ |
||||
72 | private $debug = false; |
||||
73 | |||||
74 | public static function flush() |
||||
75 | { |
||||
76 | $cache = Injector::inst()->get(CacheInterface::class . '.templateoverview'); |
||||
77 | $cache->clear(); |
||||
78 | } |
||||
79 | |||||
80 | public static function get_user_email() |
||||
81 | { |
||||
82 | $userName = Config::inst()->get(self::class, 'username'); |
||||
83 | if (! $userName) { |
||||
84 | if (Config::inst()->get(self::class, 'use_default_admin')) { |
||||
85 | $userName = DefaultAdminService::getDefaultAdminUsername(); |
||||
86 | } else { |
||||
87 | $userName = '[email protected]'; |
||||
88 | } |
||||
89 | } |
||||
90 | |||||
91 | return $userName; |
||||
92 | } |
||||
93 | |||||
94 | public static function get_password() |
||||
95 | { |
||||
96 | $password = Config::inst()->get(self::class, 'password'); |
||||
97 | if (! $password) { |
||||
98 | if (Config::inst()->get(self::class, 'use_default_admin')) { |
||||
99 | $password = DefaultAdminService::getDefaultAdminPassword(); |
||||
100 | } else { |
||||
101 | $cache = Injector::inst()->get(CacheInterface::class . '.templateoverview'); |
||||
102 | if (! $cache->has('password')) { |
||||
103 | $password = strtolower('aa' . substr(uniqid(), 0, 8)) . '_.,' . strtoupper('BB' . substr(uniqid(), 0, 8)); |
||||
104 | $cache->set('password', $password); |
||||
105 | } |
||||
106 | |||||
107 | $password = $cache->get('password'); |
||||
108 | } |
||||
109 | } |
||||
110 | |||||
111 | return $password; |
||||
112 | } |
||||
113 | |||||
114 | public static function get_test_user() |
||||
115 | { |
||||
116 | return Injector::inst()->get(self::class)->getTestUser(); |
||||
117 | } |
||||
118 | |||||
119 | /** |
||||
120 | * Main function |
||||
121 | * has two streams: |
||||
122 | * 1. check on url specified in GET variable. |
||||
123 | * 2. create a list of urls to check. |
||||
124 | */ |
||||
125 | public function testone(HTTPRequest $request) |
||||
126 | { |
||||
127 | $isCMSLink = (bool) $request->getVar('iscmslink'); |
||||
128 | $testURL = $request->getVar('test') ?: null; |
||||
129 | |||||
130 | // 1. actually test a URL and return the data |
||||
131 | if ($testURL) { |
||||
132 | $this->guzzleSetup(); |
||||
133 | $this->getTestUser(); |
||||
134 | $content = $this->testURL($testURL); |
||||
135 | $this->deleteUser(); |
||||
136 | $diff = 'Please install https://github.com/Kevin-Kip/meru/ to see diff.'; |
||||
137 | //these echo is required! |
||||
138 | echo $content; |
||||
139 | if (! Director::is_ajax()) { |
||||
140 | $comparisonBaseURL = Config::inst()->get(self::class, 'comparision_base_url'); |
||||
141 | $width = '98%'; |
||||
142 | $style = 'border: none;'; |
||||
143 | if ($comparisonBaseURL) { |
||||
144 | $width = '48%'; |
||||
145 | $style = 'float: left;'; |
||||
146 | if ($this->isSuccess && ! $isCMSLink && $this->Config()->create_diff) { |
||||
147 | $otherURL = $comparisonBaseURL . $testURL; |
||||
148 | $testContent = str_replace(Director::absoluteBaseURL(), $comparisonBaseURL, $this->rawResponse); |
||||
149 | $rawResponseOtherSite = @file_get_contents($otherURL); |
||||
150 | if (class_exists(Differ::class)) { |
||||
151 | $diff = (new Differ())->diff( |
||||
152 | $testContent, |
||||
153 | $rawResponseOtherSite |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
154 | ); |
||||
155 | $rawResponseOtherSite = Convert::raw2htmlatt(str_replace("'", '\\\'', $rawResponseOtherSite)); |
||||
156 | $diff = ' |
||||
157 | <iframe id="iframe2" width="' . $width . '%" height="7000" srcdoc=\'' . $rawResponseOtherSite . '\' style="float: right;"></iframe> |
||||
0 ignored issues
–
show
Are you sure
$rawResponseOtherSite of type array|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
158 | |||||
159 | <hr style="clear: both; margin-top: 20px; padding-top: 20px;" /> |
||||
160 | <h1>Diff</h1> |
||||
161 | <link href="/resources/vendor/sunnysideup/templateoverview/client/css/checkalltemplates.css" rel="stylesheet" type="text/css" /> |
||||
162 | ' . $diff; |
||||
163 | } |
||||
164 | } |
||||
165 | } |
||||
166 | |||||
167 | $rawResponse = Convert::raw2htmlatt(str_replace("'", '\\\'', $this->rawResponse)); |
||||
168 | echo ' |
||||
169 | <h1>Response</h1> |
||||
170 | '; |
||||
171 | echo $diff; |
||||
172 | echo ' |
||||
173 | <iframe id="iframe" width="' . $width . '" height="700" srcdoc=\'' . $rawResponse . '\' style="' . $style . '"></iframe> |
||||
174 | '; |
||||
175 | } |
||||
176 | |||||
177 | return; |
||||
178 | } |
||||
179 | |||||
180 | user_error('no test url provided.'); |
||||
181 | } |
||||
182 | |||||
183 | public function getTestUser() |
||||
184 | { |
||||
185 | if (Config::inst()->get(self::class, 'use_default_admin')) { |
||||
186 | $this->member = Injector::inst()->get(DefaultAdminService::class)->findOrCreateDefaultAdmin(); |
||||
187 | |||||
188 | return $this->member; |
||||
189 | } |
||||
190 | |||||
191 | //Make temporary admin member |
||||
192 | $filter = ['Email' => self::get_user_email()]; |
||||
193 | // @var Member|null $this->member |
||||
194 | $this->member = Member::get() |
||||
195 | ->filter($filter) |
||||
196 | ->first() |
||||
197 | ; |
||||
198 | if (empty($this->member)) { |
||||
199 | $this->member = Member::create($filter); |
||||
200 | } |
||||
201 | |||||
202 | $this->member->Password = self::get_password(); |
||||
203 | $this->member->LockedOutUntil = null; |
||||
204 | $this->member->FirstName = 'Test'; |
||||
205 | $this->member->Surname = 'User'; |
||||
206 | $this->member->write(); |
||||
207 | $auth = new MemberAuthenticator(); |
||||
208 | $result = $auth->checkPassword($this->member, self::get_password()); |
||||
209 | if (! $result->isValid()) { |
||||
210 | user_error('Error in creating test user.', E_USER_ERROR); |
||||
211 | |||||
212 | return; |
||||
213 | } |
||||
214 | |||||
215 | $service = Injector::inst()->get(DefaultAdminService::class); |
||||
216 | $adminGroup = $service->findOrCreateAdminGroup(); |
||||
217 | $this->member->Groups()->add($adminGroup); |
||||
218 | if (Permission::checkMember($this->member, 'ADMIN')) { |
||||
219 | user_error('No admin group exists', E_USER_ERROR); |
||||
220 | |||||
221 | return; |
||||
222 | } |
||||
223 | |||||
224 | return $this->member; |
||||
225 | } |
||||
226 | |||||
227 | /** |
||||
228 | * @return mixed |
||||
229 | */ |
||||
230 | protected function guzzleSendRequest(string $url) |
||||
231 | { |
||||
232 | $this->guzzleHasError = false; |
||||
233 | $credentials = base64_encode(self::get_user_email() . ':' . self::get_password()); |
||||
234 | |||||
235 | try { |
||||
236 | $response = $this->guzzleClient->request( |
||||
237 | 'GET', |
||||
238 | $url, |
||||
239 | [ |
||||
240 | 'cookies' => $this->guzzleCookieJar, |
||||
241 | 'headers' => [ |
||||
242 | 'PHP_AUTH_USER' => self::get_user_email(), |
||||
243 | 'PHP_AUTH_PW' => self::get_password(), |
||||
244 | ], |
||||
245 | 'auth' => [ |
||||
246 | self::get_user_email(), |
||||
247 | self::get_password(), |
||||
248 | ], |
||||
249 | 'Authorization' => ['Basic ' . $credentials], |
||||
250 | ] |
||||
251 | ); |
||||
252 | } catch (RequestException $requestException) { |
||||
253 | $this->rawResponse = $requestException->getResponse(); |
||||
254 | $this->guzzleHasError = true; |
||||
255 | //echo Psr7\str($exception->getRequest()); |
||||
256 | if ($requestException->hasResponse()) { |
||||
257 | $response = $requestException->getResponse(); |
||||
258 | $this->rawResponse = $response->getStatusCode() . '|' . $response->getReasonPhrase(); |
||||
259 | } else { |
||||
260 | $response = null; |
||||
261 | } |
||||
262 | } |
||||
263 | |||||
264 | return $response; |
||||
265 | } |
||||
266 | |||||
267 | protected function isJson($string) |
||||
268 | { |
||||
269 | $obj = json_decode($string); |
||||
270 | |||||
271 | return JSON_ERROR_NONE === json_last_error() && 'object' === gettype($obj); |
||||
272 | } |
||||
273 | |||||
274 | /** |
||||
275 | * ECHOES the result of testing the URL.... |
||||
276 | * |
||||
277 | * @param string $url |
||||
278 | */ |
||||
279 | private function testURL($url) |
||||
280 | { |
||||
281 | if (strlen(trim($url)) < 1) { |
||||
282 | user_error('empty url'); //Checks for empty strings. |
||||
283 | } |
||||
284 | |||||
285 | if (AllLinks::is_admin_link($url)) { |
||||
286 | $validate = false; |
||||
287 | } else { |
||||
288 | $validate = Config::inst()->get(self::class, 'use_w3_validation'); |
||||
289 | } |
||||
290 | |||||
291 | $testURL = Director::absoluteURL('/admin/templateoverviewloginandredirect/login/?BackURL='); |
||||
292 | $testURL .= urlencode($url); |
||||
293 | $this->guzzleSetup(); |
||||
294 | |||||
295 | $start = microtime(true); |
||||
296 | $response = $this->guzzleSendRequest($testURL); |
||||
297 | $end = microtime(true); |
||||
298 | |||||
299 | $data = [ |
||||
300 | 'status' => 'success', |
||||
301 | 'httpResponse' => '200', |
||||
302 | 'content' => '', |
||||
303 | 'responseTime' => round($end - $start, 4), |
||||
304 | 'type' => '', |
||||
305 | 'length' => '', |
||||
306 | 'w3Content' => '', |
||||
307 | ]; |
||||
308 | |||||
309 | $httpResponse = $response->getStatusCode(); |
||||
310 | $error = $response->getReasonPhrase(); |
||||
311 | if ($this->guzzleHasError) { |
||||
312 | //we already have the body ... |
||||
313 | } else { |
||||
314 | $this->rawResponse = $response->getBody(); |
||||
315 | } |
||||
316 | |||||
317 | $data['httpResponse'] = $httpResponse; |
||||
318 | |||||
319 | if (401 === $httpResponse) { |
||||
320 | $data['status'] = 'error'; |
||||
321 | $data['content'] = 'Could not access: ' . $url; |
||||
322 | |||||
323 | return json_encode($data); |
||||
324 | } |
||||
325 | |||||
326 | //uncaught errors ... |
||||
327 | if ($this->rawResponse && 'Fatal error' === substr((string) $this->rawResponse, 0, 12)) { |
||||
328 | $data['status'] = 'error'; |
||||
329 | $data['content'] = $this->rawResponse; |
||||
330 | } elseif (200 === $httpResponse && $this->rawResponse && strlen( (string) $this->rawResponse) < 200) { |
||||
331 | if (! $this->isJson($this->rawResponse)) { |
||||
332 | $data['status'] = 'error - no response'; |
||||
333 | $data['content'] = 'SHORT RESPONSE: ' . $this->rawResponse; |
||||
334 | } |
||||
335 | } |
||||
336 | |||||
337 | $data['w3Content'] = 'n/a'; |
||||
338 | |||||
339 | if (200 !== $httpResponse) { |
||||
340 | $data['status'] = 'error'; |
||||
341 | $data['content'] .= 'unexpected response: ' . $error . $this->rawResponse; |
||||
342 | } else { |
||||
343 | $this->isSuccess = true; |
||||
344 | $data['type'] = $response->getHeaders()['Content-Type'][0] ?? 'no-content-type'; |
||||
345 | $data['length'] = $response->getHeaders()['Content-Length'][0] ?? '0'; |
||||
346 | if ($validate) { |
||||
347 | $w3Obj = new W3cValidateApi(); |
||||
348 | $data['w3Content'] = $w3Obj->W3Validate('', $this->rawResponse); |
||||
349 | } |
||||
350 | } |
||||
351 | |||||
352 | if (Director::is_ajax()) { |
||||
353 | return json_encode($data); |
||||
354 | } |
||||
355 | |||||
356 | $content = ''; |
||||
357 | $content .= '<p><strong>URL:</strong> ' . $url . '</p>'; |
||||
358 | $content .= '<p><strong>Status:</strong> ' . $data['status'] . '</p>'; |
||||
359 | $content .= '<p><strong>HTTP response:</strong> ' . $data['httpResponse'] . '</p>'; |
||||
360 | $content .= '<p><strong>Content:</strong> ' . htmlspecialchars($data['content']) . '</p>'; |
||||
361 | $content .= '<p><strong>Response time:</strong> ' . $data['responseTime'] . '</p>'; |
||||
362 | $content .= '<p><strong>Type:</strong> ' . $data['type'] . '</p>'; |
||||
363 | |||||
364 | return $content . ('<p><strong>W3 Content:</strong> ' . $data['w3Content'] . '</p>'); |
||||
365 | } |
||||
366 | |||||
367 | /** |
||||
368 | * creates the basic curl. |
||||
369 | */ |
||||
370 | private function guzzleSetup() |
||||
371 | { |
||||
372 | // $user_agent='Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0'; |
||||
373 | // $post = $type == "GET" ? false : true; |
||||
374 | // |
||||
375 | // $strCookie = 'PHPSESSID=' . session_id() . '; path=/'; |
||||
376 | // $options = array( |
||||
377 | // CURLOPT_CUSTOMREQUEST => $type, //set request type post or get |
||||
378 | // CURLOPT_POST => $post, //set to GET |
||||
379 | // CURLOPT_USERAGENT => $user_agent, //set user agent |
||||
380 | // CURLOPT_COOKIE => $strCookie, //set cookie file |
||||
381 | // CURLOPT_COOKIEFILE => "cookie.txt", //set cookie file |
||||
382 | // CURLOPT_COOKIEJAR => "cookie.txt", //set cookie jar |
||||
383 | // CURLOPT_RETURNTRANSFER => true, // return web page |
||||
384 | // CURLOPT_HEADER => false, // don't return headers |
||||
385 | // CURLOPT_FOLLOWLOCATION => true, // follow redirects |
||||
386 | // CURLOPT_ENCODING => "", // handle all encodings |
||||
387 | // CURLOPT_AUTOREFERER => true, // set referer on redirect |
||||
388 | // CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect |
||||
389 | // CURLOPT_TIMEOUT => 120, // timeout on response |
||||
390 | // CURLOPT_MAXREDIRS => 10, // stop after 10 redirects |
||||
391 | // ); |
||||
392 | // |
||||
393 | // $this->ch = curl_init(); |
||||
394 | // |
||||
395 | // curl_setopt_array($this->ch, $options); |
||||
396 | |||||
397 | $this->guzzleCookieJar = new CookieJar(); |
||||
398 | $this->guzzleClient = new Client( |
||||
399 | [ |
||||
400 | 'base_uri' => Director::baseURL(), |
||||
401 | 'cookies' => true, |
||||
402 | ] |
||||
403 | ); |
||||
404 | } |
||||
405 | |||||
406 | private function deleteUser() |
||||
407 | { |
||||
408 | /** @var null|bool $isAdmin */ |
||||
409 | $isAdmin = Config::inst()->get(self::class, 'use_default_admin'); |
||||
410 | if ($isAdmin) { |
||||
411 | //do nothing; |
||||
412 | } else { |
||||
413 | $this->member->delete(); |
||||
414 | } |
||||
415 | } |
||||
416 | |||||
417 | // private function debugme($lineNumber, $variable = "") |
||||
418 | // { |
||||
419 | // if ($this->debug) { |
||||
420 | // echo "<br />" . $lineNumber . ": " . round(memory_get_usage() / 1048576) . "MB" . "=====" . print_r($variable, 1); |
||||
421 | // ob_flush(); |
||||
422 | // flush(); |
||||
423 | // } |
||||
424 | // } |
||||
425 | } |
||||
426 |