Passed
Push — master ( fda4c0...e41819 )
by Stefan
04:59 queued 11s
created

SanityTests::__construct()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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