Config.php 22.9 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 3/6/17
 * Time: 8:47 PM
 */

Marc Egger's avatar
Marc Egger committed
9
namespace IMATHUZH\Qfq\Core\Store;
10

11
use IMATHUZH\Qfq\Core\Exception\Thrower;
12
use IMATHUZH\Qfq\Core\Helper\HelperFile;
Marc Egger's avatar
Marc Egger committed
13
use IMATHUZH\Qfq\Core\Helper\Logger;
14
use IMATHUZH\Qfq\Core\Helper\OnArray;
15
use IMATHUZH\Qfq\Core\Helper\OnString;
16
use IMATHUZH\Qfq\Core\Helper\Path;
Marc Egger's avatar
Marc Egger committed
17
use IMATHUZH\Qfq\Core\Helper\Support;
18

19
20
21
22
/**
 * Class Config
 * @package qfq
 */
23
24
class Config {

25
26
    private static $config = null;

Marc Egger's avatar
Marc Egger committed
27
28
29
30
    const CONFIG_REQUIRED_TEMPLATE = [
        SYSTEM_DB_1_USER => null,
        SYSTEM_DB_1_PASSWORD => null,
        SYSTEM_DB_1_SERVER => "localhost",
31
        SYSTEM_DB_1_NAME => null,
Marc Egger's avatar
Marc Egger committed
32
    ];
33

34
35
36
37
38
    /**
     * Get config value with given key. Throws exception if config has not been read.
     *
     * @param string $key
     * @return mixed
39
     * @throws \CodeException
40
     * @throws \UserFormException
41
     * @throws \UserReportException
42
     */
43
    public static function get(string $key) {
44
        self::readConfig(); // only reads once
45
        return self::$config[$key] ?? null;
46
47
48
49
50
    }

    /**
     * Returns a copy of the config array. Throws exception if config has not been read.
     *
51
     * @param string $PhpUnitOverloadAbsoluteConfigFilePath
52
     * @return array
53
     * @throws \CodeException
54
     * @throws \UserFormException
55
     * @throws \UserReportException
56
     */
57
58
    public static function getConfigArray($PhpUnitOverloadAbsoluteConfigFilePath = ''): array {
        self::readConfig($PhpUnitOverloadAbsoluteConfigFilePath); // only reads once, except if argument !=''
59
60
61
62
        return self::$config;
    }

    /**
Marc Egger's avatar
Marc Egger committed
63
     * Read qfq.json (merge with Typo3-qfq config if exists).
64
     * Note: Deprecated config file typo3conf/config.qfq.php is translated to JSON in PATH:findAppToProject(..)
65
     *
66
     * @param string $PhpUnitOverloadAbsoluteConfigFilePath
67
68
69
70
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
     */
71
    private static function readConfig($PhpUnitOverloadAbsoluteConfigFilePath = '') {
72

73
        if (self::$config !== null && $PhpUnitOverloadAbsoluteConfigFilePath === '') {
74
            // only read once, except phpUnit
75
76
77
78
            return;
        }

        // read and parse config. Throw exception if not exists.
79
80
81
82
        $absoluteConfigFilePath = $PhpUnitOverloadAbsoluteConfigFilePath === '' ? Path::absoluteConf(CONFIG_QFQ_JSON) : $PhpUnitOverloadAbsoluteConfigFilePath;
        if (!file_exists($absoluteConfigFilePath)) {
            HelperFile::createPathRecursive(Path::absoluteConf());
            HelperFile::file_put_contents(Path::absoluteConf(CONFIG_QFQ_JSON_EXAMPLE), json_encode(self::CONFIG_REQUIRED_TEMPLATE, JSON_PRETTY_PRINT));
83
            Thrower::userFormException("Please create qfq config file '" . CONFIG_QFQ_JSON . "' in path fileadmin/protected/qfqProject/conf/ which is inside the project directory. Example config file '" . CONFIG_QFQ_JSON_EXAMPLE . "' was created in conf directory.", "Project directory: " . Path::absoluteProject());
84
        }
85
        $config = HelperFile::json_decode(HelperFile::file_get_contents($absoluteConfigFilePath));
86
87
88
89

        // check required keys
        foreach (self::CONFIG_REQUIRED_TEMPLATE as $key => $value) {
            if (!array_key_exists($key, $config) || is_null($config[$key]) || $config[$key] === '') {
90
                Thrower::userFormException("Required key '$key' missing in config file " . CONFIG_QFQ_JSON, "Config file: $absoluteConfigFilePath");
91
92
93
            }
        }

94
        if ($PhpUnitOverloadAbsoluteConfigFilePath === '') {
Marc Egger's avatar
Marc Egger committed
95
            $configT3qfq = self::readTypo3QfqConfig();
96
97
98
99
100
101
102
103
104
105
106
107
108

            // Settings in qfq.json overwrite T3 settings
            $config = array_merge($configT3qfq, $config);
        }

        $config = self::renameConfigElements($config);
        $config = self::setDefaults($config);
        self::checkDeprecated($config);
        self::checkForAttack($config);

        // Copy values to detect custom settings later
        $config[F_FE_DATA_PATTERN_ERROR_SYSTEM] = $config[F_FE_DATA_PATTERN_ERROR];

109
110
111
112
113
        $config = self::adjustConfig($config);
        $config = self::setAutoConfigValue($config);

        self::checkMandatoryParameter($config);

114
115
116
117
118
119
        self::$config = $config;

        // Set log paths
        Path::overrideLogPathsFromConfig();
    }

120
121
122
123
124
    /**
     * Iterates over all 30 custom vars, explode them to split between key and value, append to $config.
     *
     * @param array $config
     * @return array
Marc Egger's avatar
Marc Egger committed
125
     * @throws \UserReportException
126
     */
127
    private static function getCustomVariable(array $config) {
128
129
130
131
132
133

        for ($i = 1; $i <= 30; $i++) {
            if (isset($config['custom' . $i])) {
                $arr = explode('=', $config['custom' . $i], 2);
                if (!empty($arr[0]) && !empty($arr[1])) {

134
                    $arr[0] = trim($arr[0]);
135
                    $arr[1] = OnString::trimQuote(trim($arr[1]));
136
137

                    if (isset($config[$arr[0]])) {
Marc Egger's avatar
Marc Egger committed
138
                        throw new \UserReportException("Variable '$arr[0]' already defined", ERROR_INVALID_OR_MISSING_PARAMETER);
139
140
141
142
143
144
145
146
147
148
                    }

                    $config[$arr[0]] = $arr[1];
                }
            }
        }

        return $config;
    }

149
    /**
150
     * Overwrite the qfq config file with data from given array.
151
152
153
     *
     * @param array $config
     * @throws \UserFormException
154
     * @throws \CodeException
155
     */
156
    private static function writeConfig(array $config) {
157
158
159
        $absoluteConf = Path::absoluteConf();
        HelperFile::createPathRecursive($absoluteConf);
        HelperFile::file_put_contents(Path::join($absoluteConf, CONFIG_QFQ_JSON), json_encode($config, JSON_PRETTY_PRINT));
160
161
    }

162
    /**
163
164
165
166
167
     * Replace typo3conf/config.qfq.php with fileadmin/protected/qfqProject/qfq.json
     *
     * @throws \CodeException
     * @throws \UserFormException
     */
168
    public static function migrateConfigPhpToJson(): void {
169
        // read old config.qfq.php
170
171
        $absoluteOldConfigFilePath = Path::absoluteApp(Path::APP_TO_TYPO3_CONF, CONFIG_QFQ_PHP);
        if (!is_writeable($absoluteOldConfigFilePath)) {
172
173
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => "Can't migrate to new config: Legacy config file `config.qfq.php` not writable.",
174
                ERROR_MESSAGE_TO_DEVELOPER => "Can't write to file/directory '$absoluteOldConfigFilePath'"]));
175
        }
176
        $config = include($absoluteOldConfigFilePath);
177

178
179
180
181
182
183
        // In case the database credentials are given in the old style: rename the keys
        $config = OnArray::renameKeys([
            SYSTEM_DB_USER => SYSTEM_DB_1_USER,
            SYSTEM_DB_SERVER => SYSTEM_DB_1_SERVER,
            SYSTEM_DB_PASSWORD => SYSTEM_DB_1_PASSWORD,
            SYSTEM_DB_NAME => SYSTEM_DB_1_NAME
184
        ], $config);
185

186
187
188
189
        // write new qfq.config.json
        self::writeConfig($config);

        // remove old
190
        HelperFile::unlink($absoluteOldConfigFilePath);
191
192
    }

193
194
195
196
197
198
199
200
    /**
     * Read Typo3-QFQ config first from global variable, then from typo3conf/Localconfig.php.
     * If both not exist, return empty array.
     *
     * @return array Empty if config not found.
     * @throws \UserFormException
     * @throws \UserReportException
     */
201
    private static function readTypo3QfqConfig(): array {
202
        $configT3qfq = array();
Marc Egger's avatar
Marc Egger committed
203
        if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][EXT_KEY])) {
204
            // Typo3 version >=9
Marc Egger's avatar
Marc Egger committed
205
206
207
208
209
            $configT3qfq = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS'][EXT_KEY];
            $configT3qfq[SYSTEM_DB_NAME_T3] = self::getDbName($GLOBALS['TYPO3_CONF_VARS']['DB']);

        } elseif (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][EXT_KEY])) {
            // Typo3 version <=8
210
211
212
            $configT3qfq = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][EXT_KEY]);
            $configT3qfq[SYSTEM_DB_NAME_T3] = self::getDbName($GLOBALS['TYPO3_CONF_VARS']['DB']);

213
214
215
        } elseif (is_readable(Path::absoluteApp(Path::APP_TO_TYPO3_CONF, CONFIG_T3))) {
            $absoluteTypo3ConfigFile = Path::absoluteApp(Path::APP_TO_TYPO3_CONF, CONFIG_T3);
            $configT3 = HelperFile::include($absoluteTypo3ConfigFile);
Marc Egger's avatar
Marc Egger committed
216
            if (isset($configT3['EXTENSIONS'][EXT_KEY])) {
217
                // Typo3 version >=9
Marc Egger's avatar
Marc Egger committed
218
219
220
221
222
                $configT3qfq = $configT3['EXTENSIONS'][EXT_KEY];
            } else {
                // Typo3 version <=8
                $configT3qfq = unserialize($configT3['EXT']['extConf'][EXT_KEY]);
            }
223
            if (!is_array($configT3qfq)) {
224
                Thrower::userFormException('Error read file', "Error while reading qfq config from: $absoluteTypo3ConfigFile");
225
226
227
228
229
230
231
232
            }
            $configT3qfq[SYSTEM_DB_NAME_T3] = self::getDbName($configT3['DB']);
            unset($configT3);
        }
        $configT3qfq = self::getCustomVariable($configT3qfq);
        return $configT3qfq;
    }

233
234
235
236
237
238
    /**
     * Returns T3 DB-Name, depending on T3 version
     *
     * @param array $db
     * @return mixed
     */
Carsten  Rose's avatar
Carsten Rose committed
239
    private static function getDbName(array $db) {
240
241
242
243
244

        // T3 7.x: $GLOBALS['TYPO3_CONF_VARS']['DB']['database'],  T3 8.x: $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['dbname']
        return isset($db['database']) ? $db['database'] : $db['Connections']['Default']['dbname'];
    }

245
246
    /**
     * Checks for deprecated options.
247
     *
248
     * @param array $config
Marc Egger's avatar
Marc Egger committed
249
     * @throws \UserFormException
250
251
252
253
254
255
256
257
258
     */
    private static function checkDeprecated(array $config) {

        foreach ([SYSTEM_VAR_ADD_BY_SQL] as $key) {

            if (isset($config[$key])) {
                $msg = '';
                switch ($key) {
                    case SYSTEM_VAR_ADD_BY_SQL:
259
                        $msg = 'Replaced by: ' . SYSTEM_FILL_STORE_SYSTEM_BY_SQL . '1|2|3';
260
                }
Marc Egger's avatar
Marc Egger committed
261
                throw new \UserFormException ("Deprecated option in " . CONFIG_QFQ_PHP . ": " . SYSTEM_VAR_ADD_BY_SQL . " - " . $msg);
262
263
264
265
            }
        }
    }

266
    /**
267
268
     * Check for attack
     *
269
     * @param array $config
Marc Egger's avatar
Marc Egger committed
270
271
272
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
273
     */
274
    public static function checkForAttack(array $config) {
275
        $attack = false;
276
        $key = '';
277
        $reason = 'Problem: ';
278
279
280
281

        // Iterate over all fake vars
        $arr = explode(',', $config[SYSTEM_SECURITY_VARS_HONEYPOT]);
        foreach ($arr as $key) {
282

283
284
285
286
            $key = trim($key);
            if ($key === '') {
                continue;
            }
287
288

            if (!empty($_POST[$key])) {
289
                $attack = true;
290
                $reason .= "Post/Get Honeypot variable '$key' detected: " . htmlentities($_POST[$key]) . PHP_EOL;
291
292
293
294
295
            }
        }

        // Limit length of all get vars: protect against SQL injection based on long ...%34%34%24%34...
        $maxLength = $config[SYSTEM_SECURITY_GET_MAX_LENGTH];
296
        if ($maxLength > 0 && $attack === false) {
297
            foreach ($_GET as $key => $value) {
Carsten  Rose's avatar
Carsten Rose committed
298
299
300
301
302

                if (!is_string($value)) {
                    continue;
                }

303
304
                // Check if the variable is something like 'my_name_100' - if the part after the last '_' is numerical, this means a valid, non standard length.
                $arr = explode(GET_EXTRA_LENGTH_TOKEN, $key);
305

306
307
                $cnt = count($arr);
                if ($cnt > 1 && is_numeric($arr[$cnt - 1])) {
308
309
310
311
312
                    $maxLength = $arr[$cnt - 1];
                } else {
                    $maxLength = $config[SYSTEM_SECURITY_GET_MAX_LENGTH]; // might change again.
                }

313
314
                $len = strlen($value);
                if ($len > $maxLength) {
315
                    $attack = true;
316
                    $reason .= "Value of GET variable '$key' too long. Allowed: $maxLength, Length: $len. Value: '" . htmlentities($_GET[$key]) . "'" . PHP_EOL;
317
                }
318
319
320
321
            }
        }

        // Nothing found?
322
        if ($attack === false) {
323
324
325
            return;
        }

326
        self::attackDetectedExitNow($config, $reason);
327
328
329
    }

    /**
330
     * @param array $config
331
     * @param string $reason
Marc Egger's avatar
Marc Egger committed
332
333
334
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
335
     */
336
    public static function attackDetectedExitNow(array $config = array(), $reason = '') {
337
338

        if (count($config) == 0) {
339
            $config = self::getConfigArray();
340
341
        }

342
        Logger::logMessage(Logger::linePre() . 'Security: attack detected' . PHP_EOL . $reason, Path::absoluteQfqLogFile());
343

344
345
346
347
        // Sleep
        $penalty = (empty($config[SYSTEM_SECURITY_ATTACK_DELAY]) || !is_numeric($config[SYSTEM_SECURITY_ATTACK_DELAY])) ?
            SYSTEM_SECURITY_ATTACK_DELAY_DEFAULT : $config[SYSTEM_SECURITY_ATTACK_DELAY];

348
349
350
351
352
353
354
355
356

        // In case of an attack: log out the current user.
        // $penalty of -1 means: no destroy, no sleep, no exit
        if ($penalty != -1) {
            Session::destroy();

            if (!defined('PHPUNIT_QFQ')) {
                sleep($penalty);
            }
Carsten  Rose's avatar
Carsten Rose committed
357
        }
358

359
        if ($config[SYSTEM_SECURITY_SHOW_MESSAGE] == 'true' || $config[SYSTEM_SECURITY_SHOW_MESSAGE] == 1) {
360

361
            echo "Attack detected - stop process <p>" . $reason . '</p>';
362
363
364
365
366
367
368
//            $answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
//            $answer[API_MESSAGE] = 'Attack detected - stop process.';
//            if($getParamName!='') {
//                $answer[API_MESSAGE] .= " Attack parameter: $getParamName";
//            }
//            header("Content-Type: application/json");
//            echo json_encode($answer);
369
370
        }

371
        if (defined('PHPUNIT_QFQ')) {
Marc Egger's avatar
Marc Egger committed
372
            throw new \UserFormException('Attack detected', 1);
373
374
        }

375
376
377
378
379
        // $penalty of -1 means: no destroy, no sleep, no exit
        if ($penalty != -1) {
            return;
        }

380
381
382
383
384
        exit;
    }

    /**
     * @param array $config
Carsten  Rose's avatar
Carsten Rose committed
385
     *
386
387
     * @return array
     */
388
    public static function setDefaults(array $config) {
389
390
391

        $default = [

392
            SYSTEM_DB_INIT => 'set names utf8',
393
394
            SYSTEM_DB_INDEX_DATA => DB_INDEX_DEFAULT,
            SYSTEM_DB_INDEX_QFQ => DB_INDEX_DEFAULT,
395

396
            SYSTEM_RENDER => SYSTEM_RENDER_SINGLE,
397
398
            SYSTEM_DATE_FORMAT => 'yyyy-mm-dd',
            SYSTEM_SHOW_DEBUG_INFO => SYSTEM_SHOW_DEBUG_INFO_AUTO,
399
            SYSTEM_REPORT_MIN_PHP_VERSION => SYSTEM_REPORT_MIN_PHP_VERSION_AUTO,
400
401
402
            SYSTEM_MAIL_LOG_PATHFILENAME => '',
            SYSTEM_QFQ_LOG_PATHFILENAME => '',
            SYSTEM_SQL_LOG_PATHFILENAME => '',
403
            SYSTEM_SQL_LOG_MODE => 'modify',
404
            SYSTEM_SQL_LOG_MODE_AUTOCRON => 'error',
405
406
407
408
            F_BS_COLUMNS => 'col-md-12 col-lg-10',
            F_BS_LABEL_COLUMNS => 'col-md-3 col-lg-3',
            F_BS_INPUT_COLUMNS => 'col-md-6 col-lg-6',
            F_BS_NOTE_COLUMNS => 'col-md-3 col-lg-3',
409

410
411
            F_CLASS => 'qfq-notify',

412
413
414
415
            F_CLASS_PILL => 'qfq-color-grey-1',
            F_CLASS_BODY => 'qfq-color-grey-2',

            F_SAVE_BUTTON_TEXT => '',
416
            F_SAVE_BUTTON_TOOLTIP => '',
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
            F_SAVE_BUTTON_CLASS => 'btn btn-default navbar-btn',
            F_SAVE_BUTTON_GLYPH_ICON => GLYPH_ICON_CHECK,

            F_CLOSE_BUTTON_TEXT => '',
            F_CLOSE_BUTTON_TOOLTIP => 'Close',
            F_CLOSE_BUTTON_CLASS => 'btn btn-default navbar-btn',
            F_CLOSE_BUTTON_GLYPH_ICON => GLYPH_ICON_CLOSE,

            F_DELETE_BUTTON_TEXT => '',
            F_DELETE_BUTTON_TOOLTIP => 'Delete',
            F_DELETE_BUTTON_CLASS => 'btn btn-default navbar-btn',
            F_DELETE_BUTTON_GLYPH_ICON => GLYPH_ICON_DELETE,

            F_NEW_BUTTON_TEXT => '',
            F_NEW_BUTTON_TOOLTIP => 'New',
            F_NEW_BUTTON_CLASS => 'btn btn-default navbar-btn',
            F_NEW_BUTTON_GLYPH_ICON => GLYPH_ICON_NEW,

            F_BUTTON_ON_CHANGE_CLASS => 'btn-info alert-info',
            SYSTEM_EDIT_FORM_PAGE => 'form',
437
            SYSTEM_SECURITY_VARS_HONEYPOT => SYSTEM_SECURITY_VARS_HONEYPOT_NAMES,
438
439
440
            SYSTEM_SECURITY_ATTACK_DELAY => SYSTEM_SECURITY_ATTACK_DELAY_DEFAULT,
            SYSTEM_SECURITY_SHOW_MESSAGE => '0',
            SYSTEM_SECURITY_GET_MAX_LENGTH => SYSTEM_SECURITY_GET_MAX_LENGTH_DEFAULT,
441
442
443

            SYSTEM_LABEL_ALIGN => SYSTEM_LABEL_ALIGN_LEFT,

444
            SYSTEM_ESCAPE_TYPE_DEFAULT => TOKEN_ESCAPE_MYSQL,
445
446
            SYSTEM_EXTRA_BUTTON_INFO_INLINE => '<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>',
            SYSTEM_EXTRA_BUTTON_INFO_BELOW => '<span class="glyphicon glyphicon-info-sign text-info" aria-hidden="true"></span>',
447
448
449
450
451
            SYSTEM_EXTRA_BUTTON_INFO_CLASS => '',

            SYSTEM_DB_UPDATE => SYSTEM_DB_UPDATE_AUTO,
            SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS => SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS_DEFAULT,

452
            SYSTEM_SESSION_TIMEOUT_SECONDS => Session::getPhpSessionTimeout(),
453

454
            SYSTEM_DOCUMENTATION_QFQ => SYSTEM_DOCUMENTATION_QFQ_URL,
455
456
            SYSTEM_ENTER_AS_SUBMIT => 1,

457
            SYSTEM_CMD_WKHTMLTOPDF => '/opt/wkhtmltox/bin/wkhtmltopdf',
Carsten  Rose's avatar
WIP    
Carsten Rose committed
458
            SYSTEM_CMD_QFQPDF => '/opt/qfqpdf/qfqpdf',
459
460
            SYSTEM_CMD_INKSCAPE => 'inkscape',
            SYSTEM_CMD_CONVERT => 'convert',
461
462
            SYSTEM_CMD_PDF2SVG => 'pdf2svg',
            SYSTEM_CMD_PDFTOCAIRO => 'pdftocairo',
463
464
465
            SYSTEM_CMD_QPDF => 'qpdf',
            SYSTEM_CMD_GS => 'gs',
            SYSTEM_CMD_PDFUNITE => 'pdfunite',
466
            SYSTEM_CMD_IMG2PDF => 'img2pdf',
467
            SYSTEM_CMD_HEIF_CONVERT => 'heif-convert',
468

469
470
            SYSTEM_THUMBNAIL_DIR_SECURE_REL_TO_APP => Path::APP_TO_SYSTEM_THUMBNAIL_DIR_SECURE_DEFAULT,
            SYSTEM_THUMBNAIL_DIR_PUBLIC_REL_TO_APP => Path::APP_TO_SYSTEM_THUMBNAIL_DIR_PUBLIC_DEFAULT,
471

472
            F_FE_DATA_REQUIRED_ERROR => F_FE_DATA_REQUIRED_ERROR_DEFAULT,
473
            F_FE_DATA_MATCH_ERROR => F_FE_DATA_MATCH_ERROR_DEFAULT,
474
            F_FE_DATA_ERROR => F_FE_DATA_ERROR_DEFAULT,
475
            F_FE_DATA_PATTERN_ERROR => F_FE_DATA_PATTERN_ERROR_DEFAULT,
476
477
478

            SYSTEM_FLAG_PRODUCTION => 'yes',
            SYSTEM_THROW_GENERAL_ERROR => 'auto',
Carsten  Rose's avatar
Carsten Rose committed
479
480

            SYSTEM_SECURITY_FAILED_AUTH_DELAY => '3',
481
482

            SYSTEM_FILE_MAX_FILE_SIZE => min(Support::returnBytes(ini_get('post_max_size')), Support::returnBytes(ini_get('upload_max_filesize'))),
483
484
        ];

485
486
487
488
489
490
491
        foreach ($default as $key => $value) {
            if (!isset($config[$key]) || $config[$key] == '') {
                $config[$key] = $value;
            }
        }

        return $config;
492
493
    }

494

495
496
    /**
     * Rename Elements defined in config.qfq.ini to more appropriate in user interaction.
Carsten  Rose's avatar
Carsten Rose committed
497
498
     * E.g.: in config.qfq.ini everything is in upper case and word space is '_'. In Form.parameter it's lowercase and
     * camel hook.
499
500
     *
     * @param array $config
Carsten  Rose's avatar
Carsten Rose committed
501
     *
502
503
504
505
506
507
     * @return array
     */
    private static function renameConfigElements(array $config) {

        // oldname > newname
        $setting = [
508
            [SYSTEM_FORM_BS_COLUMNS, F_BS_COLUMNS],
509
510
511
            [SYSTEM_FORM_BS_LABEL_COLUMNS, F_BS_LABEL_COLUMNS],
            [SYSTEM_FORM_BS_INPUT_COLUMNS, F_BS_INPUT_COLUMNS],
            [SYSTEM_FORM_BS_NOTE_COLUMNS, F_BS_NOTE_COLUMNS],
512

513
514
515
516
            [SYSTEM_FORM_DATA_PATTERN_ERROR, F_FE_DATA_PATTERN_ERROR],
            [SYSTEM_FORM_DATA_REQUIRED_ERROR, F_FE_DATA_REQUIRED_ERROR],
            [SYSTEM_FORM_DATA_MATCH_ERROR, F_FE_DATA_MATCH_ERROR],
            [SYSTEM_FORM_DATA_ERROR, F_FE_DATA_ERROR],
517

518
519
520
            [SYSTEM_CSS_CLASS_QFQ_FORM, F_CLASS],
            [SYSTEM_CSS_CLASS_QFQ_FORM_PILL, F_CLASS_PILL],
            [SYSTEM_CSS_CLASS_QFQ_FORM_BODY, F_CLASS_BODY],
521
            [SYSTEM_SAVE_BUTTON_CLASS_ON_CHANGE, F_BUTTON_ON_CHANGE_CLASS],
522

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
        ];

        foreach ($setting as $row) {
            $oldName = $row[0];
            $newName = $row[1];

            if (isset($config[$oldName])) {
                $config[$newName] = $config[$oldName];
                if ($oldName != $newName) {
                    unset($config[$oldName]);
                }
            }
        }

        return $config;
    }
539
540
541
542
543
544
545
546
547

    /**
     * Depending on some configuration value, update corresponding values.
     *
     * @param array $config
     * @return array
     */
    private static function adjustConfig(array $config) {

548
        $config[SYSTEM_SHOW_DEBUG_INFO] = self::adjustConfigDebugInfoAuto($config[SYSTEM_SHOW_DEBUG_INFO], T3Info::beUserLoggedIn());
549
550
551
        if ($config[SYSTEM_REPORT_MIN_PHP_VERSION] == SYSTEM_REPORT_MIN_PHP_VERSION_AUTO && T3Info::beUserLoggedIn()) {
            $config[SYSTEM_REPORT_MIN_PHP_VERSION] = SYSTEM_REPORT_MIN_PHP_VERSION_YES;
        }
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613

        // In case the database credentials are given in the old style: copy them to the new style
        if (!isset($config[SYSTEM_DB_1_USER]) && isset($config[SYSTEM_DB_USER])) {
            $config[SYSTEM_DB_1_USER] = $config[SYSTEM_DB_USER];
            $config[SYSTEM_DB_1_SERVER] = $config[SYSTEM_DB_SERVER];
            $config[SYSTEM_DB_1_PASSWORD] = $config[SYSTEM_DB_PASSWORD];
            $config[SYSTEM_DB_1_NAME] = $config[SYSTEM_DB_NAME];
        }

        if ($config[SYSTEM_THROW_GENERAL_ERROR] == 'auto') {
            $config[SYSTEM_THROW_GENERAL_ERROR] = $config[SYSTEM_FLAG_PRODUCTION] == 'yes' ? 'no' : 'yes';
        }

        return $config;
    }

    /**
     * @param string $value
     *
     * @param $flag
     * @return string
     */
    public static function adjustConfigDebugInfoAuto($value, $flag) {

        // Check if SHOW_DEBUG_INFO contains 'auto'. Replace with appropriate.
        if (Support::findInSet(SYSTEM_SHOW_DEBUG_INFO_AUTO, $value) && $flag) {
            $value = str_replace(SYSTEM_SHOW_DEBUG_INFO_AUTO, SYSTEM_SHOW_DEBUG_INFO_YES, $value);
        }

        return $value;
    }

    /**
     * Set automatic filled values
     *
     * @param array $config
     * @return array
     */
    private static function setAutoConfigValue(array $config) {

        $config[SYSTEM_DB_NAME_DATA] = $config['DB_' . $config[SYSTEM_DB_INDEX_DATA] . '_NAME'] ?? '';
        $config[SYSTEM_DB_NAME_QFQ] = $config['DB_' . $config[SYSTEM_DB_INDEX_QFQ] . '_NAME'] ?? '';

        return $config;
    }

    /**
     * Iterate over all Parameter which have to exist in the config. Throw an array if any is missing.
     *
     * @param array $config
     *
     * @throws \UserFormException
     */
    private static function checkMandatoryParameter(array $config) {

        // Check mandatory config vars.
        $names = array_merge([SYSTEM_SQL_LOG_MODE],
            self::dbCredentialName($config[SYSTEM_DB_INDEX_DATA]),
            self::dbCredentialName($config[SYSTEM_DB_INDEX_QFQ]));

        foreach ($names as $name) {
            if (!isset($config[$name])) {
614
                throw new \UserFormException ("Missing configuration in `" . CONFIG_QFQ_JSON . "`: $name", ERROR_MISSING_CONFIG_INI_VALUE);
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
            }
        }
    }

    /**
     * @param $index
     * @return array
     */
    private static function dbCredentialName($index) {
        $names = array();
        $names[] = 'DB_' . $index . '_USER';
        $names[] = 'DB_' . $index . '_SERVER';
        $names[] = 'DB_' . $index . '_PASSWORD';
        $names[] = 'DB_' . $index . '_NAME';

        return $names;
    }
632
}