Passed
Branch master (1136d4)
by Stefan
05:08
created

SanityTests::defaults_test()   C

Complexity

Conditions 12
Paths 96

Size

Total Lines 33
Code Lines 20

Duplication

Lines 11
Ratio 33.33 %

Importance

Changes 0
Metric Value
cc 12
eloc 20
nc 96
nop 0
dl 11
loc 33
rs 5.1612
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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