Passed
Push — 2.x ( 4a338d...daf3a4 )
by Terry
02:54
created

load_css()   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
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\ServerRequestInterface;
26
use Psr\Http\Message\ResponseInterface;
27
use Shieldon\Firewall\HttpFactory;
28
use Shieldon\Firewall\Utils\Collection;
29
use Shieldon\Firewall\Utils\Container;
30
31
use function explode;
32
use function file_exists;
33
use function func_get_arg;
34
use function func_num_args;
35
use function implode;
36
use function is_array;
37
use function is_null;
38
use function preg_split;
39
use function round;
40
use function shell_exec;
41
use function str_repeat;
42
use function str_replace;
43
use function stripos;
44
use function strtoupper;
45
use function substr;
46
use function sys_getloadavg;
47
use function trim;
48
use const PHP_OS;
49
50
/**
51
 * This value will be only displayed on Firewall Panel.
52
 */
53
define('SHIELDON_FIREWALL_VERSION', '2.0');
54
55
/**
56
 * The directory in where the static assets of the firewall panel are placed.
57
 */
58
define('SHIELDON_PANEL_ASSET_DIR', __DIR__ . '/../../assets/dist');
59
60
/**
61
 * Just use PSR-4 autoloader to load those helper functions.
62
 */
63
class Helpers
64
{
65
66
}
67
68
/**
69
 * Get user lang.
70
 * 
71
 * This method is a part of  __()
72
 *
73
 * @return string
74
 */
75
function get_user_lang(): string
76
{
77
    static $lang;
78
79
    if (!$lang) {
80
        $lang = 'en';
81
82
        // Fetch session variables.
83
        $session = get_session();
84
        $panelLang = $session->get('shieldon_panel_lang');
85
        $uiLang = $session->get('shieldon_ui_lang');
86
    
87
        if (!empty($panelLang)) {
88
            $lang = $panelLang;
89
        } elseif (!empty($uiLang)) {
90
            $lang = $uiLang;
91
        }
92
    }
93
94
    return $lang;
95
}
96
97
/**
98
 * Include i18n file.
99
 * 
100
 * This method is a part of  __()
101
 *
102
 * @param string $lang     The language code.
103
 * @param string $filename The i18n language pack file.
104
 *
105
 * @return void
106
 */
107
function include_i18n_file(string $lang, string $filename): array
108
{
109
    $content = [];
110
    $lang = str_replace('-', '_', $lang);
111
112
    if (stripos($lang, 'zh_') !== false) {
113
        if (stripos($lang, 'zh_CN') !== false) {
114
            $lang = 'zh_CN';
115
        } else {
116
            $lang = 'zh';
117
        }
118
    }
119
120
    $file = __DIR__ . '/../../localization/' . $lang . '/' . $filename . '.php';
121
    
122
    if (file_exists($file)) {
123
        $content = include $file;
124
    }
125
126
    return $content;
127
}
128
129
/**
130
 * Get locale message.
131
 *
132
 * @return string
133
 */
134
function __(): string
135
{
136
    /**
137
     * Load locale string from i18n files and store them into this array 
138
     * for further use.
139
     * 
140
     * @var array 
141
     */
142
    static $i18n;
143
144
    /**
145
     * Check the file exists for not.
146
     *
147
     * @var array 
148
     */
149
    static $fileChecked;
150
151
    $num = func_num_args();
152
153
    $filename    = func_get_arg(0); // required.
154
    $langcode    = func_get_arg(1); // required.
155
    $placeholder = ($num > 2) ? func_get_arg(2) : '';
156
    $replacement = ($num > 3) ? func_get_arg(3) : [];
157
    $lang        = get_user_lang();
158
159
    if (empty($i18n[$filename]) && empty($fileChecked[$filename])) {
160
        $fileChecked[$filename] = true;
161
        $i18n[$filename] = include_i18n_file($lang, $filename);
162
    }
163
164
    // If we don't get the string from the localization file, use placeholder 
165
    // instead.
166
    $resultString = $placeholder;
167
168
    if (!empty($i18n[$filename][$langcode])) {
169
        $resultString = $i18n[$filename][$langcode];
170
    }
171
172
    if (is_array($replacement)) { 
173
        /**
174
         * Example:
175
         *     __('test', 'example_string', 'Search results: {0} items. Total items: {1}.', [5, 150]);
176
         * 
177
         * Result:
178
         *     Search results: 5 items. Total items: 150.
179
         */
180
        foreach ($replacement as $i => $r) {
181
            $resultString = str_replace('{' . $i . '}', $replacement[$i], $resultString);
182
        }
183
    }
184
    
185
    return str_replace("'", '’', $resultString);
186
}
187
188
/**
189
 * Echo string from __()
190
 *
191
 * @return void
192
 */
193
function _e(): void
194
{
195
    $num = func_num_args();
196
197
    $filename    = func_get_arg(0); // required.
198
    $langcode    = func_get_arg(1); // required.
199
    $placeholder = ($num > 2) ? func_get_arg(2) : '';
200
    $replacement = ($num > 3) ? func_get_arg(3) : [];
201
202
    echo __($filename, $langcode, $placeholder, $replacement);
203
}
204
205
/**
206
 * Mask strings with asterisks.
207
 *
208
 * @param string $str The text.
209
 *
210
 * @return string
211
 */
212
function mask_string($str): string
213
{
214
    if (filter_var($str, FILTER_VALIDATE_IP) !== false) {
215
        $tmp = explode('.', $str);
216
        $tmp[0] = '*';
217
        $tmp[1] = '*';
218
        $masked = implode('.', $tmp);
219
220
    } else {
221
        $masked =  str_repeat('*', strlen($str) - 6) . substr($str, -6);
222
    }
223
224
    return $masked;
225
}
226
227
/**
228
 * Get current CPU usage information.
229
 *
230
 * This function is only used in sending notifications and it is unavailable 
231
 * on Win system. If you are using shared hosting and your hosting provider 
232
 * has disabled `sys_getloadavg` and `shell_exec`, it won't work either.
233
 *
234
 * @return string
235
 */
236
function get_cpu_usage(): string
237
{
238
    $return = '';
239
240
    // This feature is not available on Windows platform.
241
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
242
        return $return;
243
    }
244
245
    $cpuLoads = @sys_getloadavg();
246
    $cpuCores = trim(@shell_exec("grep -P '^processor' /proc/cpuinfo|wc -l"));
247
248
    if (!empty($cpuCores) && !empty($cpuLoads)) {
249
        $return = round($cpuLoads[1] / ($cpuCores + 1) * 100, 0) . '%';
250
    }
251
    return $return;
252
}
253
254
/**
255
 * Get current RAM usage information. 
256
 *
257
 * If you are using shared hosting and your hosting provider has disabled 
258
 * `shell_exec`, this function may not work as expected.
259
 *
260
 * @return string
261
 */
262
function get_memory_usage(): string
263
{
264
    $return = '';
265
266
    // This feature is not available on Windows platform.
267
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
268
        return $return;
269
    }
270
271
    $freeResult = explode("\n", trim(@shell_exec('free')));
272
273
    if (!empty($freeResult)) {
274
        $parsed = preg_split("/[\s]+/", $freeResult[1]);
275
        $return = round($parsed[2] / $parsed[1] * 100, 0) . '%';
276
    }
277
    return $return;
278
}
279
280
/**
281
 * The default settings of Shieldon core.
282
 *
283
 * @return array
284
 */
285
function get_default_properties(): array
286
{
287
    return [
288
289
        'time_unit_quota' => [
290
            's' => 2,
291
            'm' => 10,
292
            'h' => 30,
293
            'd' => 60
294
        ],
295
296
        'time_reset_limit'       => 3600,
297
        'interval_check_referer' => 5,
298
        'interval_check_session' => 30,
299
        'limit_unusual_behavior' => [
300
            'cookie'  => 5,
301
            'session' => 5,
302
            'referer' => 10
303
        ],
304
305
        'cookie_name'         => 'ssjd',
306
        'cookie_domain'       => '',
307
        'cookie_value'        => '1',
308
        'display_online_info' => true,
309
        'display_user_info'   => false,
310
311
        /**
312
         * If you set this option enabled, Shieldon will record every CAPTCHA fails 
313
         * in a row, once that user have reached the limitation number, Shieldon will
314
         * put it as a blocked IP in rule table, until the new data cycle begins.
315
         * 
316
         * Once that user have been blocked, they are still access the warning page, 
317
         * it means that they are not humain for sure, so let's throw them into the 
318
         * system firewall and say goodbye to them forever.
319
         */
320
        'deny_attempt_enable' => [
321
            'data_circle'     => false,
322
            'system_firewall' => false,
323
        ],
324
        'deny_attempt_notify' => [
325
            'data_circle'     => false,
326
            'system_firewall' => false,
327
        ],
328
        'deny_attempt_buffer' => [
329
            'data_circle'     => 10,
330
            'system_firewall' => 10,
331
        ],
332
333
        /**
334
         * To prevent dropping social platform robots into iptables firewall, such 
335
         * as Facebook, Line and others who scrape snapshots from your web pages, 
336
         * you should adjust the values below to fit your needs. (unit: second)
337
         */
338
        'record_attempt_detection_period' => 5, // 5 seconds.
339
340
        // Reset the counter after n second.
341
        'reset_attempt_counter' => 1800, // 30 minutes.
342
343
        // System-layer firewall, ip6table service watches this folder to 
344
        // receive command created by Shieldon Firewall.
345
        'iptables_watching_folder' => '/tmp/',
346
    ];
347
}
348
349
/**
350
 * PSR-7 HTTP server request
351
 *
352
 * @return ServerRequestInterface
353
 */
354
function get_request(): ServerRequestInterface
355
{
356
    $request = Container::get('request');
357
358
    if (is_null($request)) {
359
        $request = HttpFactory::createRequest();
360
        Container::set('request', $request);
361
    }
362
363
    return $request;
364
}
365
366
/**
367
 * PSR-7 HTTP response.
368
 *
369
 * @return ResponseInterface
370
 */
371
function get_response(): ResponseInterface
372
{
373
    $response = Container::get('response');
374
375
    if (is_null($response)) {
376
        $response = HttpFactory::createResponse();
377
        Container::set('response', $response);
378
    }
379
380
    return $response;
381
}
382
383
/**
384
 * Session
385
 *
386
 * @return Collection
387
 */
388
function get_session(): Collection
389
{
390
    $session = Container::get('session');
391
392
    if (is_null($session)) {
393
        $session = HttpFactory::createSession();
394
        Container::set('session', $session);
395
    }
396
397
    return $session;
398
}
399
400
/**
401
 * Set a PSR-7 HTTP server request into container.
402
 *
403
 * @param ServerRequestInterface $request The PSR-7 server request.
404
 *
405
 * @return void
406
 */
407
function set_request(ServerRequestInterface $request): void
408
{
409
    Container::set('request', $request, true);
410
}
411
412
/**
413
 * Set a PSR-7 HTTP response into container.
414
 *
415
 * @param ResponseInterface $response The PSR-7 server response.
416
 *
417
 * @return void
418
 */
419
function set_response(ResponseInterface $response): void
420
{
421
    Container::set('response', $response, true);
422
}
423
424
/**
425
 * Unset cookie.
426
 *
427
 * @param string|null $name The name (key) in the array of the superglobal.
428
 *
429
 * @return void
430
 */
431
function unset_global_cookie($name = null): void
432
{
433
    if (empty($name)) {
434
        $cookieParams = get_request()->getCookieParams();
435
        set_request(get_request()->withCookieParams([]));
436
        foreach (array_keys($cookieParams) as $name) {
437
            set_response(
438
                get_response()->withHeader(
439
                    'Set-Cookie',
440
                    "$name=; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0"
441
                )
442
            );
443
        }
444
        $_COOKIE = [];
445
446
        return;
447
    }
448
449
    $cookieParams = get_request()->getCookieParams();
450
    unset($cookieParams[$name]);
451
452
    set_request(
453
        get_request()->withCookieParams(
454
            $cookieParams
455
        )
456
    );
457
458
    set_response(
459
        get_response()->withHeader(
460
            'Set-Cookie',
461
            "$name=; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0"
462
        )
463
    );
464
    // Prevent direct access to superglobal.
465
    unset($_COOKIE[$name]);
466
}
467
468
/**
469
 * Unset post.
470
 *
471
 * @param string|null $name The name (key) in the array of the superglobal.
472
 *
473
 * @return void
474
 */
475
function unset_global_post($name = null): void
476
{
477
    if (empty($name)) {
478
        set_request(get_request()->withParsedBody([]));
479
        $_POST = [];
480
481
        return;
482
    }
483
484
    $postParams = get_request()->getParsedBody();
485
    unset($postParams[$name]);
486
    set_request(get_request()->withParsedBody($postParams));
487
    unset($_POST[$name]);
488
}
489
490
/**
491
 * Unset get.
492
 *
493
 * @param string|null $name The name (key) in the array of the superglobal.
494
 *
495
 * @return void
496
 */
497
function unset_global_get($name = null): void
498
{
499
    if (empty($name)) {
500
        set_request(get_request()->withQueryParams([]));
501
        $_GET = [];
502
503
        return;
504
    }
505
506
    $getParams = get_request()->getQueryParams();
507
    unset($getParams[$name]);
508
    set_request(get_request()->withQueryParams($getParams));
509
    unset($_GET[$name]);
510
}
511
512
/**
513
 * Unset session.
514
 *
515
 * @param string|null $name The name (key) in the array of the superglobal.
516
 *
517
 * @return void
518
 */
519
function unset_global_session($name = null): void
520
{
521
    if (empty($name)) {
522
        get_session()->clear();
523
        $_SESSION = [];
524
525
        return;
526
    }
527
528
    get_session()->remove($name);
529
    unset($_SESSION[$name]);
530
}
531
532
/**
533
 * Unset a variable of superglobal.
534
 *
535
 * @param string|null $name The name (key) in the array of the superglobal.
536
 *                          If $name is null that means clear all.
537
 * @param string      $type The type of the superglobal.
538
 *
539
 * @return void
540
 */
541
function unset_superglobal($name, string $type): void
542
{
543
    $types = [
544
        'get',
545
        'post',
546
        'cookie',
547
        'session',
548
    ];
549
550
    if (!in_array($type, $types)) {
551
        return;
552
    }
553
554
    $method = '\Shieldon\Firewall\unset_global_' . $type;
555
    $method($name, $type);
556
}
557
558
/**
559
 * Load CSS.
560
 *
561
 * @return void
562
 */
563
function load_css(): void
564
{
565
    include SHIELDON_PANEL_ASSET_DIR . '/app-packed.css';
566
}
567
568
/**
569
 * Load JS.
570
 *
571
 * @return void
572
 */
573
function load_js(): void
574
{
575
    include SHIELDON_PANEL_ASSET_DIR . '/app-packed.js';
576
}