Test Failed
Branch release_2_0 (0c7e20)
by Stefan
05:47
created

SanityTests   F

Complexity

Total Complexity 129

Size/Duplication

Total Lines 751
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 129
eloc 417
dl 0
loc 751
rs 2
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 5
A run_tests() 0 11 4
A openssl_test() 0 11 3
A cat_base_url_test() 0 8 4
A get_test_names() 0 9 3
A php_test() 0 5 2
A test() 0 10 2
C geoip_test() 0 59 13
A ssp_test() 0 11 4
A eapol_test_test() 0 11 3
A security_test() 0 3 2
A logdir_test() 0 5 2
B phpModules_test() 0 35 7
A zip_test() 0 5 2
A testReturn() 0 4 1
A directories_test() 0 27 4
F databases_test() 0 48 14
A getExecPath() 0 18 4
C defaults_test() 0 31 12
A locales_test() 0 12 4
B makensis_test() 0 28 9
C device_cache_test() 0 35 13
A UDPhosts_test() 0 1 1
B NSISmodules_test() 0 26 7
A mailer_test() 0 27 4

How to fix   Complexity   

Complex Class

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.

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
 * Contributions to this work were made on behalf of the GÉANT project, a 
5
 * project that has received funding from the European Union’s Framework 
6
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
7
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
8
 * 691567 (GN4-1) and No. 731122 (GN4-2).
9
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
10
 * of the copyright in all material which was developed by a member of the GÉANT
11
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
12
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
13
 * UK as a branch of GÉANT Vereniging.
14
 * 
15
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
16
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
17
 *
18
 * License: see the web/copyright.inc.php file in the file structure or
19
 *          <base_url>/copyright.php after deploying the software
20
 */
21
22
/**
23
 * 
24
 * 
25
 * This is the definition of the CAT class implementing various configuration
26
 * tests. 
27
 * Each test is implemented as a priviate method which needs to be named "test_name_test".
28
 * The test returns the results by calling the testReturn method, this passing the return
29
 * code and the explanatory message. Multiple calls to testReturn are allowed.
30
 *
31
 * An individual test can be run by the "test" method which takes the test name as an argument
32
 * multiple tests should be run by the run_all_tests method which takes an array as an argument
33
 * see method descriptions for more information.
34
 * 
35
 * The results of the tests are passed within the $test_result array
36
 *
37
 * Some configuration of this class is required, see further down.
38
 * @author Stefan Winter <[email protected]>
39
 * @author Tomasz Wolniewicz <[email protected]>
40
 *
41
 * @license see LICENSE file in root directory
42
 *
43
 * @package Utilities
44
 */
45
46
namespace core;
47
48
use GeoIp2\Database\Reader;
49
use \Exception;
50
51
require_once dirname(dirname(__FILE__)) . "/config/_config.php";
52
require_once dirname(dirname(__FILE__)) . "/core/PHPMailer/src/PHPMailer.php";
53
require_once dirname(dirname(__FILE__)) . "/core/PHPMailer/src/SMTP.php";
54
55
class SanityTests extends CAT {
56
    /* in this section set current CAT requirements */
57
58
    /* $php_needversion sets the minumum required php version */
59
60
    // because of bug:
61
    // Fixed bug #74005 (mail.add_x_header causes RFC-breaking lone line feed).
62
    private $php_needversion = '7.2.0';
63
    private $ssp_needversion = ['major' => 1, 'minor' => 15];
64
65
66
    /* List all required NSIS modules below */
67
    private $NSIS_Modules = [
68
        "nsArray.nsh",
69
        "FileFunc.nsh",
70
        "LogicLib.nsh",
71
        "WordFunc.nsh",
72
        "FileFunc.nsh",
73
        "x64.nsh",
74
    ];
75
76
    /* set $profile_option_ct to the number of rows returned by "SELECT * FROM profile_option_dict" */
77
    private $profile_option_ct;
78
    /* set $view_admin_ct to the number of rows returned by "desc view_admin" */
79
    private $view_admin_ct = 8;
80
81
    /* end of config */
82
    public $out;
83
    public $name;
84
85
    /**
86
     * initialise the tests. Includes counting the number of expected rows in the profile_option_dict table.
87
     */
88
    public function __construct() {
89
        parent::__construct();
90
        $this->test_result = [];
91
        $this->test_result['global'] = 0;
92
        // parse the schema file to find out the number of expected rows...
93
        $schema = file(dirname(dirname(__FILE__)) . "/schema/schema.sql");
94
        $this->profile_option_ct = 0;
95
        $passedTheWindmill = FALSE;
96
        foreach ($schema as $schemaLine) {
97
            if (preg_match("/^INSERT INTO \`profile_option_dict\` VALUES/", $schemaLine)) {
98
                $passedTheWindmill = TRUE;
99
                continue;
100
            }
101
            if ($passedTheWindmill) {
102
                if (substr($schemaLine, 0, 1) == '(') { // a relevant line in schema
103
                    $this->profile_option_ct = $this->profile_option_ct + 1;
104
                } else { // anything else, quit parsing
105
                    break;
106
                }
107
            }
108
        }
109
    }
110
111
    /**
112
     * The single test wrapper
113
     * @param string $test the test name
114
     * @return void
115
     */
116
    public function test($test) {
117
        $this->out[$test] = [];
118
        $this->name = $test;
119
        $m_name = $test . '_test';
120
        $this->test_result[$test] = 0;
121
        if (!method_exists($this, $m_name)) {
122
            $this->testReturn(\core\common\Entity::L_ERROR, "Configuration error, no test configured for <strong>$test</strong>.");
123
            return;
124
        }
125
        $this->$m_name();
126
    }
127
128
    /**
129
     * The multiple tests wrapper
130
     * @param array $Tests the tests array is a simple string array, where each 
131
     *                     entry is a test name. The test names can also be 
132
     *                     given in the format "test=>subtest", which defines a
133
     *                     conditional execution of the "subtest" if the "test"
134
     *                     was run earlier and returned a success.
135
     * @return void
136
     */
137
    public function run_tests($Tests) {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::run_tests" is not in camel caps format
Loading history...
138
        foreach ($Tests as $testName) {
139
            $matchArray = [];
140
            if (preg_match('/(.+)=>(.+)/', $testName, $matchArray)) {
141
                $tst = $matchArray[1];
142
                $subtst = $matchArray[2];
143
                if ($this->test_result[$tst] < \core\common\Entity::L_ERROR) {
144
                    $this->test($subtst);
145
                }
146
            } else {
147
                $this->test($testName);
148
            }
149
        }
150
    }
151
152
    /**
153
     * enumerates the tests which are defined
154
     * 
155
     * @return array
156
     */
157
    public function get_test_names() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::get_test_names" is not in camel caps format
Loading history...
158
        $T = get_class_methods($this);
159
        $out = [];
160
        foreach ($T as $t) {
161
            if (preg_match('/^(.*)_test$/', $t, $m)) {
162
                $out[] = $m[1];
163
            }
164
        }
165
        return $out;
166
    }
167
168
    /**
169
     * This array is used to return the test results.
170
     * As the 'global' entry it returns the maximum return value
171
     * from all tests.
172
     * Individual tests results are teturned as separate entires
173
     * indexed by test names; each value is an array passing "level" and "message"
174
     * from each of the tests.
175
     * $test_result is set by the testReturn method
176
     *
177
     * @var array $test_result
178
     */
179
    public $test_result;
180
181
    /**
182
     * stores the result of a given test in standardised format
183
     * 
184
     * @param int    $level   severity level of the result
185
     * @param string $message verbal description of the result
186
     * @return void
187
     */
188
    private function testReturn($level, $message) {
189
        $this->out[$this->name][] = ['level' => $level, 'message' => $message];
190
        $this->test_result[$this->name] = max($this->test_result[$this->name], $level);
191
        $this->test_result['global'] = max($this->test_result['global'], $level);
192
    }
193
194
    /**
195
     * finds out if a path name is configured as an absolute path or only implicit (e.g. is in $PATH)
196
     * @param string $pathToCheck the path to check
197
     * @return array
198
     */
199
    private function getExecPath($pathToCheck) {
200
        $the_path = "";
201
        $exec_is = "UNDEFINED";
202
        foreach ([CONFIG, CONFIG_CONFASSISTANT, CONFIG_DIAGNOSTICS] as $config) {
203
            if (!empty($config['PATHS'][$pathToCheck])) {
204
                $matchArray = [];
205
                preg_match('/([^ ]+) ?/', $config['PATHS'][$pathToCheck], $matchArray);
206
                $exe = $matchArray[1];
207
                $the_path = exec("which " . $config['PATHS'][$pathToCheck]);
208
                if ($the_path == $exe) {
209
                    $exec_is = "EXPLICIT";
210
                } else {
211
                    $exec_is = "IMPLICIT";
212
                }
213
                return(['exec' => $the_path, 'exec_is' => $exec_is]);
214
            }
215
        }
216
        return(['exec' => $the_path, 'exec_is' => $exec_is]);
217
    }
218
219
    /**
220
     *  Test for php version
221
     * 
222
     * @return void
223
     */
224
    private function php_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::php_test" is not in camel caps format
Loading history...
225
        if (version_compare(phpversion(), $this->php_needversion, '>=')) {
226
            $this->testReturn(\core\common\Entity::L_OK, "<strong>PHP</strong> is sufficiently recent. You are running " . phpversion() . ".");
227
        } else {
228
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>PHP</strong> is too old. We need at least $this->php_needversion, but you only have " . phpversion() . ".");
229
        }
230
    }
231
232
    /**
233
     * set for cat_base_url setting
234
     * 
235
     * @return void
236
     */
237
    private function cat_base_url_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::cat_base_url_test" is not in camel caps format
Loading history...
238
        $rootUrl = substr(CONFIG['PATHS']['cat_base_url'], -1) === '/' ? substr(CONFIG['PATHS']['cat_base_url'], 0, -1) : CONFIG['PATHS']['cat_base_url'];
239
        preg_match('/(^.*)\/admin\/112365365321.php/', $_SERVER['SCRIPT_NAME'], $m);
240
        if ($rootUrl === $m[1]) {
241
            $this->testReturn(\core\common\Entity::L_OK, "<strong>cat_base_url</strong> set correctly");
242
        } else {
243
            $rootFromScript = $m[1] === '' ? '/' : $m[1];
244
            $this->testReturn(\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>");
245
        }
246
    }
247
248
    /**
249
     * test for simpleSAMLphp
250
     * 
251
     * @return void
252
     */
253
    private function ssp_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::ssp_test" is not in camel caps format
Loading history...
254
        if (!is_file(CONFIG['AUTHENTICATION']['ssp-path-to-autoloader'])) {
255
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>simpleSAMLphp</strong> not found!");
256
        } else {
257
            include_once CONFIG['AUTHENTICATION']['ssp-path-to-autoloader'];
258
            $SSPconfig = \SimpleSAML_Configuration::getInstance();
259
            $sspVersion = explode('.', $SSPconfig->getVersion());
260
            if ((int) $sspVersion[0] >= $this->ssp_needversion['major'] && (int) $sspVersion[1] >= $this->ssp_needversion['minor']) {
0 ignored issues
show
Coding Style introduced by
A cast statement should not be followed as per the coding-style.
Loading history...
261
                $this->testReturn(\core\common\Entity::L_OK, "<strong>simpleSAMLphp</strong> is sufficently recent. You are running " . implode('.', $sspVersion));
262
            } else {
263
                $this->testReturn(\core\common\Entity::L_ERROR, "<strong>simpleSAMLphp</strong> is too old. We need at least " . implode('.', $this->ssp_needversion));
264
            }
265
        }
266
    }
267
268
    /**
269
     * test for security setting
270
     * 
271
     * @return void
272
     */
273
    private function security_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::security_test" is not in camel caps format
Loading history...
274
        if (in_array("I do not care about security!", CONFIG['SUPERADMINS'])) {
275
            $this->testReturn(\core\common\Entity::L_WARN, "You do not care about security. This page should be made accessible to the CAT admin only! See config-master.php: 'SUPERADMINS'!");
276
        }
277
    }
278
279
    /**
280
     * test if zip is available
281
     * 
282
     * @return void
283
     */
284
    private function zip_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::zip_test" is not in camel caps format
Loading history...
285
        if (exec("which zip") != "") {
286
            $this->testReturn(\core\common\Entity::L_OK, "<strong>zip</strong> binary found.");
287
        } else {
288
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>zip</strong> not found in your \$PATH!");
289
        }
290
    }
291
292
    /**
293
     * test if eapol_test is available and recent enough
294
     * 
295
     * @return void
296
     */
297
    private function eapol_test_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::eapol_test_test" is not in camel caps format
Loading history...
298
        exec(CONFIG_DIAGNOSTICS['PATHS']['eapol_test'], $out, $retval);
299
        if ($retval == 255) {
300
            $o = preg_grep('/-o<server cert/', $out);
301
            if (count($o) > 0) {
302
                $this->testReturn(\core\common\Entity::L_OK, "<strong>eapol_test</strong> script found.");
303
            } else {
304
                $this->testReturn(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> found, but is too old!");
305
            }
306
        } else {
307
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>eapol_test</strong> not found!");
308
        }
309
    }
310
311
    /**
312
     * test if logdir exists and is writable
313
     * 
314
     * @return void
315
     */
316
    private function logdir_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::logdir_test" is not in camel caps format
Loading history...
317
        if (fopen(CONFIG['PATHS']['logdir'] . "/debug.log", "a") == FALSE) {
318
            $this->testReturn(\core\common\Entity::L_WARN, "Log files in <strong>" . CONFIG['PATHS']['logdir'] . "</strong> are not writable!");
319
        } else {
320
            $this->testReturn(\core\common\Entity::L_OK, "Log directory is writable.");
321
        }
322
    }
323
324
    /**
325
     * test for required PHP modules
326
     * 
327
     * @return void
328
     */
329
    private function phpModules_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::phpModules_test" is not in camel caps format
Loading history...
330
        if (function_exists('idn_to_ascii')) {
331
            $this->testReturn(\core\common\Entity::L_OK, "PHP can handle internationalisation.");
332
        } else {
333
            $this->testReturn(\core\common\Entity::L_ERROR, "PHP can <strong>NOT</strong> handle internationalisation (idn_to_ascii() from php7.0-intl).");
334
        }
335
336
        if (function_exists('gettext')) {
337
            $this->testReturn(\core\common\Entity::L_OK, "PHP extension <strong>GNU Gettext</strong> is installed.");
338
        } else {
339
            $this->testReturn(\core\common\Entity::L_ERROR, "PHP extension <strong>GNU Gettext</strong> not found!");
340
        }
341
342
        if (function_exists('openssl_sign')) {
343
            $this->testReturn(\core\common\Entity::L_OK, "PHP extension <strong>OpenSSL</strong> is installed.");
344
        } else {
345
            $this->testReturn(\core\common\Entity::L_ERROR, "PHP extension <strong>OpenSSL</strong> not found!");
346
        }
347
348
        if (class_exists('\Imagick')) {
349
            $this->testReturn(\core\common\Entity::L_OK, "PHP extension <strong>Imagick</strong> is installed.");
350
        } else {
351
            $this->testReturn(\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>.");
352
        }
353
354
        if (function_exists('ImageCreate')) {
355
            $this->testReturn(\core\common\Entity::L_OK, "PHP extension <strong>GD</strong> is installed.");
356
        } else {
357
            $this->testReturn(\core\common\Entity::L_ERROR, "PHP extension <strong>GD</strong> not found!</a>.");
358
        }
359
360
        if (function_exists('mysqli_connect')) {
361
            $this->testReturn(\core\common\Entity::L_OK, "PHP extension <strong>MySQL</strong> is installed.");
362
        } else {
363
            $this->testReturn(\core\common\Entity::L_ERROR, "PHP extension <strong>MySQL</strong> not found!");
364
        }
365
    }
366
367
    /**
368
     * test if GeoIP is installed correctly
369
     * 
370
     * @return void
371
     */
372
    private function geoip_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::geoip_test" is not in camel caps format
Loading history...
373
        $host_4 = '145.0.2.50';
374
        $host_6 = '2001:610:188:444::50';
375
        switch (CONFIG['GEOIP']['version']) {
376
            case 0:
377
                $this->testReturn(\core\common\Entity::L_REMARK, "As set in the config, no geolocation service will be used");
378
                break;
379
            case 1:
380
                if (!function_exists('geoip_record_by_name')) {
381
                    $this->testReturn(\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>.");
382
                    return;
383
                }
384
                $record = geoip_record_by_name($host_4);
385
                if ($record === FALSE) {
386
                    $this->testReturn(\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.");
387
                    return;
388
                }
389
                if ($record['city'] != 'Utrecht') {
390
                    $this->testReturn(\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.");
391
                    return;
392
                }
393
                $this->testReturn(\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>.");
394
                break;
395
            case 2:
396
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-autoloader'])) {
397
                    $this->testReturn(\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>.");
398
                    return;
399
                }
400
                if (!is_file(CONFIG['GEOIP']['geoip2-path-to-db'])) {
401
                    $this->testReturn(\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.");
402
                    return;
403
                }
404
                include_once CONFIG['GEOIP']['geoip2-path-to-autoloader'];
405
                $reader = new Reader(CONFIG['GEOIP']['geoip2-path-to-db']);
406
                try {
407
                    $record = $reader->city($host_4);
408
                } catch (Exception $e) {
409
                    $this->testReturn(\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.");
410
                    return;
411
                }
412
                if ($record->city->name != 'Utrecht') {
413
                    $this->testReturn(\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.");
414
                    return;
415
                }
416
                try {
417
                    $record = $reader->city($host_6);
418
                } catch (Exception $e) {
419
                    $this->testReturn(\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.");
420
                    return;
421
                }
422
                if ($record->city->name != 'Utrecht') {
423
                    $this->testReturn(\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.");
424
                    return;
425
                }
426
                $this->testReturn(\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.");
427
                break;
428
            default:
429
                $this->testReturn(\core\common\Entity::L_ERROR, 'Check CONFIG[\'GEOIP\'][\'version\'], it must be set to either 1 or 2');
430
                break;
431
        }
432
    }
433
434
    /**
435
     * test if openssl is available
436
     * 
437
     * @return void
438
     */
439
    private function openssl_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::openssl_test" is not in camel caps format
Loading history...
440
        $A = $this->getExecPath('openssl');
441
        if ($A['exec'] != "") {
442
            $t = exec($A['exec'] . ' version');
443
            if ($A['exec_is'] == "EXPLICIT") {
444
                $this->testReturn(\core\common\Entity::L_OK, "<strong>$t</strong> was found and is configured explicitly in your config.");
445
            } else {
446
                $this->testReturn(\core\common\Entity::L_WARN, "<strong>$t</strong> was found, but is not configured with an absolute path in your config.");
447
            }
448
        } else {
449
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>openssl</strong> was not found on your system!");
450
        }
451
    }
452
453
    /**
454
     * test if makensis is available
455
     * 
456
     * @return void
457
     */
458
    private function makensis_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::makensis_test" is not in camel caps format
Loading history...
459
        if (!is_numeric(CONFIG_CONFASSISTANT['NSIS_VERSION'])) {
460
            $this->testReturn(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be numeric!");
461
            return;
462
        }
463
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] < 2) {
464
            $this->testReturn(\core\common\Entity::L_ERROR, "NSIS_VERSION needs to be at least 2!");
465
            return;
466
        }
467
        $A = $this->getExecPath('makensis');
468
        if ($A['exec'] != "") {
469
            $t = exec($A['exec'] . ' -VERSION');
470
            if ($A['exec_is'] == "EXPLICIT") {
471
                $this->testReturn(\core\common\Entity::L_OK, "<strong>makensis $t</strong> was found and is configured explicitly in your config.");
472
            } else {
473
                $this->testReturn(\core\common\Entity::L_WARN, "<strong>makensis $t</strong> was found, but is not configured with an absolute path in your config.");
474
            }
475
            $outputArray = [];
476
            exec($A['exec'] . ' -HELP', $outputArray);
477
            $t1 = count(preg_grep('/INPUTCHARSET/', $outputArray));
478
            if ($t1 == 1 && CONFIG_CONFASSISTANT['NSIS_VERSION'] == 2) {
479
                $this->testReturn(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
480
            }
481
            if ($t1 == 0 && CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
482
                $this->testReturn(\core\common\Entity::L_ERROR, "Declared NSIS_VERSION does not seem to match the file pointed to by PATHS['makensis']!");
483
            }
484
        } else {
485
            $this->testReturn(\core\common\Entity::L_ERROR, "<strong>makensis</strong> was not found on your system!");
486
        }
487
    }
488
489
    /**
490
     * test if all required NSIS modules are available
491
     * 
492
     * @return void
493
     */
494
    private function NSISmodules_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::NSISmodules_test" is not in camel caps format
Loading history...
495
        $tmp_dir = $this->createTemporaryDirectory('installer', 0)['dir'];
496
        if (!chdir($tmp_dir)) {
497
            $this->loggerInstance->debug(2, "Cannot chdir to $tmp_dir\n");
498
            $this->testReturn(\core\common\Entity::L_ERROR, "NSIS modules test - problem with temporary directory permissions, cannot continue");
499
            return;
500
        }
501
        $exe = 'tt.exe';
502
        $NSIS_Module_status = [];
503
        foreach ($this->NSIS_Modules as $module) {
504
            unset($out);
505
            exec(CONFIG_CONFASSISTANT['PATHS']['makensis'] . " -V1 '-X!include $module' '-XOutFile $exe' '-XSection X' '-XSectionEnd'", $out, $retval);
506
            if ($retval > 0) {
507
                $NSIS_Module_status[$module] = 0;
508
            } else {
509
                $NSIS_Module_status[$module] = 1;
510
            }
511
        }
512
        if (is_file($exe)) {
513
            unlink($exe);
514
        }
515
        foreach ($NSIS_Module_status as $module => $status) {
516
            if ($status == 1) {
517
                $this->testReturn(\core\common\Entity::L_OK, "NSIS module <strong>$module</strong> was found.");
518
            } else {
519
                $this->testReturn(\core\common\Entity::L_ERROR, "NSIS module <strong>$module</strong> was not found or is not working correctly.");
520
            }
521
        }
522
    }
523
524
    /**
525
     * test access to dowloads directories
526
     * 
527
     * @return void
528
     */
529
    private function directories_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::directories_test" is not in camel caps format
Loading history...
530
        $Dir1 = $this->createTemporaryDirectory('installer', 0);
531
        $dir1 = $Dir1['dir'];
532
        $base1 = $Dir1['base'];
533
        if ($dir1) {
534
            $this->testReturn(\core\common\Entity::L_OK, "Installer cache directory is writable.");
535
            \core\common\Entity::rrmdir($dir1);
536
        } else {
537
            $this->testReturn(\core\common\Entity::L_ERROR, "Installer cache directory $base1 does not exist or is not writable!");
538
        }
539
        $Dir2 = $this->createTemporaryDirectory('test', 0);
540
        $dir2 = $Dir2['dir'];
541
        $base2 = $Dir2['base'];
542
        if ($dir2) {
543
            $this->testReturn(\core\common\Entity::L_OK, "Test directory is writable.");
544
            \core\common\Entity::rrmdir($dir2);
545
        } else {
546
            $this->testReturn(\core\common\Entity::L_ERROR, "Test directory $base2 does not exist or is not writable!");
547
        }
548
        $Dir3 = $this->createTemporaryDirectory('logo', 0);
549
        $dir3 = $Dir3['dir'];
550
        $base3 = $Dir3['base'];
551
        if ($dir3) {
552
            $this->testReturn(\core\common\Entity::L_OK, "Logos cache directory is writable.");
553
            \core\common\Entity::rrmdir($dir3);
554
        } else {
555
            $this->testReturn(\core\common\Entity::L_ERROR, "Logos cache directory $base3 does not exist or is not writable!");
556
        }
557
    }
558
559
    /**
560
     * test if all required locales are enabled
561
     * 
562
     * @return void
563
     */
564
    private function locales_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::locales_test" is not in camel caps format
Loading history...
565
        $locales = shell_exec("locale -a");
566
        $allthere = "";
567
        foreach (CONFIG['LANGUAGES'] as $onelanguage) {
568
            if (preg_match("/" . $onelanguage['locale'] . "/", $locales) == 0) {
569
                $allthere .= $onelanguage['locale'] . " ";
570
            }
571
        }
572
        if ($allthere == "") {
573
            $this->testReturn(\core\common\Entity::L_OK, "All of your configured locales are available on your system.");
574
        } else {
575
            $this->testReturn(\core\common\Entity::L_WARN, "Some of your configured locales (<strong>$allthere</strong>) are not installed and will not be displayed correctly!");
576
        }
577
    }
578
579
    const DEFAULTS = [
580
        ["SETTING" => CONFIG['APPEARANCE']['from-mail'],
581
            "DEFVALUE" => "[email protected]",
582
            "COMPLAINTSTRING" => "APPEARANCE/from-mail ",
583
            "REQUIRED" => FALSE,],
584
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['url'],
585
            "DEFVALUE" => "[email protected]?body=Only%20English%20language%20please!",
586
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/url ",
587
            "REQUIRED" => FALSE,],
588
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['display'],
589
            "DEFVALUE" => "[email protected]",
590
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/display ",
591
            "REQUIRED" => FALSE,],
592
        ["SETTING" => CONFIG['APPEARANCE']['support-contact']['developer-mail'],
593
            "DEFVALUE" => "[email protected]",
594
            "COMPLAINTSTRING" => "APPEARANCE/support-contact/mail ",
595
            "REQUIRED" => FALSE,],
596
        ["SETTING" => CONFIG['APPEARANCE']['abuse-mail'],
597
            "DEFVALUE" => "[email protected]",
598
            "COMPLAINTSTRING" => "APPEARANCE/abuse-mail ",
599
            "REQUIRED" => FALSE,],
600
        ["SETTING" => CONFIG['APPEARANCE']['MOTD'],
601
            "DEFVALUE" => "Release Candidate. All bugs to be shot on sight!",
602
            "COMPLAINTSTRING" => "APPEARANCE/MOTD ",
603
            "REQUIRED" => FALSE,],
604
        ["SETTING" => CONFIG['APPEARANCE']['webcert_CRLDP'],
605
            "DEFVALUE" => ['list', 'of', 'CRL', 'pointers'],
606
            "COMPLAINTSTRING" => "APPEARANCE/webcert_CRLDP ",
607
            "REQUIRED" => TRUE,],
608
        ["SETTING" => CONFIG['APPEARANCE']['webcert_OCSP'],
609
            "DEFVALUE" => ['list', 'of', 'OCSP', 'pointers'],
610
            "COMPLAINTSTRING" => "APPEARANCE/webcert_OCSP ",
611
            "REQUIRED" => TRUE,],
612
        ["SETTING" => CONFIG['DB']['INST']['host'],
613
            "DEFVALUE" => "db.host.example",
614
            "COMPLAINTSTRING" => "DB/INST ",
615
            "REQUIRED" => TRUE,],
616
        ["SETTING" => CONFIG['DB']['INST']['host'],
617
            "DEFVALUE" => "db.host.example",
618
            "COMPLAINTSTRING" => "DB/USER ",
619
            "REQUIRED" => TRUE,],
620
        ["SETTING" => CONFIG['DB']['EXTERNAL']['host'],
621
            "DEFVALUE" => "customerdb.otherhost.example",
622
            "COMPLAINTSTRING" => "DB/EXTERNAL ",
623
            "REQUIRED" => FALSE,],
624
    ];
625
626
    /**
627
     * test if defaults in the config have been replaced with some real values
628
     * 
629
     * @return void
630
     */
631
    private function defaults_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::defaults_test" is not in camel caps format
Loading history...
632
        $defaultvalues = "";
633
        $missingvalues = "";
634
        // all the checks for equality with a shipped default value
635
        foreach (SanityTests::DEFAULTS as $oneCheckItem) {
636
            if ($oneCheckItem['REQUIRED'] && !$oneCheckItem['SETTING']) {
637
                $missingvalues .= $oneCheckItem["COMPLAINTSTRING"];
638
            } elseif ($oneCheckItem['SETTING'] == $oneCheckItem["DEFVALUE"]) {
639
                $defaultvalues .= $oneCheckItem["COMPLAINTSTRING"];
640
            }
641
        }
642
        // additional checks for defaults, which are not simple equality checks
643
        if (isset(CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]) && CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'][0]['ip'] == "192.0.2.1") {
644
            $defaultvalues .= "RADIUSTESTS/UDP-hosts ";
645
        }
646
647
        foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts'] as $cadata) {
648
            foreach ($cadata['certificates'] as $cert_files) {
649
                if (file_get_contents(ROOT . "/config/cli-certs/" . $cert_files['public']) === FALSE) {
650
                    $defaultvalues .= "CERTIFICATE/" . $cert_files['public'] . " ";
651
                }
652
                if (file_get_contents(ROOT . "/config/cli-certs/" . $cert_files['private']) === FALSE) {
653
                    $defaultvalues .= "CERTIFICATE/" . $cert_files['private'] . " ";
654
                }
655
            }
656
        }
657
658
        if ($defaultvalues != "") {
659
            $this->testReturn(\core\common\Entity::L_WARN, "Your configuration in config/config.php contains unchanged default values or links to inexistent files: <strong>$defaultvalues</strong>!");
660
        } else {
661
            $this->testReturn(\core\common\Entity::L_OK, "Your configuration does not contain any unchanged defaults, which is a good sign.");
662
        }
663
    }
664
665
    /**
666
     * test access to databases
667
     * 
668
     * @return void
669
     */
670
    private function databases_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::databases_test" is not in camel caps format
Loading history...
671
        $databaseName1 = 'INST';
672
        try {
673
            $db1 = DBConnection::handle($databaseName1);
674
            $res1 = $db1->exec('SELECT * FROM profile_option_dict');
675
            if ($res1->num_rows == $this->profile_option_ct) {
676
                $this->testReturn(\core\common\Entity::L_OK, "The $databaseName1 database appears to be OK.");
677
            } else {
678
                $this->testReturn(\core\common\Entity::L_ERROR, "The $databaseName1 database is reacheable but probably not updated to this version of CAT.");
679
            }
680
        } catch (Exception $e) {
681
            $this->testReturn(\core\common\Entity::L_ERROR, "Connection to the  $databaseName1 database failed");
682
        }
683
684
        $databaseName2 = 'USER';
685
        try {
686
            $db2 = DBConnection::handle($databaseName2);
687
            if (CONFIG_CONFASSISTANT['CONSORTIUM']['name'] == "eduroam" && isset(CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo']) && CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo'] == "Operations Team") { // SW: APPROVED
688
                $res2 = $db2->exec('desc view_admin');
689
                if ($res2->num_rows == $this->view_admin_ct) {
690
                    $this->testReturn(\core\common\Entity::L_OK, "The $databaseName2 database appears to be OK.");
691
                } else {
692
                    $this->testReturn(\core\common\Entity::L_ERROR, "The $databaseName2 is reacheable but there is something wrong with the schema");
693
                }
694
            } else {
695
                $this->testReturn(\core\common\Entity::L_OK, "The $databaseName2 database appears to be OK.");
696
            }
697
        } catch (Exception $e) {
698
            $this->testReturn(\core\common\Entity::L_ERROR, "Connection to the  $databaseName2 database failed");
699
        }
700
701
        $databaseName3 = 'EXTERNAL';
702
        if (!empty(CONFIG['DB'][$databaseName3])) {
703
            try {
704
                $db3 = DBConnection::handle($databaseName3);
705
                if (CONFIG_CONFASSISTANT['CONSORTIUM']['name'] == "eduroam" && isset(CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo']) && CONFIG_CONFASSISTANT['CONSORTIUM']['deployment-voodoo'] == "Operations Team") { // SW: APPROVED
706
                    $res3 = $db3->exec('desc view_admin');
707
                    if ($res3->num_rows == $this->view_admin_ct) {
708
                        $this->testReturn(\core\common\Entity::L_OK, "The $databaseName3 database appears to be OK.");
709
                    } else {
710
                        $this->testReturn(\core\common\Entity::L_ERROR, "The $databaseName3 is reacheable but there is something wrong with the schema");
711
                    }
712
                } else {
713
                    $this->testReturn(\core\common\Entity::L_OK, "The $databaseName3 database appears to be OK.");
714
                }
715
            } catch (Exception $e) {
716
717
                $this->testReturn(\core\common\Entity::L_ERROR, "Connection to the  $databaseName3 database failed");
718
            }
719
        }
720
    }
721
722
    /**
723
     * test devices.php for the no_cache option
724
     * 
725
     * @return void
726
     */
727
    private function device_cache_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::device_cache_test" is not in camel caps format
Loading history...
728
        if ((!empty(\devices\Devices::$Options['no_cache'])) && \devices\Devices::$Options['no_cache']) {
729
            $global_no_cache = 1;
730
        } else {
731
            $global_no_cache = 0;
732
        }
733
734
        if ($global_no_cache == 1) {
735
            $this->testReturn(\core\common\Entity::L_WARN, "Devices no_cache global option is set, this is not a good idea in a production setting\n");
736
        }
737
        $Devs = \devices\Devices::listDevices();
738
        $no_cache_dev = '';
739
        $no_cache_dev_count = 0;
740
        if ($global_no_cache) {
741
            foreach ($Devs as $dev => $D) {
742
                if (empty($D['options']['no_cache']) || $D['options']['no_cache'] != 0) {
743
                    $no_cache_dev .= $dev . " ";
744
                    $no_cache_dev_count++;
745
                }
746
            }
747
        } else {
748
            foreach ($Devs as $dev => $D) {
749
                if (!empty($D['options']['no_cache']) && $D['options']['no_cache'] != 0) {
750
                    $no_cache_dev .= $dev . " ";
751
                    $no_cache_dev_count++;
752
                }
753
            }
754
        }
755
756
757
        if ($no_cache_dev_count > 1) {
758
            $this->testReturn(\core\common\Entity::L_WARN, "The following devices will not be cached: $no_cache_dev");
759
        }
760
        if ($no_cache_dev_count == 1) {
761
            $this->testReturn(\core\common\Entity::L_WARN, "The following device will not be cached: $no_cache_dev");
762
        }
763
    }
764
765
    /**
766
     * test if mailer works
767
     * 
768
     * @return void
769
     */
770
    private function mailer_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::mailer_test" is not in camel caps format
Loading history...
771
        if (empty(CONFIG['APPEARANCE']['abuse-mail']) || CONFIG['APPEARANCE']['abuse-mail'] == "[email protected]") {
772
            $this->testReturn(\core\common\Entity::L_ERROR, "Your abuse-mail has not been set, cannot continue with mailer tests.");
773
            return;
774
        }
775
        $mail = new \PHPMailer\PHPMailer\PHPMailer();
776
        $mail->isSMTP();
777
        $mail->Port = 587;
778
        $mail->SMTPAuth = true;
779
        $mail->SMTPSecure = 'tls';
780
        $mail->Host = CONFIG['MAILSETTINGS']['host'];
781
        $mail->Username = CONFIG['MAILSETTINGS']['user'];
782
        $mail->Password = CONFIG['MAILSETTINGS']['pass'];
783
        $mail->SMTPOptions = CONFIG['MAILSETTINGS']['options'];
784
        $mail->WordWrap = 72;
785
        $mail->isHTML(FALSE);
786
        $mail->CharSet = 'UTF-8';
787
        $mail->From = CONFIG['APPEARANCE']['from-mail'];
788
        $mail->FromName = CONFIG['APPEARANCE']['productname'] . " Invitation System";
789
        $mail->addAddress(CONFIG['APPEARANCE']['abuse-mail']);
790
        $mail->Subject = "testing CAT configuration mail";
791
        $mail->Body = "Testing CAT mailing\n";
792
        $sent = $mail->send();
793
        if ($sent) {
794
            $this->testReturn(\core\common\Entity::L_OK, "mailer settings appear to be working, check " . CONFIG['APPEARANCE']['abuse-mail'] . " mailbox if the message was receiced.");
795
        } else {
796
            $this->testReturn(\core\common\Entity::L_ERROR, "mailer settings failed, check the Config::MAILSETTINGS");
797
        }
798
    }
799
800
    /**
801
     * TODO test if RADIUS connections work
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
802
     * 
803
     * @return void
804
     */
805
    private function UDPhosts_test() {
0 ignored issues
show
Coding Style introduced by
Method name "SanityTests::UDPhosts_test" is not in camel caps format
Loading history...
806
//        if(empty)
807
    }
808
809
}
810