Passed
Push — 2.x ( b32285...d40732 )
by Terry
01:54
created

Kernel::setResultCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall;
24
25
use Psr\Http\Message\ResponseInterface;
26
use Psr\Http\Message\ServerRequestInterface;
27
use Shieldon\Firewall\Captcha\Foundation;
28
use Shieldon\Firewall\Helpers;
29
use Shieldon\Firewall\HttpFactory;
30
use Shieldon\Firewall\IpTrait;
31
use Shieldon\Firewall\Kernel\CaptchaTrait;
32
use Shieldon\Firewall\Kernel\ComponentTrait;
33
use Shieldon\Firewall\Kernel\DriverTrait;
34
use Shieldon\Firewall\Kernel\FilterTrait;
35
use Shieldon\Firewall\Kernel\MessengerTrait;
36
use Shieldon\Firewall\Kernel\RuleTrait;
37
use Shieldon\Firewall\Kernel\SessionTrait;
38
use Shieldon\Firewall\Log\ActionLogger;
39
use Shieldon\Firewall\Utils\Container;
40
use function Shieldon\Firewall\get_default_properties;
41
use function Shieldon\Firewall\get_request;
42
use function Shieldon\Firewall\get_response;
43
use function Shieldon\Firewall\get_session;
44
45
use Closure;
46
use InvalidArgumentException;
47
use RuntimeException;
48
use function file_exists;
49
use function get_class;
50
use function gethostbyaddr;
51
use function is_dir;
52
use function ob_end_clean;
53
use function ob_get_contents;
54
use function ob_start;
55
use function strpos;
56
use function strrpos;
57
use function substr;
58
use function time;
59
60
/**
61
 * The primary Shiendon class.
62
 */
63
class Kernel
64
{
65
    /**
66
     *   Public methods       | Desctiotion
67
     *  ----------------------|---------------------------------------------
68
     *   ban                  | Ban an IP.
69
     *   getCurrentUrl        | Get current user's browsing path.
70
     *   getJavascript        | Print a JavaScript snippet in the pages.
71
     *   managedBy            | Used on testing purpose.
72
     *   respond              | Respond the result.
73
     *   run                  | Run the checking process.
74
     *   setClosure           | Set a closure function.
75
     *   setDialog            | Customize the dialog UI.
76
     *   setExcludedUrls      | Set the URLs you want them excluded them from protection.
77
     *   setLogger            | Set the action log logger.
78
     *   setProperties        | Set the property settings.
79
     *   setProperty          | Set a property setting.
80
     *   setStrict            | Strict mode apply to all components.
81
     *   setTemplateDirectory | Set the frontend template directory.
82
     *   unban                | Unban an IP.
83
     *  ----------------------|---------------------------------------------
84
     */
85
86
    /**
87
     *   Public methods       | Desctiotion
88
     *  ----------------------|---------------------------------------------
89
     *   setIp                | Ban an IP.
90
     *   getIp                | Get current user's browsing path.
91
     *   setRdns              | Print a JavaScript snippet in the pages.
92
     *   getRdns              | Used on testing purpose.
93
     *  ----------------------|---------------------------------------------
94
     */
95
    use CaptchaTrait;
96
97
    /**
98
     *   Public methods       | Desctiotion
99
     *  ----------------------|---------------------------------------------
100
     *   setComponent         | Set a commponent.
101
     *   getComponent         | Get a component instance from component's container.
102
     *   disableComponents    | Disable all components.
103
     *  ----------------------|---------------------------------------------
104
     */
105
    use ComponentTrait;
106
107
    /**
108
     *   Public methods       | Desctiotion
109
     *  ----------------------|---------------------------------------------
110
     *   setDriver            | Set a data driver.
111
     *   setChannel           | Set a data channel.
112
     *   disableDbBuilder     | disable creating data tables.
113
     *  ----------------------|---------------------------------------------
114
     */
115
    use DriverTrait;
116
117
    /**
118
     *   Public methods       | Desctiotion
119
     *  ----------------------|---------------------------------------------
120
     *   setFilters           | Set the filters.
121
     *   setFilter            | Set a filter.
122
     *   disableFilters       | Disable all filters.
123
     *  ----------------------|---------------------------------------------
124
     */
125
    use FilterTrait;
126
127
    /**
128
     *   Public methods       | Desctiotion
129
     *  ----------------------|---------------------------------------------
130
     *   setIp                | Set an IP address.
131
     *   getIp                | Get current set IP.
132
     *   setRdns              | Set a RDNS record for the check.
133
     *   getRdns              | Get IP resolved hostname.
134
     *  ----------------------|---------------------------------------------
135
     */
136
    use IpTrait;
137
138
    /**
139
     *   Public methods       | Desctiotion
140
     *  ----------------------|---------------------------------------------
141
     *   setMessenger         | Set a messenger
142
     *  ----------------------|---------------------------------------------
143
     */
144
    use MessengerTrait;
145
146
    /**
147
     *   Public methods       | Desctiotion
148
     *  ----------------------|---------------------------------------------
149
     *                        |  
150
     *  ----------------------|---------------------------------------------
151
     */
152
    use RuleTrait;
153
154
    /**
155
     *   Public methods       | Desctiotion
156
     *  ----------------------|---------------------------------------------
157
     *   limitSession         | Limit the amount of the online users.
158
     *   getSessionCount      | Get the amount of the sessions.
159
     *  ----------------------|---------------------------------------------
160
     */
161
    use SessionTrait;
162
163
    /**
164
     * HTTP Status Codes
165
     */
166
    const HTTP_STATUS_OK                 = 200;
167
    const HTTP_STATUS_SEE_OTHER          = 303;
168
    const HTTP_STATUS_BAD_REQUEST        = 400;
169
    const HTTP_STATUS_FORBIDDEN          = 403;
170
    const HTTP_STATUS_TOO_MANY_REQUESTS  = 429;
171
172
    /**
173
     * Reason Codes (ALLOW)
174
     */
175
    const REASON_IS_SEARCH_ENGINE        = 100;
176
    const REASON_IS_GOOGLE               = 101;
177
    const REASON_IS_BING                 = 102;
178
    const REASON_IS_YAHOO                = 103;
179
    const REASON_IS_SOCIAL_NETWORK       = 110;
180
    const REASON_IS_FACEBOOK             = 111;
181
    const REASON_IS_TWITTER              = 112;
182
183
    /**
184
     * Reason Codes (DENY)
185
     */
186
    const REASON_TOO_MANY_SESSIONS       = 1;
187
    const REASON_TOO_MANY_ACCESSES       = 2; // (not used)
188
    const REASON_EMPTY_JS_COOKIE         = 3;
189
    const REASON_EMPTY_REFERER           = 4;
190
    const REASON_REACHED_LIMIT_DAY       = 11;
191
    const REASON_REACHED_LIMIT_HOUR      = 12;
192
    const REASON_REACHED_LIMIT_MINUTE    = 13;
193
    const REASON_REACHED_LIMIT_SECOND    = 14;
194
    const REASON_INVALID_IP              = 40;
195
    const REASON_DENY_IP                 = 41;
196
    const REASON_ALLOW_IP                = 42;
197
    const REASON_COMPONENT_IP            = 81;
198
    const REASON_COMPONENT_RDNS          = 82;
199
    const REASON_COMPONENT_HEADER        = 83;
200
    const REASON_COMPONENT_USERAGENT     = 84;
201
    const REASON_COMPONENT_TRUSTED_ROBOT = 85;
202
    const REASON_MANUAL_BAN              = 99;
203
204
    /**
205
     * Action Codes
206
     */
207
    const ACTION_DENY                    = 0;
208
    const ACTION_ALLOW                   = 1;
209
    const ACTION_TEMPORARILY_DENY        = 2;
210
    const ACTION_UNBAN                   = 9;
211
212
    /**
213
     * Result Codes
214
     */
215
    const RESPONSE_DENY                  = 0;
216
    const RESPONSE_ALLOW                 = 1;
217
    const RESPONSE_TEMPORARILY_DENY      = 2;
218
    const RESPONSE_LIMIT_SESSION         = 3;
219
220
    /**
221
     * Logger Codes
222
     */
223
    const LOG_LIMIT                      = 3;
224
    const LOG_PAGEVIEW                   = 11;
225
    const LOG_BLACKLIST                  = 98;
226
    const LOG_CAPTCHA                    = 99;
227
228
    const KERNEL_DIR = __DIR__;
229
230
    /**
231
     * The result passed from filters, compoents, etc.
232
     * 
233
     * DENY    : 0
234
     * ALLOW   : 1
235
     * CAPTCHA : 2
236
     *
237
     * @var int
238
     */
239
    protected $result = 1;
240
241
    /**
242
     * Default settings
243
     *
244
     * @var array
245
     */
246
    protected $properties = [];
247
248
    /**
249
     * Logger instance.
250
     *
251
     * @var ActionLogger
252
     */
253
    public $logger;
254
255
    /**
256
     * The closure functions that will be executed in this->run()
257
     *
258
     * @var array
259
     */
260
    protected $closures = [];
261
262
    /**
263
     * URLs that are excluded from Shieldon's protection.
264
     *
265
     * @var array
266
     */
267
    protected $excludedUrls = [];
268
269
    /**
270
     * Custom dialog UI settings.
271
     *
272
     * @var array
273
     */
274
    protected $dialog = [];
275
276
    /**
277
     * Strict mode.
278
     * 
279
     * Set by `strictMode()` only. The default value of this propertry is undefined.
280
     *
281
     * @var bool|null
282
     */
283
    protected $strictMode;
284
285
    /**
286
     * The directory in where the frontend template files are placed.
287
     *
288
     * @var string
289
     */
290
    protected $templateDirectory = '';
291
292
    /**
293
     * Which type of configuration source that Shieldon firewall managed?
294
     * value: managed | config | self | demo
295
     *
296
     * @var string
297
     */
298
    protected $firewallType = 'self'; 
299
300
    /**
301
     * Shieldon constructor.
302
     *
303
     * @param ServerRequestInterface|null $request  A PSR-7 server request.
304
     * @param ResponseInterface|null      $response A PSR-7 server response.
305
     *
306
     * @return void
307
     */
308
    public function __construct(?ServerRequestInterface $request = null, ?ResponseInterface $response = null)
309
    {
310
        // Load helper functions. This is the must.
311
        new Helpers();
312
313
        $request = $request ?? HttpFactory::createRequest();
314
        $response = $response ?? HttpFactory::createResponse();
315
        $session = HttpFactory::createSession();
316
317
        $this->properties = get_default_properties();
318
        $this->setCaptcha(new Foundation());
319
320
        Container::set('request', $request);
321
        Container::set('response', $response);
322
        Container::set('session', $session);
323
        Container::set('shieldon', $this);
324
    }
325
326
    /**
327
     * Run, run, run!
328
     *
329
     * Check the rule tables first, if an IP address has been listed.
330
     * Call function filter() if an IP address is not listed in rule tables.
331
     *
332
     * @return int
333
     */
334
    public function run(): int
335
    {
336
        $this->assertDriver();
337
338
        // Ignore the excluded urls.
339
        foreach ($this->excludedUrls as $url) {
340
            if (strpos($this->getCurrentUrl(), $url) === 0) {
341
                return $this->result = self::RESPONSE_ALLOW;
342
            }
343
        }
344
345
        // Execute closure functions.
346
        foreach ($this->closures as $closure) {
347
            $closure();
348
        }
349
350
        $result = $this->process();
351
352
        if ($result !== self::RESPONSE_ALLOW) {
353
354
            // Current session did not pass the CAPTCHA, it is still stuck in 
355
            // CAPTCHA page.
356
            $actionCode = self::LOG_CAPTCHA;
357
358
            // If current session's respone code is RESPONSE_DENY, record it as 
359
            // `blacklist_count` in our logs.
360
            // It is stuck in warning page, not CAPTCHA.
361
            if ($result === self::RESPONSE_DENY) {
362
                $actionCode = self::LOG_BLACKLIST;
363
            }
364
365
            if ($result === self::RESPONSE_LIMIT_SESSION) {
366
                $actionCode = self::LOG_LIMIT;
367
            }
368
369
            $this->log($actionCode);
370
371
        } else {
372
373
            $this->log(self::LOG_PAGEVIEW);
374
        }
375
376
        // @ MessengerTrait
377
        $this->triggerMessengers();
378
379
        return $result;
380
    }
381
382
    /**
383
     * Respond the result.
384
     *
385
     * @return ResponseInterface
386
     */
387
    public function respond(): ResponseInterface
388
    {
389
        $response = get_response();
390
        $type = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $type is dead and can be removed.
Loading history...
391
392
        $httpStatusCodes = [
393
            self::RESPONSE_TEMPORARILY_DENY => [
394
                'type' => 'captcha',
395
                'code' => self::HTTP_STATUS_FORBIDDEN,
396
            ],
397
398
            self::RESPONSE_LIMIT_SESSION => [
399
                'type' => 'session_limitation',
400
                'code' => self::HTTP_STATUS_TOO_MANY_REQUESTS,
401
            ],
402
403
            self::RESPONSE_DENY => [
404
                'type' => 'rejection',
405
                'code' => self::HTTP_STATUS_BAD_REQUEST,
406
            ],
407
        ];
408
409
        // Nothing happened. Return.
410
        if (empty($httpStatusCodes[$this->result])) {
411
            return $response;
412
        }
413
414
        $type = $httpStatusCodes[$this->result]['type'];
415
        $statusCode = $httpStatusCodes[$this->result]['code'];
416
417
        $viewPath = $this->getTemplate($type);
418
419
        // The language of output UI. It is used on views.
420
        $langCode = get_session()->get('shieldon_ui_lang') ?? 'en';
421
422
        $showOnlineInformation = false;
423
        $showUserInformation = false;
424
        
425
        // Show online session count. It is used on views.
426
        if (!empty($this->properties['display_online_info'])) {
427
            $showOnlineInformation = true;
428
            $onlineinfo['queue'] = $this->sessionStatus['queue'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$onlineinfo was never initialized. Although not strictly required by PHP, it is generally a good practice to add $onlineinfo = array(); before regardless.
Loading history...
429
            $onlineinfo['count'] = $this->sessionStatus['count'];
430
            $onlineinfo['period'] = $this->sessionLimit['period'];
431
        } 
432
433
        // Show user information such as IP, user-agent, device name.
434
        if (!empty($this->properties['display_user_info'])) {
435
            $showUserInformation = true;
436
            $dialoguserinfo['ip'] = $this->ip;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$dialoguserinfo was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dialoguserinfo = array(); before regardless.
Loading history...
437
            $dialoguserinfo['rdns'] = $this->rdns;
438
            $dialoguserinfo['user_agent'] = get_request()->getHeaderLine('user-agent');
439
        }
440
441
        // Captcha form
442
        $form = $this->getCurrentUrl();
443
        $captchas = $this->captcha;
444
445
        $ui = [
446
            'background_image' => '',
447
            'bg_color'         => '#ffffff',
448
            'header_bg_color'  => '#212531',
449
            'header_color'     => '#ffffff',
450
            'shadow_opacity'   => '0.2',
451
        ];
452
453
        foreach (array_keys($ui) as $key) {
454
            if (!empty($this->dialog[$key])) {
455
                $ui[$key] = $this->dialog[$key];
456
            }
457
        }
458
459
        if (!defined('SHIELDON_VIEW')) {
460
            define('SHIELDON_VIEW', true);
461
        }
462
463
        $css = include $this->getTemplate('css/default');
464
465
        ob_start();
466
        include $viewPath;
467
        $output = ob_get_contents();
468
        ob_end_clean();
469
470
        // Remove unused variable notices generated from PHP intelephense.
471
        unset(
472
            $css,
473
            $ui,
474
            $form,
475
            $captchas,
476
            $csrf,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $csrf seems to be never defined.
Loading history...
477
            $langCode,
478
            $showOnlineInformation,
479
            $showUserInformation
480
        );
481
482
        $stream = HttpFactory::createStream();
483
        $stream->write($output);
484
        $stream->rewind();
485
486
        return $response
487
            ->withHeader('X-Protected-By', 'shieldon.io')
488
            ->withBody($stream)
489
            ->withStatus($statusCode);
490
    }
491
492
    /**
493
     * Ban an IP.
494
     *
495
     * @param string $ip A valid IP address.
496
     *
497
     * @return void
498
     */
499
    public function ban(string $ip = ''): void
500
    {
501
        if ('' === $ip) {
502
            $ip = $this->ip;
503
        }
504
 
505
        $this->action(
506
            self::ACTION_DENY,
507
            self::REASON_MANUAL_BAN,
508
            $ip
509
        );
510
    }
511
512
    /**
513
     * Unban an IP.
514
     *
515
     * @param string $ip A valid IP address.
516
     *
517
     * @return void
518
     */
519
    public function unban(string $ip = ''): void
520
    {
521
        if ($ip === '') {
522
            $ip = $this->ip;
523
        }
524
525
        $this->action(
526
            self::ACTION_UNBAN,
527
            self::REASON_MANUAL_BAN,
528
            $ip
529
        );
530
        $this->log(self::ACTION_UNBAN);
531
532
        $this->result = self::RESPONSE_ALLOW;
533
    }
534
535
    /**
536
     * Set a property setting.
537
     *
538
     * @param string $key   The key of a property setting.
539
     * @param mixed  $value The value of a property setting.
540
     *
541
     * @return void
542
     */
543
    public function setProperty(string $key = '', $value = '')
544
    {
545
        if (isset($this->properties[$key])) {
546
            $this->properties[$key] = $value;
547
        }
548
    }
549
550
    /**
551
     * Set the property settings.
552
     * 
553
     * @param array $settings The settings.
554
     *
555
     * @return void
556
     */
557
    public function setProperties(array $settings): void
558
    {
559
        foreach (array_keys($this->properties) as $k) {
560
            if (isset($settings[$k])) {
561
                $this->properties[$k] = $settings[$k];
562
            }
563
        }
564
    }
565
566
    /**
567
     * Strict mode.
568
     * This option will take effects to all components.
569
     * 
570
     * @param bool $bool Set true to enble strict mode, false to disable it overwise.
571
     *
572
     * @return void
573
     */
574
    public function setStrict(bool $bool)
575
    {
576
        $this->strictMode = $bool;
577
    }
578
579
    /**
580
     * Set an action log logger.
581
     *
582
     * @param ActionLogger $logger Record action logs for users.
583
     *
584
     * @return void
585
     */
586
    public function setLogger(ActionLogger $logger): void
587
    {
588
        $this->logger = $logger;
589
    }
590
591
    /**
592
     * Set the URLs you want them excluded them from protection.
593
     *
594
     * @param array $urls The list of URL want to be excluded.
595
     *
596
     * @return void
597
     */
598
    public function setExcludedUrls(array $urls = []): void
599
    {
600
        $this->excludedUrls = $urls;
601
    }
602
603
    /**
604
     * Set a closure function.
605
     *
606
     * @param string  $key     The name for the closure class.
607
     * @param Closure $closure An instance will be later called.
608
     *
609
     * @return void
610
     */
611
    public function setClosure(string $key, Closure $closure): void
612
    {
613
        $this->closures[$key] = $closure;
614
    }
615
616
    /**
617
     * Customize the dialog UI.
618
     * 
619
     * @param array $settings The dialog UI settings.
620
     *
621
     * @return void
622
     */
623
    public function setDialog(array $settings): void
624
    {
625
        $this->dialog = $settings;
626
    }
627
628
    /**
629
     * Set the frontend template directory.
630
     *
631
     * @param string $directory The directory in where the template files are placed.
632
     *
633
     * @return void
634
     */
635
    public function setTemplateDirectory(string $directory)
636
    {
637
        if (!is_dir($directory)) {
638
            throw new InvalidArgumentException(
639
                'The template directory does not exist.'
640
            );
641
        }
642
        $this->templateDirectory = $directory;
643
    }
644
645
    /**
646
     * Print a JavaScript snippet in your webpages.
647
     * 
648
     * This snippet generate cookie on client's browser,then we check the 
649
     * cookie to identify the client is a rebot or not.
650
     *
651
     * @return string
652
     */
653
    public function getJavascript(): string
654
    {
655
        $tmpCookieName = $this->properties['cookie_name'];
656
        $tmpCookieDomain = $this->properties['cookie_domain'];
657
658
        if (empty($tmpCookieDomain) && get_request()->getHeaderLine('host')) {
659
            $tmpCookieDomain = get_request()->getHeaderLine('host');
660
        }
661
662
        $tmpCookieValue = $this->properties['cookie_value'];
663
664
        $jsString = '
665
            <script>
666
                var d = new Date();
667
                d.setTime(d.getTime()+(60*60*24*30));
668
                document.cookie = "' . $tmpCookieName . '=' . $tmpCookieValue . ';domain=.' . $tmpCookieDomain . ';expires="+d.toUTCString();
669
            </script>
670
        ';
671
672
        return $jsString;
673
    }
674
675
    /**
676
     * Get current visior's path.
677
     *
678
     * @return string
679
     */
680
    public function getCurrentUrl(): string
681
    {
682
        return get_request()->getUri()->getPath();
683
    }
684
685
    /**
686
     * Displayed on Firewall Panel, telling you current what type of 
687
     * configuration is used.
688
     * 
689
     * @param string $type The type of configuration.
690
     *                     accepted value: demo | managed | config
691
     *
692
     * @return void
693
     */
694
    public function managedBy(string $type = ''): void
695
    {
696
        if (in_array($type, ['managed', 'config', 'demo'])) {
697
            $this->firewallType = $type;
698
        }
699
    }
700
701
    /*
702
    |-------------------------------------------------------------------
703
    | Non-public methids.
704
    |-------------------------------------------------------------------
705
    */
706
707
    /**
708
     * Run, run, run!
709
     *
710
     * Check the rule tables first, if an IP address has been listed.
711
     * Call function filter() if an IP address is not listed in rule tables.
712
     *
713
     * @return int The response code.
714
     */
715
    protected function process(): int
716
    {
717
        $this->driver->init($this->isCreateDatabase);
718
719
        $this->initComponents();
720
721
        $processMethods = [
722
            'isRuleExist',   // Stage 1 - Looking for rule table.
723
            'isTrustedBot',  // Stage 2 - Detect popular search engine.
724
            'isFakeRobot',   // Stage 3 - Reject fake search engine crawlers.
725
            'isIpComponent', // Stage 4 - IP manager.
726
            'isComponents'   // Stage 5 - Check other components.
727
        ];
728
729
        foreach ($processMethods as $method) {
730
            if ($this->{$method}()) {
731
                return $this->result;
732
            }
733
        }
734
735
        // Stage 6 - Check filters if set.
736
        if (array_search(true, $this->filterStatus)) {
737
            return $this->result = $this->sessionHandler($this->filter());
738
        }
739
740
        // Stage 7 - Go into session limit check.
741
        return $this->result = $this->sessionHandler(self::RESPONSE_ALLOW);
742
    }
743
744
    /**
745
     * Start an action for this IP address, allow or deny, and give a reason for it.
746
     *
747
     * @param int    $actionCode The action code. - 0: deny, 1: allow, 9: unban.
748
     * @param string $reasonCode The response code.
749
     * @param string $assignIp   The IP address.
750
     * 
751
     * @return void
752
     */
753
    protected function action(
754
        int    $actionCode,
755
        int    $reasonCode,
756
        string $assignIp = ''
757
    ): void {
758
759
        $ip = $this->ip;
760
        $rdns = $this->rdns;
761
        $now = time();
762
        $logData = [];
763
    
764
        if ('' !== $assignIp) {
765
            $ip = $assignIp;
766
            $rdns = gethostbyaddr($ip);
767
        }
768
769
        if ($actionCode === self::ACTION_UNBAN) {
770
            $this->driver->delete($ip, 'rule');
771
        } else {
772
            $logData['log_ip']     = $ip;
773
            $logData['ip_resolve'] = $rdns;
774
            $logData['time']       = $now;
775
            $logData['type']       = $actionCode;
776
            $logData['reason']     = $reasonCode;
777
            $logData['attempts']   = 0;
778
779
            $this->driver->save($ip, $logData, 'rule');
780
        }
781
782
        // Remove logs for this IP address because It already has it's own rule on system.
783
        // No need to count for it anymore.
784
        $this->driver->delete($ip, 'filter');
785
786
        // Log this action.
787
        $this->log($actionCode, $ip);
788
    }
789
790
    /**
791
     * Log actions.
792
     *
793
     * @param int    $actionCode The code number of the action.
794
     * @param string $ip         The IP address.
795
     *
796
     * @return void
797
     */
798
    protected function log(int $actionCode, $ip = ''): void
799
    {
800
        if (!$this->logger) {
801
            return;
802
        }
803
804
        $logData = [];
805
        $logData['ip'] = $ip ?? $this->getIp();
806
        $logData['session_id'] = get_session()->get('id');
807
        $logData['action_code'] = $actionCode;
808
        $logData['timesamp'] = time();
809
810
        $this->logger->add($logData);
811
    }
812
813
    /**
814
     * Get a template PHP file.
815
     *
816
     * @param string $type The template type.
817
     *
818
     * @return string
819
     */
820
    protected function getTemplate(string $type): string
821
    {
822
        $directory = self::KERNEL_DIR . '/../../templates/frontend';
823
824
        if (!empty($this->templateDirectory)) {
825
            $directory = $this->templateDirectory;
826
        }
827
828
        $path = $directory . '/' . $type . '.php';
829
830
        if (!file_exists($path)) {
831
            throw new RuntimeException(
832
                sprintf(
833
                    'The templeate file is missing. (%s)',
834
                    $path
835
                )
836
            );
837
        }
838
839
        return $path;
840
    }
841
842
    /**
843
     * Get a class name without namespace string.
844
     *
845
     * @param object $instance Class
846
     * 
847
     * @return void
848
     */
849
    protected function getClassName($instance): string
850
    {
851
        $class = get_class($instance);
852
        return substr($class, strrpos($class, '\\') + 1); 
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr($class, strrpos($class, '\') + 1) returns the type string which is incompatible with the documented return type void.
Loading history...
853
    }
854
855
    /**
856
     * Save and return the result identifier.
857
     * This method is for passing value from traits.
858
     *
859
     * @param int $resultCode The result identifier.
860
     *
861
     * @return int
862
     */
863
    protected function setResultCode(int $resultCode): int
864
    {
865
        return $this->result = $resultCode;
866
    }
867
}
868