Passed
Push — master ( f44b93...1b743f )
by Nicolaas
02:00
created

CheckAllTemplatesResponseController::debugme()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Sunnysideup\TemplateOverview\Control;
4
5
use GuzzleHttp\Client;
6
7
use GuzzleHttp\Cookie\CookieJar;
8
use GuzzleHttp\Exception\RequestException;
9
use GuzzleHttp\Psr7;
10
11
12
use Psr\SimpleCache\CacheInterface;
13
use SilverStripe\Control\Controller;
14
use SilverStripe\Control\Director;
15
use SilverStripe\Core\Config\Config;
16
use SilverStripe\Core\Convert;
17
use SilverStripe\Core\Flushable;
18
use SilverStripe\Core\Injector\Injector;
19
20
use SilverStripe\Security\DefaultAdminService;
21
use SilverStripe\Security\Member;
22
use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator;
23
24
use SilverStripe\Security\Permission;
25
use Sunnysideup\TemplateOverview\Api\AllLinks;
26
use Sunnysideup\TemplateOverview\Api\DiffMachine;
27
use Sunnysideup\TemplateOverview\Api\W3cValidateApi;
28
29
/**
30
 * @description (see $this->description)
31
 *
32
 * @authors: Andrew Pett [at] sunny side up .co.nz, Nicolaas [at] Sunny Side Up .co.nz
33
 * @package: templateoverview
34
 * @sub-package: tasks
35
 **/
36
37
class CheckAllTemplatesResponseController extends Controller implements Flushable
38
{
39
    /**
40
     * Defines methods that can be called directly
41
     * @var array
42
     */
43
    private static $allowed_actions = [
44
        'testone' => 'ADMIN',
45
    ];
46
47
    private static $use_default_admin = true;
48
49
    private static $username = '';
50
51
    private static $password = '';
52
53
    private static $url_segment = 'templateoverviewsmoketestresponse';
54
55
    private static $use_w3_validation = false;
56
57
    private static $create_diff = false;
58
59
    private $guzzleCookieJar = null;
60
61
    private $guzzleClient = null;
62
63
    private $guzzleHasError = false;
64
65
    private $isSuccess = false;
66
67
    /**
68
     * temporary Admin used to log in.
69
     * @var Member
70
     */
71
    private $member = null;
72
73
    private $rawResponse = '';
74
75
    /**
76
     * @var boolean
77
     */
78
    private $debug = false;
79
80
    public static function flush()
81
    {
82
        $cache = Injector::inst()->get(CacheInterface::class . '.templateoverview');
83
        $cache->clear();
84
    }
85
86
    public static function get_user_email()
87
    {
88
        $userName = Config::inst()->get(self::class, 'username');
89
        if (! $userName) {
90
            if (Config::inst()->get(self::class, 'use_default_admin')) {
91
                $userName = DefaultAdminService::getDefaultAdminUsername();
92
            } else {
93
                $userName = '[email protected]';
94
            }
95
        }
96
97
        return $userName;
98
    }
99
100
    public static function get_password()
101
    {
102
        $password = Config::inst()->get(self::class, 'password');
103
        if (! $password) {
104
            if (Config::inst()->get(self::class, 'use_default_admin')) {
105
                $password = DefaultAdminService::getDefaultAdminPassword();
106
            } else {
107
                $cache = Injector::inst()->get(CacheInterface::class . '.templateoverview');
108
                if (! $cache->has('password')) {
109
                    $password = strtolower('aa' . substr(uniqid(), 0, 8)) . '_.,' . strtoupper('BB' . substr(uniqid(), 0, 8));
110
                    $cache->set('password', $password);
111
                }
112
                $password = $cache->get('password');
113
            }
114
        }
115
116
        return $password;
117
    }
118
119
    public static function get_test_user()
120
    {
121
        return Injector::inst()->get(self::class)->getTestUser();
122
    }
123
124
    /**
125
     * Main function
126
     * has two streams:
127
     * 1. check on url specified in GET variable.
128
     * 2. create a list of urls to check
129
     *
130
     * @param HTTPRequest $request
0 ignored issues
show
Bug introduced by
The type Sunnysideup\TemplateOverview\Control\HTTPRequest was not found. Did you mean HTTPRequest? If so, make sure to prefix the type with \.
Loading history...
131
     */
132
    public function testone($request)
133
    {
134
        $isCMSLink = $request->getVar('iscmslink') ? true : false;
135
        $testURL = $request->getVar('test') ?: null;
136
137
        // 1. actually test a URL and return the data
138
        if ($testURL) {
139
            $this->guzzleSetup();
140
            $this->getTestUser();
141
            $content = $this->testURL($testURL);
142
            $this->deleteUser();
143
            $this->cleanup();
144
            print $content;
145
            if (! Director::is_ajax()) {
146
                $diff = '';
147
                $comparisonBaseURL = Config::inst()->get(self::class, 'comparision_base_url');
148
                $width = '98%';
149
                $style = 'border: none;';
150
                if ($comparisonBaseURL) {
151
                    $width = '48%';
152
                    $style = 'float: left;';
153
                    if ($this->isSuccess && ! $isCMSLink && $this->Config()->create_diff) {
154
                        $otherURL = $comparisonBaseURL . $testURL;
155
                        $testContent = str_replace(Director::absoluteBaseURL(), $comparisonBaseURL, $this->rawResponse);
156
                        $rawResponseOtherSite = @file_get_contents($otherURL);
157
                        $diff = DiffMachine::compare(
158
                            $testContent,
159
                            $rawResponseOtherSite
160
                        );
161
                        $rawResponseOtherSite = Convert::raw2htmlatt(str_replace('\'', '\\\'', $rawResponseOtherSite));
162
                        $diff = '
163
                        <iframe id="iframe2" width="' . $width . '%" height="7000" srcdoc=\'' . $rawResponseOtherSite . '\' style="float: right;"></iframe>
164
165
                        <hr style="clear: both; margin-top: 20px; padding-top: 20px;" />
166
                        <h1>Diff</h1>
167
                        <link href="/resources/vendor/sunnysideup/templateoverview/client/css/checkalltemplates.css" rel="stylesheet" type="text/css" />
168
                        ' . $diff;
169
                    }
170
                }
171
                $rawResponse = Convert::raw2htmlatt(str_replace('\'', '\\\'', $this->rawResponse));
172
                echo '
173
                    <h1>Response</h1>
174
                    <iframe id="iframe" width="' . $width . '%" height="7000" srcdoc=\'' . $rawResponse . '\' style="' . $style . '"></iframe>
175
                ';
176
                echo $diff;
177
            }
178
            return;
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
            return $this->member;
188
        }
189
        //Make temporary admin member
190
        $filter = ['Email' => self::get_user_email()];
191
        $this->member = Member::get()
192
            ->filter($filter)
193
            ->first();
194
        if ($this->member) {
195
        } else {
196
            $this->member = Member::create($filter);
197
        }
198
        $this->member->Password = self::get_password();
199
        $this->member->LockedOutUntil = null;
200
        $this->member->FirstName = 'Test';
201
        $this->member->Surname = 'User';
202
        $this->member->write();
203
        $auth = new MemberAuthenticator();
204
        $result = $auth->checkPassword($this->member, self::get_password());
205
        if (! $result->isValid()) {
206
            user_error('Error in creating test user.', E_USER_ERROR);
207
            return;
208
        }
209
210
        $adminGroup = DefaultAdminService::findOrCreateAdminGroup();
0 ignored issues
show
Bug Best Practice introduced by
The method SilverStripe\Security\De...indOrCreateAdminGroup() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
        /** @scrutinizer ignore-call */ 
211
        $adminGroup = DefaultAdminService::findOrCreateAdminGroup();
Loading history...
211
        if (! $adminGroup) {
0 ignored issues
show
introduced by
$adminGroup is of type SilverStripe\Security\Group, thus it always evaluated to true.
Loading history...
212
            user_error('No admin group exists', E_USER_ERROR);
213
            return;
214
        }
215
        $this->member->Groups()->add($adminGroup);
216
        if (Permission::checkMember($this->member, 'ADMIN')) {
217
            user_error('No admin group exists', E_USER_ERROR);
218
            return;
219
        }
220
221
        return $this->member;
222
    }
223
224
    /**
225
     * @return
226
     */
227
    protected function guzzleSendRequest($url, $type = 'GET')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

227
    protected function guzzleSendRequest($url, /** @scrutinizer ignore-unused */ $type = 'GET')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
228
    {
229
        $this->guzzleHasError = false;
230
        $credentials = base64_encode(self::get_user_email() . ':' . self::get_password());
231
        try {
232
            $response = $this->guzzleClient->request(
233
                'GET',
234
                $url,
235
                [
236
                    'cookies' => $this->guzzleCookieJar,
237
                    'headers' => [
238
                        'PHP_AUTH_USER' => self::get_user_email(),
239
                        'PHP_AUTH_PW' => self::get_password(),
240
                    ],
241
                    'auth' => [
242
                        self::get_user_email(),
243
                        self::get_password(),
244
                    ],
245
                    'Authorization' => ['Basic ' . $credentials],
246
                ]
247
            );
248
        } catch (RequestException $exception) {
249
            $this->rawResponse = $exception->getResponse();
250
            $this->guzzleHasError = true;
251
            //echo Psr7\str($exception->getRequest());
252
            if ($exception->hasResponse()) {
253
                $response = $exception->getResponse();
254
                $this->rawResponse = $exception->getResponseBodySummary($response);
255
            } else {
256
                $response = null;
257
            }
258
        }
259
        return $response;
260
    }
261
262
    /**
263
     * ECHOES the result of testing the URL....
264
     * @param string $url
265
     */
266
    private function testURL($url)
267
    {
268
        if (strlen(trim($url)) < 1) {
269
            user_error('empty url'); //Checks for empty strings.
270
        }
271
        if (AllLinks::is_admin_link($url)) {
272
            $validate = false;
273
        } else {
274
            $validate = Config::inst()->get(self::class, 'use_w3_validation');
275
        }
276
        $testURL = Director::absoluteURL('/templateoverviewloginandredirect/login/?BackURL=');
277
        $testURL .= urlencode($url);
278
        $this->guzzleSetup();
279
280
        $start = microtime(true);
281
        $response = $this->guzzleSendRequest($testURL);
282
        $end = microtime(true);
283
284
        $data = [
285
            'status' => 'success',
286
            'httpResponse' => '200',
287
            'content' => '',
288
            'responseTime' => round($end - $start, 4),
289
            'w3Content' => '',
290
        ];
291
292
        $httpResponse = $response->getStatusCode();
293
        $error = $response->getReasonPhrase();
294
        if ($this->guzzleHasError) {
295
            //we already have the body ...
296
        } else {
297
            $this->rawResponse = $response->getBody();
298
        }
299
300
        $data['httpResponse'] = $httpResponse;
301
302
        if ($httpResponse === '401') {
0 ignored issues
show
introduced by
The condition $httpResponse === '401' is always false.
Loading history...
303
            $data['status'] = 'error';
304
            $data['content'] = 'Could not access: ' . $url;
305
306
            return json_encode($data);
307
        }
308
309
        //uncaught errors ...
310
        if ($this->rawResponse && substr($this->rawResponse, 0, 12) === 'Fatal error') {
311
            $data['status'] = 'error';
312
            $data['content'] = $this->rawResponse;
313
        } elseif ($httpResponse === 200 && $this->rawResponse && strlen($this->rawResponse) < 500) {
314
            $data['status'] = 'error';
315
            $data['content'] = 'SHORT RESPONSE: ' . $this->rawResponse;
316
        }
317
318
        $data['w3Content'] = 'n/a';
319
320
        if ($httpResponse !== 200) {
321
            $data['status'] = 'error';
322
            $data['content'] .= 'unexpected response: ' . $error . $this->rawResponse;
323
        } else {
324
            $this->isSuccess = true;
325
            if ($validate) {
326
                $w3Obj = new W3cValidateApi();
327
                $data['w3Content'] = $w3Obj->W3Validate('', $this->rawResponse);
328
            }
329
        }
330
331
        if (Director::is_ajax()) {
332
            return json_encode($data);
333
        }
334
        $content = '';
335
        $content .= '<p><strong>URL:</strong> ' . $url . '</p>';
336
        $content .= '<p><strong>Status:</strong> ' . $data['status'] . '</p>';
337
        $content .= '<p><strong>HTTP response:</strong> ' . $data['httpResponse'] . '</p>';
338
        $content .= '<p><strong>Content:</strong> ' . htmlspecialchars($data['content']) . '</p>';
339
        $content .= '<p><strong>Response time:</strong> ' . $data['responseTime'] . '</p>';
340
        $content .= '<p><strong>W3 Content:</strong> ' . $data['w3Content'] . '</p>';
341
342
        return $content;
343
    }
344
345
    /**
346
     * creates the basic curl
347
     */
348
    private function guzzleSetup($type = 'GET')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

348
    private function guzzleSetup(/** @scrutinizer ignore-unused */ $type = 'GET')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
349
    {
350
        // $user_agent='Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0';
351
        // $post = $type == "GET" ? false : true;
352
        //
353
        // $strCookie = 'PHPSESSID=' . session_id() . '; path=/';
354
        // $options = array(
355
        //     CURLOPT_CUSTOMREQUEST  => $type,        //set request type post or get
356
        //     CURLOPT_POST           => $post,        //set to GET
357
        //     CURLOPT_USERAGENT      => $user_agent, //set user agent
358
        //     CURLOPT_COOKIE         => $strCookie, //set cookie file
359
        //     CURLOPT_COOKIEFILE     => "cookie.txt", //set cookie file
360
        //     CURLOPT_COOKIEJAR      => "cookie.txt", //set cookie jar
361
        //     CURLOPT_RETURNTRANSFER => true,     // return web page
362
        //     CURLOPT_HEADER         => false,    // don't return headers
363
        //     CURLOPT_FOLLOWLOCATION => true,     // follow redirects
364
        //     CURLOPT_ENCODING       => "",       // handle all encodings
365
        //     CURLOPT_AUTOREFERER    => true,     // set referer on redirect
366
        //     CURLOPT_CONNECTTIMEOUT => 120,      // timeout on connect
367
        //     CURLOPT_TIMEOUT        => 120,      // timeout on response
368
        //     CURLOPT_MAXREDIRS      => 10,       // stop after 10 redirects
369
        // );
370
        //
371
        // $this->ch = curl_init();
372
        //
373
        // curl_setopt_array($this->ch, $options);
374
375
        $this->guzzleCookieJar = new CookieJar();
376
        $this->guzzleClient = new Client(
377
            [
378
                'base_uri' => Director::baseURL(),
379
                'cookies' => true,
380
            ]
381
        );
382
    }
383
384
    private function deleteUser()
385
    {
386
        if (Config::inst()->get(self::class, 'use_default_admin')) {
387
            //do nothing;
388
        } else {
389
            if ($this->member) {
390
                $this->member->delete();
391
            }
392
        }
393
    }
394
395
    /**
396
     * cleans up the curl connection.
397
     */
398
    private function cleanup()
399
    {
400
    }
401
402
    // private function debugme($lineNumber, $variable = "")
403
    // {
404
    //     if ($this->debug) {
405
    //         echo "<br />" . $lineNumber . ": " . round(memory_get_usage() / 1048576) . "MB" . "=====" . print_r($variable, 1);
406
    //         ob_flush();
407
    //         flush();
408
    //     }
409
    // }
410
}
411