Passed
Push — master ( a2fae3...4fc126 )
by Tomasz
08:32 queued 03:54
created

SanityTests   D

Complexity

Total Complexity 119

Size/Duplication

Total Lines 650
Duplicated Lines 13.23 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 86
loc 650
rs 4.6666
c 0
b 0
f 0
wmc 119
lcom 1
cbo 4

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A test() 0 11 2
A run_tests() 0 14 4
A get_test_names() 0 10 3
A test_return() 0 5 1
A get_exec_path() 0 19 4
A php_test() 0 7 2
A cat_base_url_test() 0 10 4
A ssp_test() 0 7 2
A security_test() 0 5 2
A zip_test() 5 7 2
A eapol_test_test() 5 13 3
A logdir_test() 0 7 2
C phpModules_test() 0 37 7
C geoip_test() 8 61 13
A openssl_test() 5 13 3
D makensis_test() 11 29 9
C NSISmodules_test() 0 29 7
B directories_test() 0 29 4
A locales_test() 5 14 4
F defaults_test() 5 43 14
C databases_test() 30 40 8
C device_cache_test() 12 37 13
B mailer_test() 0 28 4
A UDPhosts_test() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SanityTests often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SanityTests, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
/**
13
 * 
14
 * 
15
 * This is the definition of the CAT class implementing various configuration
16
 * tests. 
17
 * Each test is implemented as a priviate method which needs to be named "test_name_test".
18
 * The test returns the results by calling the test_return method, this passing the return
19
 * code and the explanatory message. Multiple calls to test_return are allowed.
20
 *
21
 * An individual test can be run by the "test" method which takes the test name as an argument
22
 * multiple tests should be run by the run_all_tests method which takes an array as an argument
23
 * see method descriptions for more information.
24
 * 
25
 * The results of the tests are passed within the $test_result array
26
 *
27
 * Some configuration of this class is required, see further down.
28
 * @author Stefan Winter <[email protected]>
29
 * @author Tomasz Wolniewicz <[email protected]>
30
 *
31
 * @license see LICENSE file in root directory
32
 *
33
 * @package Utilities
34
 */
35
36
namespace core;
37
38
use GeoIp2\Database\Reader;
39
use \Exception;
40
41
require_once(dirname(dirname(__FILE__)) . "/config/_config.php");
42
require_once(dirname(dirname(__FILE__)) . "/core/PHPMailer/src/PHPMailer.php");
43
require_once(dirname(dirname(__FILE__)) . "/core/PHPMailer/src/SMTP.php");
44
45
class SanityTests extends CAT {
46
    /* in this section set current CAT requirements */
47
48
    /* $php_needversion sets the minumum required php version */
49
50
    // because of bug:
51
    // Fixed bug #74005 (mail.add_x_header causes RFC-breaking lone line feed).
52
    private $php_needversion = '7.0.17';
53
54
    /* List all required NSIS modules below */
55
    private $NSIS_Modules = [
56
        "nsArray.nsh",
57
        "FileFunc.nsh",
58
        "LogicLib.nsh",
59
        "WordFunc.nsh",
60
        "FileFunc.nsh",
61
        "x64.nsh",
62
    ];
63
64
    /* set $profile_option_ct to the number of rows returned by "SELECT * FROM profile_option_dict" */
65
    private $profile_option_ct = 33;
66
    /* set $view_admin_ct to the number of rows returned by "desc view_admin" */
67
    private $view_admin_ct = 8;
68
69
    /* end of config */
70
    public $out;
71
    public $name;
72
73
    public function __construct() {
74
        parent::__construct();
75
        $this->test_result = [];
76
        $this->test_result['global'] = 0;
77
    }
78
79
    /**
80
     * The single test wrapper
81
     * @param string $test the test name
82
     */
83
    public function test($test) {
84
        $this->out[$test] = [];
85
        $this->name = $test;
86
        $m_name = $test . '_test';
87
        $this->test_result[$test] = 0;
88
        if (!method_exists($this, $m_name)) {
89
            $this->test_return(\core\common\Entity::L_ERROR, "Configuration error, no test configured for <strong>$test</strong>.");
90
            return;
91
        }
92
        $this->$m_name();
93
    }
94
95
    /**
96
     * The multiple tests wrapper
97
     * @param array $Tests the tests array.
98
     *
99
     * The $Tests is a simple string array, where each entry is a test name
100
     * the test names can also be given in the format "test=>subtest", which 
101
     * defines a conditional execution of the "subtest" if the "test" was run earier
102
     * and returned a success.
103
     */
104
    public function run_tests($Tests) {
105
        foreach ($Tests as $testName) {
106
            $matchArray = [];
107
            if (preg_match('/(.+)=>(.+)/', $testName, $matchArray)) {
108
                $tst = $matchArray[1];
109
                $subtst = $matchArray[2];
110
                if ($this->test_result[$tst] < \core\common\Entity::L_ERROR) {
111
                    $this->test($subtst);
112
                }
113
            } else {
114
                $this->test($testName);
115
            }
116
        }
117
    }
118
119
    public function get_test_names() {
120
        $T = get_class_methods($this);
121
        $out = [];
122
        foreach ($T as $t) {
123
            if (preg_match('/^(.*)_test$/', $t, $m)) {
124
                $out[] = $m[1];
125
            }
126
        }
127
        return $out;
128
    }
129
130
    /**
131
     * This array is used to return the test results.
132
     * As the 'global' entry it returns the maximum return value
133
     * from all tests.
134
     * Individual tests results are teturned as separate entires
135
     * indexed by test names; each value is an array passing "level" and "message"
136
     * from each of the tests.
137
     * $test_result is set by the test_return method
138
     *
139
     * @var array $test_result
140
     */
141
    public $test_result;
142
143
    private function test_return($level, $message) {
144
        $this->out[$this->name][] = ['level' => $level, 'message' => $message];
145
        $this->test_result[$this->name] = max($this->test_result[$this->name], $level);
146
        $this->test_result['global'] = max($this->test_result['global'], $level);
147
    }
148
149
    private function get_exec_path($pathToCheck) {
150
        $the_path = "";
151
        $exec_is = "UNDEFINED";
152
        foreach ([CONFIG, CONFIG_CONFASSISTANT, CONFIG_DIAGNOSTICS] as $config) {
153
            if (!empty($config['PATHS'][$pathToCheck])) {
154
                $matchArray = [];
155
                preg_match('/([^ ]+) ?/', $config['PATHS'][$pathToCheck], $matchArray);
156
                $exe = $matchArray[1];
157
                $the_path = exec("which " . $config['PATHS'][$pathToCheck]);
158
                if ($the_path == $exe) {
159
                    $exec_is = "EXPLICIT";
160
                } else {
161
                    $exec_is = "IMPLICIT";
162
                }
163
                return(['exec' => $the_path, 'exec_is' => $exec_is]);
164
            }
165
        }
166
        return(['exec' => $the_path, 'exec_is' => $exec_is]);
167
    }
168
169
    /**
170
     *  Test for php version
171
     */
172
    private function php_test() {
173
        if (version_compare(phpversion(), $this->php_needversion, '>=')) {
174
            $this->test_return(\core\common\Entity::L_OK, "<strong>PHP</strong> is sufficiently recent. You are running " . phpversion() . ".");
175
        } else {
176
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>PHP</strong> is too old. We need at least $this->php_needversion, but you only have " . phpversion() . ".");
177
        }
178
    }
179
    
180
    /**
181
     * set for cat_base_url setting
182
     */
183
    private function cat_base_url_test() {
184
        $rootUrl = substr(CONFIG['PATHS']['cat_base_url'], -1) === '/' ? substr(CONFIG['PATHS']['cat_base_url'], 0, -1) : CONFIG['PATHS']['cat_base_url'];
185
        preg_match('/(^.*)\/admin\/112365365321.php/', $_SERVER['SCRIPT_NAME'], $m);
186
        if ($rootUrl === $m[1]) {
187
            $this->test_return(\core\common\Entity::L_OK, "<strong>cat_base_url</strong> set correctly");
188
        } else {
189
            $rootFromScript = $m[1] === '' ? '/' : $m[1];
190
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>cat_base_url</strong> is set to <strong>" . CONFIG['PATHS']['cat_base_url'] . "</strong> and should be <strong>$rootFromScript</strong>");
191
        }
192
    }
193
194
    /**
195
     * test for simpleSAMLphp
196
     */
197
    private function ssp_test() {
198
        if (!is_file(CONFIG['AUTHENTICATION']['ssp-path-to-autoloader'])) {
199
            $this->test_return(\core\common\Entity::L_ERROR, "SS<strong>simpleSAMLphp</strong> not found!");
200
        } else {
201
            $this->test_return(\core\common\Entity::L_OK, "<strong>simpleSAMLphp</strong> autoloader found.");
202
        }
203
    }
204
205
    /**
206
     * test for security setting
207
     */
208
    private function security_test() {
209
        if (in_array("I do not care about security!", CONFIG['SUPERADMINS'])) {
210
            $this->test_return(\core\common\Entity::L_WARN, "You do not care about security. This page should be made accessible to the CAT admin only! See config.php 'Superadmins'!");
211
        }
212
    }
213
214
    /**
215
     * test if zip is available
216
     */
217
    private function zip_test() {
218 View Code Duplication
        if (exec("which zip") != "") {
219
            $this->test_return(\core\common\Entity::L_OK, "<strong>zip</strong> binary found.");
220
        } else {
221
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>zip</strong> not found in your \$PATH!");
222
        }
223
    }
224
225
    /**
226
     * test if eapol_test is availabe and reacent enough
227
     */
228
    private function eapol_test_test() {
229
        exec(CONFIG_DIAGNOSTICS['PATHS']['eapol_test'], $out, $retval);
230
        if ($retval == 255) {
231
            $o = preg_grep('/-o<server cert/', $out);
232 View Code Duplication
            if (count($o) > 0) {
233
                $this->test_return(\core\common\Entity::L_OK, "<strong>eapol_test</strong> script found.");
234
            } else {
235
                $this->test_return(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> found, but is too old!");
236
            }
237
        } else {
238
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> not found!");
239
        }
240
    }
241
242
    /**
243
     * test if logdir exists and is writable
244
     */
245
    private function logdir_test() {
246
        if (fopen(CONFIG['PATHS']['logdir'] . "/debug.log", "a") == FALSE) {
247
            $this->test_return(\core\common\Entity::L_WARN, "Log files in <strong>" . CONFIG['PATHS']['logdir'] . "</strong> are not writable!");
248
        } else {
249
            $this->test_return(\core\common\Entity::L_OK, "Log directory is writable.");
250
        }
251
    }
252
253
    /**
254
     * test for required PHP modules
255
     */
256
    private function phpModules_test() {
257
        if (function_exists('idn_to_ascii')) {
258
            $this->test_return(\core\common\Entity::L_OK, "PHP can handle internationalisation.");
259
        } else {
260
            $this->test_return(\core\common\Entity::L_ERROR, "PHP can <strong>NOT</strong> handle internationalisation (idn_to_ascii() from php7.0-intl).");
261
        }
262
263
        if (function_exists('gettext')) {
264
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GNU Gettext</strong> is installed.");
265
        } else {
266
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GNU Gettext</strong> not found!");
267
        }
268
269
        if (function_exists('openssl_sign')) {
270
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>OpenSSL</strong> is installed.");
271
        } else {
272
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>OpenSSL</strong> not found!");
273
        }
274
275
        if (class_exists('\Imagick')) {
276
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>Imagick</strong> is installed.");
277
        } else {
278
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>Imagick</strong> not found! Get it from your distribution or <a href='http://pecl.php.net/package/imagick'>here</a>.");
279
        }
280
281
        if (function_exists('ImageCreate')) {
282
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GD</strong> is installed.");
283
        } else {
284
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GD</strong> not found!</a>.");
285
        }
286
287
        if (function_exists('mysqli_connect')) {
288
            $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>MySQL</strong> is installed.");
289
        } else {
290
            $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>MySQL</strong> not found!");
291
        }
292
    }
293
294
    /**
295
     * test if GeoIP is installed correctly
296
     */
297
    private function geoip_test() {
298
        $host_4 = '145.0.2.50';
299
        $host_6 = '2001:610:188:444::50';
300
        switch (CONFIG['GEOIP']['version']) {
301
            case 0:
302
                $this->test_return(\core\common\Entity::L_REMARK, "As set in the config, no geolocation service will be used");
303
                break;
304
            case 1:
305
                if (!function_exists('geoip_record_by_name')) {
306
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) not found! Get it from your distribution or <a href='http://pecl.php.net/package/geoip'>here</a> or better install GeoIP2 from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
307
                    return;
308
                }
309
                $record = geoip_record_by_name($host_4);
310
                if ($record === FALSE) {
311
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
312
                    return;
313
                }
314
                if ($record['city'] != 'Utrecht') {
315
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP</strong> (legacy) found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
316
                    return;
317
                }
318
                $this->test_return(\core\common\Entity::L_REMARK, "PHP extension <strong>GeoIP</strong> (legacy) is installed and working. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly. We stronly advise to replace the legacy GeoIP with GeoIP2 from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
319
                break;
320
            case 2:
321 View Code Duplication
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-autoloader'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> not found! Get it from <a href='https://github.com/maxmind/GeoIP2-php'>here</a>.");
323
                    return;
324
                }
325 View Code Duplication
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-db'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
                    $this->test_return(\core\common\Entity::L_ERROR, "<strong>GeoIP2 database</strong> not found! See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
327
                    return;
328
                }
329
                require_once CONFIG['GEOIP']['geoip2-path-to-autoloader'];
330
                $reader = new Reader(CONFIG['GEOIP']['geoip2-path-to-db']);
331
                try {
332
                    $record = $reader->city($host_4);
333
                } catch (Exception $e) {
334
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
335
                    return;
336
                }
337
                if ($record->city->name != 'Utrecht') {
338
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
339
                    return;
340
                }
341
                try {
342
                    $record = $reader->city($host_6);
343
                } catch (Exception $e) {
344
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly with IPv6, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
345
                    return;
346
                }
347
                if ($record->city->name != 'Utrecht') {
348
                    $this->test_return(\core\common\Entity::L_ERROR, "PHP extension <strong>GeoIP2</strong> found but not working properly with IPv6, perhaps you need to download the databases. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
349
                    return;
350
                }
351
                $this->test_return(\core\common\Entity::L_OK, "PHP extension <strong>GeoIP2</strong> is installed and working. See utils/GeoIP-update.sh in the CAT distribution and use it tu update the GeoIP database regularly.");
352
                break;
353
            default:
354
                $this->test_return(\core\common\Entity::L_ERROR, 'Check CONFIG[\'GEOIP\'][\'version\'], it must be set to either 1 or 2');
355
                break;
356
        }
357
    }
358
359
    /**
360
     * test if openssl is available
361
     */
362
    private function openssl_test() {
363
        $A = $this->get_exec_path('openssl');
364
        if ($A['exec'] != "") {
365
            $t = exec($A['exec'] . ' version');
366 View Code Duplication
            if ($A['exec_is'] == "EXPLICIT") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
367
                $this->test_return(\core\common\Entity::L_OK, "<strong>$t</strong> was found and is configured explicitly in your config.");
368
            } else {
369
                $this->test_return(\core\common\Entity::L_WARN, "<strong>$t</strong> was found, but is not configured with an absolute path in your config.");
370
            }
371
        } else {
372
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>openssl</strong> was not found on your system!");
373
        }
374
    }
375
376
    /**
377
     * test if makensis is available
378
     */
379
    private function makensis_test() {
380
        if (!is_numeric(CONFIG_CONFASSISTANT['NSIS_VERSION'])) {
381
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be numeric!");
382
            return;
383
        }
384
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] < 2) {
385
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be at least 2!");
386
            return;
387
        }
388
        $A = $this->get_exec_path('makensis');
389
        if ($A['exec'] != "") {
390
            $t = exec($A['exec'] . ' -VERSION');
391 View Code Duplication
            if ($A['exec_is'] == "EXPLICIT") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
392
                $this->test_return(\core\common\Entity::L_OK, "<strong>makensis $t</strong> was found and is configured explicitly in your config.");
393
            } else {
394
                $this->test_return(\core\common\Entity::L_WARN, "<strong>makensis $t</strong> was found, but is not configured with an absolute path in your config.");
395
            }
396
            exec($A['exec'] . ' -HELP', $t);
397
            $t1 = count(preg_grep('/INPUTCHARSET/', $t));
398 View Code Duplication
            if ($t1 == 1 && CONFIG_CONFASSISTANT['NSIS_VERSION'] == 2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399
                $this->test_return(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
400
            }
401 View Code Duplication
            if ($t1 == 0 && CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
402
                $this->test_return(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
403
            }
404
        } else {
405
            $this->test_return(\core\common\Entity::L_ERROR, "<strong>makensis</strong> was not found on your system!");
406
        }
407
    }
408
409
    /**
410
     * test if all required NSIS modules are available
411
     */
412
    private function NSISmodules_test() {
413
        $tmp_dir = $this->createTemporaryDirectory('installer', 0)['dir'];
414
        if (!chdir($tmp_dir)) {
415
            $this->loggerInstance->debug(2, "Cannot chdir to $tmp_dir\n");
416
            $this->test_return(\core\common\Entity::L_ERROR, "NSIS modules test - problem with temporary directory permissions, cannot continue");
417
            return;
418
        }
419
        $exe = 'tt.exe';
420
        $NSIS_Module_status = [];
421
        foreach ($this->NSIS_Modules as $module) {
422
            unset($out);
423
            exec(CONFIG_CONFASSISTANT['PATHS']['makensis'] . " -V1 '-X!include $module' '-XOutFile $exe' '-XSection X' '-XSectionEnd'", $out, $retval);
424
            if ($retval > 0) {
425
                $NSIS_Module_status[$module] = 0;
426
            } else {
427
                $NSIS_Module_status[$module] = 1;
428
            }
429
        }
430
        if (is_file($exe)) {
431
            unlink($exe);
432
        }
433
        foreach ($NSIS_Module_status as $module => $status) {
434
            if ($status == 1) {
435
                $this->test_return(\core\common\Entity::L_OK, "NSIS module <strong>$module</strong> was found.");
436
            } else {
437
                $this->test_return(\core\common\Entity::L_ERROR, "NSIS module <strong>$module</strong> was not found or is not working correctly.");
438
            }
439
        }
440
    }
441
442
    /**
443
     * test access to dowloads directories
444
     */
445
    private function directories_test() {
446
        $Dir1 = $this->createTemporaryDirectory('installer', 0);
447
        $dir1 = $Dir1['dir'];
448
        $base1 = $Dir1['base'];
449
        if ($dir1) {
450
            $this->test_return(\core\common\Entity::L_OK, "Installer cache directory is writable.");
451
            \core\common\Entity::rrmdir($dir1);
452
        } else {
453
            $this->test_return(\core\common\Entity::L_ERROR, "Installer cache directory $base1 does not exist or is not writable!");
454
        }
455
        $Dir2 = $this->createTemporaryDirectory('test', 0);
456
        $dir2 = $Dir2['dir'];
457
        $base2 = $Dir2['base'];
458
        if ($dir2) {
459
            $this->test_return(\core\common\Entity::L_OK, "Test directory is writable.");
460
            \core\common\Entity::rrmdir($dir2);
461
        } else {
462
            $this->test_return(\core\common\Entity::L_ERROR, "Test directory $base2 does not exist or is not writable!");
463
        }
464
        $Dir3 = $this->createTemporaryDirectory('logo', 0);
465
        $dir3 = $Dir3['dir'];
466
        $base3 = $Dir3['base'];
467
        if ($dir3) {
468
            $this->test_return(\core\common\Entity::L_OK, "Logos cache directory is writable.");
469
            \core\common\Entity::rrmdir($dir3);
470
        } else {
471
            $this->test_return(\core\common\Entity::L_ERROR, "Logos cache directory $base3 does not exist or is not writable!");
472
        }
473
    }
474
475
    /**
476
     * test if all required locales are enabled
477
     */
478
    private function locales_test() {
479
        $locales = shell_exec("locale -a");
480
        $allthere = "";
481
        foreach (CONFIG['LANGUAGES'] as $onelanguage) {
482
            if (preg_match("/" . $onelanguage['locale'] . "/", $locales) == 0) {
483
                $allthere .= $onelanguage['locale'] . " ";
484
            }
485
        }
486 View Code Duplication
        if ($allthere == "") {
487
            $this->test_return(\core\common\Entity::L_OK, "All of your configured locales are available on your system.");
488
        } else {
489
            $this->test_return(\core\common\Entity::L_WARN, "Some of your configured locales (<strong>$allthere</strong>) are not installed and will not be displayed correctly!");
490
        }
491
    }
492
493
    const DEFAULTS = [
494
        ["SETTING" => CONFIG['APPEARANCE']['from-mail'],
495
            "DEFVALUE" => "[email protected]",
496
            "COMPLAINTSTRING" => "APPEARANCE/from-mail "],
497
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['url'],
498
            "DEFVALUE" => "[email protected]?body=Only%20English%20language%20please!",
499
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/url "],
500
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['display'],
501
            "DEFVALUE" => "[email protected]",
502
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/display "],
503
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['developer-mail'],
504
            "DEFVALUE" => "[email protected]",
505
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/mail "],
506
        ["SETTING" => CONFIG['APPEARANCE']['abuse-mail'],
507
            "DEFVALUE" => "[email protected]",
508
            "COMPLAINTSTRING" => "APPEARANCE/abuse-mail "],
509
        ["SETTING" => CONFIG['APPEARANCE']['MOTD'],
510
            "DEFVALUE" => "Release Candidate. All bugs to be shot on sight!",
511
            "COMPLAINTSTRING" => "APPEARANCE/MOTD "],
512
        ["SETTING" => CONFIG['APPEARANCE']['webcert_CRLDP'],
513
            "DEFVALUE" => ['list', 'of', 'CRL', 'pointers'],
514
            "COMPLAINTSTRING" => "APPEARANCE/webcert_CRLDP "],
515
        ["SETTING" => CONFIG['DB']['INST']['host'],
516
            "DEFVALUE" => "db.host.example",
517
            "COMPLAINTSTRING" => "DB/INST "],
518
        ["SETTING" => CONFIG['DB']['INST']['host'],
519
            "DEFVALUE" => "db.host.example",
520
            "COMPLAINTSTRING" => "DB/USER "],
521
    ];
522
523
    /**
524
     * test if defaults in the config have been replaced with some real values
525
     */
526
    private function defaults_test() {
527
        $defaultvalues = "";
528
        $missingvalues = "";
529
        // all the checks for equality with a shipped default value
530
        foreach (SanityTests::DEFAULTS as $oneCheckItem) {
531
            if ($oneCheckItem['SETTING'] == $oneCheckItem["DEFVALUE"]) {
532
                $defaultvalues .= $oneCheckItem["COMPLAINTSTRING"];
533
            }
534
        }
535
        // additional checks for defaults, which are not simple equality checks
536
        if (empty(CONFIG['APPEARANCE']['webcert_OCSP'])) {
537
            $missingvalues .= "APPEARANCE/webcert_OCSP ";
538
        } elseif (CONFIG['APPEARANCE']['webcert_OCSP'] == ['list', 'of', 'OCSP', 'pointers']) {
539
            $defaultvalues .= "APPEARANCE/webcert_OCSP ";
540
        }
541
        if (isset(CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]) && CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]['ip'] == "192.0.2.1") {
542
            $defaultvalues .= "RADIUSTESTS/UDP-hosts ";
543
        }
544
        if (!empty(CONFIG['DB']['EXTERNAL']) && CONFIG['DB']['EXTERNAL']['host'] == "customerdb.otherhost.example") {
545
            $defaultvalues .= "DB/EXTERNAL ";
546
        }
547
        $files = [];
548
        foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts'] as $cadata) {
549
            foreach ($cadata['certificates'] as $cert_files) {
550
                $files[] = $cert_files['public'];
551
                $files[] = $cert_files['private'];
552
            }
553
        }
554
555
        foreach ($files as $file) {
556
            $handle = fopen(ROOT . "/config/cli-certs/" . $file, 'r');
557
            if (!$handle) {
558
                $defaultvalues .= "CERTIFICATE/$file ";
559
            } else {
560
                fclose($handle);
561
            }
562
        }
563 View Code Duplication
        if ($defaultvalues != "") {
564
            $this->test_return(\core\common\Entity::L_WARN, "Your configuration in config/config.php contains unchanged default values or links to inexistent files: <strong>$defaultvalues</strong>!");
565
        } else {
566
            $this->test_return(\core\common\Entity::L_OK, "Your configuration does not contain any unchanged defaults, which is a good sign.");
567
        }
568
    }
569
570
    /**
571
     * test access to databases
572
     */
573
    private function databases_test() {
574
        $databaseName1 = 'INST';
575
        $db1 = mysqli_connect(CONFIG['DB'][$databaseName1]['host'], CONFIG['DB'][$databaseName1]['user'], CONFIG['DB'][$databaseName1]['pass'], CONFIG['DB'][$databaseName1]['db']);
576 View Code Duplication
        if (!$db1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
577
            $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName1 database failed");
578
        } else {
579
            $r = mysqli_query($db1, 'select * from profile_option_dict');
580
            if ($r->num_rows == $this->profile_option_ct) {
581
                $this->test_return(\core\common\Entity::L_OK, "The $databaseName1 database appears to be OK.");
582
            } else {
583
                $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName1 database is reacheable but probably not updated to this version of CAT.");
584
            }
585
        }
586
        $databaseName2 = 'USER';
587
        $db2 = mysqli_connect(CONFIG['DB'][$databaseName2]['host'], CONFIG['DB'][$databaseName2]['user'], CONFIG['DB'][$databaseName2]['pass'], CONFIG['DB'][$databaseName2]['db']);
588 View Code Duplication
        if (!$db2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
589
            $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName2 database failed");
590
        } else {
591
            $r = mysqli_query($db2, 'desc view_admin');
592
            if ($r->num_rows == $this->view_admin_ct) {
593
                $this->test_return(\core\common\Entity::L_OK, "The $databaseName2 database appears to be OK.");
594
            } else {
595
                $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName2 is reacheable but there is something wrong with the schema");
596
            }
597
        }
598
        $databaseName3 = 'EXTERNAL';
599
        if (!empty(CONFIG['DB'][$databaseName3])) {
600
            $db3 = mysqli_connect(CONFIG['DB'][$databaseName3]['host'], CONFIG['DB'][$databaseName3]['user'], CONFIG['DB'][$databaseName3]['pass'], CONFIG['DB'][$databaseName3]['db']);
601 View Code Duplication
            if (!$db3) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
602
                $this->test_return(\core\common\Entity::L_ERROR, "Connection to the  $databaseName3 database failed");
603
            } else {
604
                $r = mysqli_query($db3, 'desc view_admin');
605
                if ($r->num_rows == $this->view_admin_ct) {
606
                    $this->test_return(\core\common\Entity::L_OK, "The $databaseName3 database appears to be OK.");
607
                } else {
608
                    $this->test_return(\core\common\Entity::L_ERROR, "The $databaseName3 is reacheable but there is something wrong with the schema");
609
                }
610
            }
611
        }
612
    }
613
614
    /**
615
     * test devices.php for the no_cache option
616
     */
617
    private function device_cache_test() {
618
        if ((!empty(\devices\Devices::$Options['no_cache'])) && \devices\Devices::$Options['no_cache']) {
619
            $global_no_cache = 1;
620
        } else {
621
            $global_no_cache = 0;
622
        }
623
624
        if ($global_no_cache == 1) {
625
            $this->test_return(\core\common\Entity::L_WARN, "Devices no_cache global option is set, this is not a good idea in a production setting\n");
626
        }
627
        $Devs = \devices\Devices::listDevices();
628
        $no_cache_dev = '';
629
        $no_cache_dev_count = 0;
630
        if ($global_no_cache) {
631 View Code Duplication
            foreach ($Devs as $dev => $D) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
632
                if (empty($D['options']['no_cache']) || $D['options']['no_cache'] != 0) {
633
                    $no_cache_dev .= $dev . " ";
634
                    $no_cache_dev_count++;
635
                }
636
            }
637
        } else {
638 View Code Duplication
            foreach ($Devs as $dev => $D) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
639
                if (!empty($D['options']['no_cache']) && $D['options']['no_cache'] != 0) {
640
                    $no_cache_dev .= $dev . " ";
641
                    $no_cache_dev_count++;
642
                }
643
            }
644
        }
645
646
647
        if ($no_cache_dev_count > 1) {
648
            $this->test_return(\core\common\Entity::L_WARN, "The following devices will not be cached: $no_cache_dev");
649
        }
650
        if ($no_cache_dev_count == 1) {
651
            $this->test_return(\core\common\Entity::L_WARN, "The following device will not be cached: $no_cache_dev");
652
        }
653
    }
654
655
    /**
656
     * test if mailer works
657
     */
658
    private function mailer_test() {
659
        if (empty(CONFIG['APPEARANCE']['abuse-mail']) || CONFIG['APPEARANCE']['abuse-mail'] == "[email protected]") {
660
            $this->test_return(\core\common\Entity::L_ERROR, "Your abuse-mail has not been set, cannot continue with mailer tests.");
661
            return;
662
        }
663
        $mail = new \PHPMailer\PHPMailer\PHPMailer();
664
        $mail->isSMTP();
665
        $mail->Port = 587;
666
        $mail->SMTPAuth = true;
667
        $mail->SMTPSecure = 'tls';
668
        $mail->Host = CONFIG['MAILSETTINGS']['host'];
669
        $mail->Username = CONFIG['MAILSETTINGS']['user'];
670
        $mail->Password = CONFIG['MAILSETTINGS']['pass'];
671
        $mail->WordWrap = 72;
672
        $mail->isHTML(FALSE);
673
        $mail->CharSet = 'UTF-8';
674
        $mail->From = CONFIG['APPEARANCE']['from-mail'];
675
        $mail->FromName = CONFIG['APPEARANCE']['productname'] . " Invitation System";
676
        $mail->addAddress(CONFIG['APPEARANCE']['abuse-mail']);
677
        $mail->Subject = "testing CAT configuration mail";
678
        $mail->Body = "Testing CAT mailing\n";
679
        $sent = $mail->send();
680
        if ($sent) {
681
            $this->test_return(\core\common\Entity::L_OK, "mailer settings appear to be working, check " . CONFIG['APPEARANCE']['abuse-mail'] . " mailbox if the message was receiced.");
682
        } else {
683
            $this->test_return(\core\common\Entity::L_ERROR, "mailer settings failed, check the Config::MAILSETTINGS");
684
        }
685
    }
686
687
    /**
688
     * TODO test if RADIUS connections work
689
     */
690
    private function UDPhosts_test() {
691
//        if(empty)
692
    }
693
694
}
695