Config.php 18.1 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\Path;
Marc Egger's avatar
Marc Egger committed
15
16
use IMATHUZH\Qfq\Core\Helper\Support;
use IMATHUZH\Qfq\Core\Helper\OnString;
17

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

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

31
32
33
34
35
    /**
     * 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
36
     * @throws \UserReportException
37
     */
38
    private static function getCustomVariable(array $config) {
39
40
41
42
43
44

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

45
                    $arr[0] = trim($arr[0]);
46
                    $arr[1] = OnString::trimQuote(trim($arr[1]));
47
48

                    if (isset($config[$arr[0]])) {
Marc Egger's avatar
Marc Egger committed
49
                        throw new \UserReportException("Variable '$arr[0]' already defined", ERROR_INVALID_OR_MISSING_PARAMETER);
50
51
52
53
54
55
56
57
58
59
                    }

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

        return $config;
    }

60
61
62
63
64
    /**
     * Overwrite the config file with data from given array.
     *
     * @param array $config
     * @throws \UserFormException
65
     * @throws \CodeException
66
     */
67
    public static function writeConfig(array $config)
68
69
70
71
72
73
    {
        $cwdToProject = Path::cwdToProject();
        HelperFile::createPathRecursive($cwdToProject);
        HelperFile::file_put_contents(Path::join($cwdToProject, CONFIG_QFQ_JSON), json_encode($config, JSON_PRETTY_PRINT));
    }

74
    /**
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
     * Replace typo3conf/config.qfq.php with fileadmin/protected/qfqProject/qfq.json
     *
     * @throws \CodeException
     * @throws \UserFormException
     */
    public static function migrateConfigPhpToJson(): void
    {
        // read old config.qfq.php
        $cwdToOldConfigFile = Path::cwdToApp(Path::APP_TO_TYPO3_CONFIG, CONFIG_QFQ_PHP);
        HelperFile::enforce_writable($cwdToOldConfigFile);  // so we can delete it.
        $config = include($cwdToOldConfigFile);

        // write new qfq.config.json
        self::writeConfig($config);

        // remove old
        HelperFile::unlink($cwdToOldConfigFile);
    }

    /**
     * Read qfq.json (merge with Typo3-qfq config if exists)
     * Deprecated config file typo3conf/config.qfq.php is translated to JSON in PATH:cwdToProject(..)
97
     *
98
     * @param string $PhpUnitOverloadCwdToConfigFile
99
     * @return array
Marc Egger's avatar
Marc Egger committed
100
101
102
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
103
     */
104
105
106
107
108
    public static function readConfig($PhpUnitOverloadCwdToConfigFile = '') {

        // read and parse config
        $cwdToConfigFile = $PhpUnitOverloadCwdToConfigFile === '' ? Path::cwdToProject(CONFIG_QFQ_JSON) : $PhpUnitOverloadCwdToConfigFile;
        if (!file_exists($cwdToConfigFile)) {
Marc Egger's avatar
Marc Egger committed
109
            HelperFile::file_put_contents(Path::cwdToProject(CONFIG_QFQ_JSON_EXAMPLE), json_encode(self::CONFIG_REQUIRED_TEMPLATE, JSON_PRETTY_PRINT));
110
            Thrower::userFormException("Please create qfq config file '" . CONFIG_QFQ_JSON . "' in project directory. Example config file '" . CONFIG_QFQ_JSON_EXAMPLE . "' was created in project directory.", "Project directory: " . realpath(Path::cwdToProject()));
111
        }
112
        $config = HelperFile::json_decode(HelperFile::file_get_contents($cwdToConfigFile));
113

114
        // check required keys
Marc Egger's avatar
Marc Egger committed
115
        foreach (self::CONFIG_REQUIRED_TEMPLATE as $key => $value) {
116
117
118
119
            if (!array_key_exists($key, $config) || is_null($config[$key]) || $config[$key] === '') {
                Thrower::userFormException("Required key '$key' missing in config file " . CONFIG_QFQ_JSON,  "Config file: $cwdToConfigFile");
            }
        }
120

121
        if ($PhpUnitOverloadCwdToConfigFile === '') {
122

123
            // Settings in qfq.json overwrite T3 settings
124
            $configT3qfq = self::readTypo3QfqConfig();
125
            $config = array_merge($configT3qfq, $config);
126
        }
127

128
        $config = self::renameConfigElements($config);
129
        $config = self::setDefaults($config);
130
        self::checkDeprecated($config);
131
132
        self::checkForAttack($config);

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

136
137
138
        return $config;
    }

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    /**
     * 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
     */
    private static function readTypo3QfqConfig(): array
    {
        $configT3qfq = array();
        if (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][EXT_KEY])) {
            $configT3qfq = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][EXT_KEY]);
            $configT3qfq[SYSTEM_DB_NAME_T3] = self::getDbName($GLOBALS['TYPO3_CONF_VARS']['DB']);

        } elseif (is_readable(Path::cwdToApp(Path::APP_TO_TYPO3_CONFIG, CONFIG_T3))) {
            $cwdToTypo3ConfigFile = Path::cwdToApp(Path::APP_TO_TYPO3_CONFIG, CONFIG_T3);
            $configT3 = HelperFile::include($cwdToTypo3ConfigFile);
            $configT3qfq = unserialize($configT3['EXT']['extConf'][EXT_KEY]);
            if (!is_array($configT3qfq)) {
                Thrower::userFormException('Error read file', "Error while unserializing qfq config from: $cwdToTypo3ConfigFile");
            }
            $configT3qfq[SYSTEM_DB_NAME_T3] = self::getDbName($configT3['DB']);
            unset($configT3);
        }
        $configT3qfq = self::getCustomVariable($configT3qfq);
        return $configT3qfq;
    }

168
169
170
171
172
173
    /**
     * Returns T3 DB-Name, depending on T3 version
     *
     * @param array $db
     * @return mixed
     */
Carsten  Rose's avatar
Carsten Rose committed
174
    private static function getDbName(array $db) {
175
176
177
178
179

        // 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'];
    }

180
181
    /**
     * Checks for deprecated options.
182
     *
183
     * @param array $config
Marc Egger's avatar
Marc Egger committed
184
     * @throws \UserFormException
185
186
187
188
189
190
191
192
193
     */
    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:
194
                        $msg = 'Replaced by: ' . SYSTEM_FILL_STORE_SYSTEM_BY_SQL . '1|2|3';
195
                }
Marc Egger's avatar
Marc Egger committed
196
                throw new \UserFormException ("Deprecated option in " . CONFIG_QFQ_PHP . ": " . SYSTEM_VAR_ADD_BY_SQL . " - " . $msg);
197
198
199
200
            }
        }
    }

201
    /**
202
203
     * Check for attack
     *
204
     * @param array $config
Marc Egger's avatar
Marc Egger committed
205
206
207
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
208
     */
209
    public static function checkForAttack(array $config) {
210
        $attack = false;
211
        $key = '';
212
        $reason = 'Problem: ';
213
214
215
216

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

218
219
220
221
            $key = trim($key);
            if ($key === '') {
                continue;
            }
222
223

            if (!empty($_POST[$key])) {
224
                $attack = true;
225
                $reason .= "Post/Get Honeypot variable '$key' detected: " . htmlentities($_POST[$key]) . PHP_EOL;
226
227
228
229
230
            }
        }

        // Limit length of all get vars: protect against SQL injection based on long ...%34%34%24%34...
        $maxLength = $config[SYSTEM_SECURITY_GET_MAX_LENGTH];
231
        if ($maxLength > 0 && $attack === false) {
232
            foreach ($_GET as $key => $value) {
Carsten  Rose's avatar
Carsten Rose committed
233
234
235
236
237

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

238
239
                // 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);
240

241
242
                $cnt = count($arr);
                if ($cnt > 1 && is_numeric($arr[$cnt - 1])) {
243
244
245
246
247
                    $maxLength = $arr[$cnt - 1];
                } else {
                    $maxLength = $config[SYSTEM_SECURITY_GET_MAX_LENGTH]; // might change again.
                }

248
249
                $len = strlen($value);
                if ($len > $maxLength) {
250
                    $attack = true;
251
                    $reason .= "Value of GET variable '$key' too long. Allowed: $maxLength, Length: $len. Value: '" . htmlentities($_GET[$key]) . "'" . PHP_EOL;
252
                }
253
254
255
256
            }
        }

        // Nothing found?
257
        if ($attack === false) {
258
259
260
            return;
        }

261
        self::attackDetectedExitNow($config, $reason);
262
263
264
    }

    /**
265
     * @param array $config
266
     * @param string $reason
Marc Egger's avatar
Marc Egger committed
267
268
269
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
270
     */
271
    public static function attackDetectedExitNow(array $config = array(), $reason = '') {
272
273
274

        if (count($config) == 0) {
            $config = self::readConfig();
275
276
        }

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

279
280
281
        // In case of an attack: log out the current user.
        Session::destroy();

282
283
284
285
        // 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];

Carsten  Rose's avatar
Carsten Rose committed
286
287
288
        if (!defined('PHPUNIT_QFQ')) {
            sleep($penalty);
        }
289

290
        if ($config[SYSTEM_SECURITY_SHOW_MESSAGE] == 'true' || $config[SYSTEM_SECURITY_SHOW_MESSAGE] == 1) {
291

292
            echo "Attack detected - stop process <p>" . $reason . '</p>';
293
294
295
296
297
298
299
//            $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);
300
301
        }

302
        if (defined('PHPUNIT_QFQ')) {
Marc Egger's avatar
Marc Egger committed
303
            throw new \UserFormException('Attack detected', 1);
304
305
        }

306
307
308
309
310
        exit;
    }

    /**
     * @param array $config
Carsten  Rose's avatar
Carsten Rose committed
311
     *
312
313
     * @return array
     */
314
    public static function setDefaults(array $config) {
315
316
317

        $default = [

318
            SYSTEM_DB_INIT => 'set names utf8',
319
320
            SYSTEM_DB_INDEX_DATA => DB_INDEX_DEFAULT,
            SYSTEM_DB_INDEX_QFQ => DB_INDEX_DEFAULT,
321

322
            SYSTEM_RENDER => SYSTEM_RENDER_SINGLE,
323
324
            SYSTEM_DATE_FORMAT => 'yyyy-mm-dd',
            SYSTEM_SHOW_DEBUG_INFO => SYSTEM_SHOW_DEBUG_INFO_AUTO,
325
326
327
328
            SYSTEM_QFQ_LOG_PATH => '',
            SYSTEM_MAIL_LOG_PATHFILENAME => '',
            SYSTEM_QFQ_LOG_PATHFILENAME => '',
            SYSTEM_SQL_LOG_PATHFILENAME => '',
329
            SYSTEM_SQL_LOG_MODE => 'modify',
330
            SYSTEM_SQL_LOG_MODE_AUTOCRON => 'error',
331
332
333
334
            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',
335

336
337
            F_CLASS => 'qfq-notify',

338
339
340
341
            F_CLASS_PILL => 'qfq-color-grey-1',
            F_CLASS_BODY => 'qfq-color-grey-2',

            F_SAVE_BUTTON_TEXT => '',
342
            F_SAVE_BUTTON_TOOLTIP => '',
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
            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',
363
            SYSTEM_SECURITY_VARS_HONEYPOT => SYSTEM_SECURITY_VARS_HONEYPOT_NAMES,
364
365
366
            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,
367
368
369

            SYSTEM_LABEL_ALIGN => SYSTEM_LABEL_ALIGN_LEFT,

370
            SYSTEM_ESCAPE_TYPE_DEFAULT => TOKEN_ESCAPE_MYSQL,
371
372
            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>',
373
374
375
376
377
            SYSTEM_EXTRA_BUTTON_INFO_CLASS => '',

            SYSTEM_DB_UPDATE => SYSTEM_DB_UPDATE_AUTO,
            SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS => SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS_DEFAULT,

378
            SYSTEM_SESSION_TIMEOUT_SECONDS => self::getPhpSessionTimeout(),
379

380
            SYSTEM_DOCUMENTATION_QFQ => SYSTEM_DOCUMENTATION_QFQ_URL,
381
382
            SYSTEM_ENTER_AS_SUBMIT => 1,

383
            SYSTEM_CMD_WKHTMLTOPDF => '/opt/wkhtmltox/bin/wkhtmltopdf',
384
385
            SYSTEM_CMD_INKSCAPE => 'inkscape',
            SYSTEM_CMD_CONVERT => 'convert',
386
387
388
389
            SYSTEM_CMD_QPDF => 'qpdf',
            SYSTEM_CMD_GS => 'gs',
            SYSTEM_CMD_PDFUNITE => 'pdfunite',

390
391
            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,
392

393
            F_FE_DATA_REQUIRED_ERROR => F_FE_DATA_REQUIRED_ERROR_DEFAULT,
394
            F_FE_DATA_MATCH_ERROR => F_FE_DATA_MATCH_ERROR_DEFAULT,
395
            F_FE_DATA_ERROR => F_FE_DATA_ERROR_DEFAULT,
396
            F_FE_DATA_PATTERN_ERROR => F_FE_DATA_PATTERN_ERROR_DEFAULT,
397
398
399

            SYSTEM_FLAG_PRODUCTION => 'yes',
            SYSTEM_THROW_GENERAL_ERROR => 'auto',
Carsten  Rose's avatar
Carsten Rose committed
400
401

            SYSTEM_SECURITY_FAILED_AUTH_DELAY => '3',
402
403
404

            SYSTEM_FILE_MAX_FILE_SIZE => min(Support::returnBytes(ini_get('post_max_size')), Support::returnBytes(ini_get('upload_max_filesize'))),

405
406
        ];

407
408
409
410
411
412
        foreach ($default as $key => $value) {
            if (!isset($config[$key]) || $config[$key] == '') {
                $config[$key] = $value;
            }
        }

Marc Egger's avatar
Newdoc    
Marc Egger committed
413
414
415
416
417
        // don't accept deprecated documentation url
        if ($config[SYSTEM_DOCUMENTATION_QFQ] === 'https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html') {
            $config[SYSTEM_DOCUMENTATION_QFQ] = SYSTEM_DOCUMENTATION_QFQ_URL;
        }

418
        return $config;
419
420
    }

421

422
423
    /**
     * Rename Elements defined in config.qfq.ini to more appropriate in user interaction.
Carsten  Rose's avatar
Carsten Rose committed
424
425
     * E.g.: in config.qfq.ini everything is in upper case and word space is '_'. In Form.parameter it's lowercase and
     * camel hook.
426
427
     *
     * @param array $config
Carsten  Rose's avatar
Carsten Rose committed
428
     *
429
430
431
432
433
434
     * @return array
     */
    private static function renameConfigElements(array $config) {

        // oldname > newname
        $setting = [
435
            [SYSTEM_FORM_BS_COLUMNS, F_BS_COLUMNS],
436
437
438
            [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],
439

440
441
442
443
            [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],
444

445
446
447
            [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],
448
            [SYSTEM_SAVE_BUTTON_CLASS_ON_CHANGE, F_BUTTON_ON_CHANGE_CLASS],
449

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        ];

        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;
    }
466
467

    /**
468
469
     * Check Session Timeout
     *
470
     * @param $timeout
Marc Egger's avatar
Marc Egger committed
471
     * @throws \UserFormException
472
473
474
     */
    public static function checkSessionTimeout($timeout) {

475
        if (self::getPhpSessionTimeout() < $timeout) {
Marc Egger's avatar
Marc Egger committed
476
            throw new \UserFormException ("The specified timeout of $timeout seconds is higher than the PHP config 'session.gc_maxlifetime' ("
477
478
                . ini_get('session.gc_maxlifetime') . ") and/or 'session.cookie_lifetime' ("
                . ini_get('session.cookie_lifetime') . ")");
479
480
481
        }

        if ($timeout > SYSTEM_COOKIE_LIFETIME) {
Marc Egger's avatar
Marc Egger committed
482
            throw new \UserFormException ("The specified timeout of $timeout seconds is higher than the hardcoded cookie lifetime (" . SYSTEM_COOKIE_LIFETIME . ")");
483
484
        }
    }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

    /**
     * Get the minimum of 'session.cookie_lifetime' and 'session.gc_maxlifetime'
     * A zero means unlimited and will be limited to one day.
     *
     * @return int|string
     */
    private static function getPhpSessionTimeout() {
        $timeout = ini_get('session.cookie_lifetime');
        $gc_maxlifetime = ini_get('session.gc_maxlifetime');

        if ($timeout == 0) {
            $timeout = 86400;
        }

        if ($gc_maxlifetime == 0) {
            // Just to set a limit
            $timeout = 86400;
        }

        return $timeout > $gc_maxlifetime ? $gc_maxlifetime : $timeout;
    }
507
}