Passed
Push — master ( 07f3a1...208b0e )
by Maurício
10:08
created

Config::load()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 59
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 25
dl 0
loc 59
rs 9.52
c 0
b 0
f 0
cc 4
nc 6
nop 1

How to fix   Long Method   

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
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
/**
4
 * Configuration handling.
5
 *
6
 * @package PhpMyAdmin
7
 */
8
declare(strict_types=1);
9
10
namespace PhpMyAdmin;
11
12
use DirectoryIterator;
13
use PhpMyAdmin\Config;
14
use PhpMyAdmin\Core;
15
use PhpMyAdmin\Error;
16
use PhpMyAdmin\LanguageManager;
17
use PhpMyAdmin\Message;
18
use PhpMyAdmin\ThemeManager;
19
use PhpMyAdmin\Url;
20
use PhpMyAdmin\UserPreferences;
21
use PhpMyAdmin\Util;
22
use PhpMyAdmin\Utils\HttpRequest;
23
24
/**
25
 * Indication for error handler (see end of this file).
26
 */
27
$GLOBALS['pma_config_loading'] = false;
28
29
/**
30
 * Configuration class
31
 *
32
 * @package PhpMyAdmin
33
 */
34
class Config
35
{
36
    /**
37
     * @var string  default config source
38
     */
39
    public $default_source = ROOT_PATH . 'libraries/config.default.php';
40
41
    /**
42
     * @var array   default configuration settings
43
     */
44
    public $default = [];
45
46
    /**
47
     * @var array   configuration settings, without user preferences applied
48
     */
49
    public $base_settings = [];
50
51
    /**
52
     * @var array   configuration settings
53
     */
54
    public $settings = [];
55
56
    /**
57
     * @var string  config source
58
     */
59
    public $source = '';
60
61
    /**
62
     * @var int     source modification time
63
     */
64
    public $source_mtime = 0;
65
    public $default_source_mtime = 0;
66
    public $set_mtime = 0;
67
68
    /**
69
     * @var boolean
70
     */
71
    public $error_config_file = false;
72
73
    /**
74
     * @var boolean
75
     */
76
    public $error_config_default_file = false;
77
78
    /**
79
     * @var array
80
     */
81
    public $default_server = [];
82
83
    /**
84
     * @var boolean whether init is done or not
85
     * set this to false to force some initial checks
86
     * like checking for required functions
87
     */
88
    public $done = false;
89
90
    /**
91
     * constructor
92
     *
93
     * @param string $source source to read config from
94
     */
95
    public function __construct(?string $source = null)
96
    {
97
        $this->settings = ['is_setup' => false];
98
99
        // functions need to refresh in case of config file changed goes in
100
        // PhpMyAdmin\Config::load()
101
        $this->load($source);
102
103
        // other settings, independent from config file, comes in
104
        $this->checkSystem();
105
106
        $this->base_settings = $this->settings;
107
    }
108
109
    /**
110
     * sets system and application settings
111
     *
112
     * @return void
113
     */
114
    public function checkSystem(): void
115
    {
116
        $this->set('PMA_VERSION', '5.1.0-dev');
117
        /* Major version */
118
        $this->set(
119
            'PMA_MAJOR_VERSION',
120
            implode('.', array_slice(explode('.', $this->get('PMA_VERSION'), 3), 0, 2))
121
        );
122
123
        $this->checkWebServerOs();
124
        $this->checkWebServer();
125
        $this->checkGd2();
126
        $this->checkClient();
127
        $this->checkUpload();
128
        $this->checkUploadSize();
129
        $this->checkOutputCompression();
130
    }
131
132
    /**
133
     * whether to use gzip output compression or not
134
     *
135
     * @return void
136
     */
137
    public function checkOutputCompression(): void
138
    {
139
        // If zlib output compression is set in the php configuration file, no
140
        // output buffering should be run
141
        if (ini_get('zlib.output_compression')) {
142
            $this->set('OBGzip', false);
143
        }
144
145
        // enable output-buffering (if set to 'auto')
146
        if (strtolower((string) $this->get('OBGzip')) == 'auto') {
147
            $this->set('OBGzip', true);
148
        }
149
    }
150
151
    /**
152
     * Sets the client platform based on user agent
153
     *
154
     * @param string $user_agent the user agent
155
     *
156
     * @return void
157
     */
158
    private function _setClientPlatform(string $user_agent): void
159
    {
160
        if (mb_strstr($user_agent, 'Win')) {
161
            $this->set('PMA_USR_OS', 'Win');
162
        } elseif (mb_strstr($user_agent, 'Mac')) {
163
            $this->set('PMA_USR_OS', 'Mac');
164
        } elseif (mb_strstr($user_agent, 'Linux')) {
165
            $this->set('PMA_USR_OS', 'Linux');
166
        } elseif (mb_strstr($user_agent, 'Unix')) {
167
            $this->set('PMA_USR_OS', 'Unix');
168
        } elseif (mb_strstr($user_agent, 'OS/2')) {
169
            $this->set('PMA_USR_OS', 'OS/2');
170
        } else {
171
            $this->set('PMA_USR_OS', 'Other');
172
        }
173
    }
174
175
    /**
176
     * Determines platform (OS), browser and version of the user
177
     * Based on a phpBuilder article:
178
     *
179
     * @see http://www.phpbuilder.net/columns/tim20000821.php
180
     *
181
     * @return void
182
     */
183
    public function checkClient(): void
184
    {
185
        if (Core::getenv('HTTP_USER_AGENT')) {
186
            $HTTP_USER_AGENT = Core::getenv('HTTP_USER_AGENT');
187
        } else {
188
            $HTTP_USER_AGENT = '';
189
        }
190
191
        // 1. Platform
192
        $this->_setClientPlatform($HTTP_USER_AGENT);
193
194
        // 2. browser and version
195
        // (must check everything else before Mozilla)
196
197
        $is_mozilla = preg_match(
198
            '@Mozilla/([0-9]\.[0-9]{1,2})@',
199
            $HTTP_USER_AGENT,
200
            $mozilla_version
201
        );
202
203
        if (preg_match(
204
            '@Opera(/| )([0-9]\.[0-9]{1,2})@',
205
            $HTTP_USER_AGENT,
206
            $log_version
207
        )) {
208
            $this->set('PMA_USR_BROWSER_VER', $log_version[2]);
209
            $this->set('PMA_USR_BROWSER_AGENT', 'OPERA');
210
        } elseif (preg_match(
211
            '@(MS)?IE ([0-9]{1,2}\.[0-9]{1,2})@',
212
            $HTTP_USER_AGENT,
213
            $log_version
214
        )) {
215
            $this->set('PMA_USR_BROWSER_VER', $log_version[2]);
216
            $this->set('PMA_USR_BROWSER_AGENT', 'IE');
217
        } elseif (preg_match(
218
            '@Trident/(7)\.0@',
219
            $HTTP_USER_AGENT,
220
            $log_version
221
        )) {
222
            $this->set('PMA_USR_BROWSER_VER', intval($log_version[1]) + 4);
223
            $this->set('PMA_USR_BROWSER_AGENT', 'IE');
224
        } elseif (preg_match(
225
            '@OmniWeb/([0-9]{1,3})@',
226
            $HTTP_USER_AGENT,
227
            $log_version
228
        )) {
229
            $this->set('PMA_USR_BROWSER_VER', $log_version[1]);
230
            $this->set('PMA_USR_BROWSER_AGENT', 'OMNIWEB');
231
            // Konqueror 2.2.2 says Konqueror/2.2.2
232
            // Konqueror 3.0.3 says Konqueror/3
233
        } elseif (preg_match(
234
            '@(Konqueror/)(.*)(;)@',
235
            $HTTP_USER_AGENT,
236
            $log_version
237
        )) {
238
            $this->set('PMA_USR_BROWSER_VER', $log_version[2]);
239
            $this->set('PMA_USR_BROWSER_AGENT', 'KONQUEROR');
240
            // must check Chrome before Safari
241
        } elseif ($is_mozilla
242
            && preg_match('@Chrome/([0-9.]*)@', $HTTP_USER_AGENT, $log_version)
243
        ) {
244
            $this->set('PMA_USR_BROWSER_VER', $log_version[1]);
245
            $this->set('PMA_USR_BROWSER_AGENT', 'CHROME');
246
            // newer Safari
247
        } elseif ($is_mozilla
248
            && preg_match('@Version/(.*) Safari@', $HTTP_USER_AGENT, $log_version)
249
        ) {
250
            $this->set(
251
                'PMA_USR_BROWSER_VER',
252
                $log_version[1]
253
            );
254
            $this->set('PMA_USR_BROWSER_AGENT', 'SAFARI');
255
            // older Safari
256
        } elseif ($is_mozilla
257
            && preg_match('@Safari/([0-9]*)@', $HTTP_USER_AGENT, $log_version)
258
        ) {
259
            $this->set(
260
                'PMA_USR_BROWSER_VER',
261
                $mozilla_version[1] . '.' . $log_version[1]
262
            );
263
            $this->set('PMA_USR_BROWSER_AGENT', 'SAFARI');
264
            // Firefox
265
        } elseif (! mb_strstr($HTTP_USER_AGENT, 'compatible')
266
            && preg_match('@Firefox/([\w.]+)@', $HTTP_USER_AGENT, $log_version)
267
        ) {
268
            $this->set(
269
                'PMA_USR_BROWSER_VER',
270
                $log_version[1]
271
            );
272
            $this->set('PMA_USR_BROWSER_AGENT', 'FIREFOX');
273
        } elseif (preg_match('@rv:1\.9(.*)Gecko@', $HTTP_USER_AGENT)) {
274
            $this->set('PMA_USR_BROWSER_VER', '1.9');
275
            $this->set('PMA_USR_BROWSER_AGENT', 'GECKO');
276
        } elseif ($is_mozilla) {
277
            $this->set('PMA_USR_BROWSER_VER', $mozilla_version[1]);
278
            $this->set('PMA_USR_BROWSER_AGENT', 'MOZILLA');
279
        } else {
280
            $this->set('PMA_USR_BROWSER_VER', 0);
281
            $this->set('PMA_USR_BROWSER_AGENT', 'OTHER');
282
        }
283
    }
284
285
    /**
286
     * Whether GD2 is present
287
     *
288
     * @return void
289
     */
290
    public function checkGd2(): void
291
    {
292
        if ($this->get('GD2Available') == 'yes') {
293
            $this->set('PMA_IS_GD2', 1);
294
            return;
295
        }
296
297
        if ($this->get('GD2Available') == 'no') {
298
            $this->set('PMA_IS_GD2', 0);
299
            return;
300
        }
301
302
        if (! function_exists('imagecreatetruecolor')) {
303
            $this->set('PMA_IS_GD2', 0);
304
            return;
305
        }
306
307
        if (function_exists('gd_info')) {
308
            $gd_nfo = gd_info();
309
            if (mb_strstr($gd_nfo["GD Version"], '2.')) {
310
                $this->set('PMA_IS_GD2', 1);
311
            } else {
312
                $this->set('PMA_IS_GD2', 0);
313
            }
314
        } else {
315
            $this->set('PMA_IS_GD2', 0);
316
        }
317
    }
318
319
    /**
320
     * Whether the Web server php is running on is IIS
321
     *
322
     * @return void
323
     */
324
    public function checkWebServer(): void
325
    {
326
        // some versions return Microsoft-IIS, some Microsoft/IIS
327
        // we could use a preg_match() but it's slower
328
        if (Core::getenv('SERVER_SOFTWARE')
329
            && false !== stripos(Core::getenv('SERVER_SOFTWARE'), 'Microsoft')
330
            && false !== stripos(Core::getenv('SERVER_SOFTWARE'), 'IIS')
331
        ) {
332
            $this->set('PMA_IS_IIS', 1);
333
        } else {
334
            $this->set('PMA_IS_IIS', 0);
335
        }
336
    }
337
338
    /**
339
     * Whether the os php is running on is windows or not
340
     *
341
     * @return void
342
     */
343
    public function checkWebServerOs(): void
344
    {
345
        // Default to Unix or Equiv
346
        $this->set('PMA_IS_WINDOWS', 0);
347
        // If PHP_OS is defined then continue
348
        if (defined('PHP_OS')) {
349
            if (false !== stripos(PHP_OS, 'win') && false === stripos(PHP_OS, 'darwin')) {
350
                // Is it some version of Windows
351
                $this->set('PMA_IS_WINDOWS', 1);
352
            } elseif (false !== stripos(PHP_OS, 'OS/2')) {
353
                // Is it OS/2 (No file permissions like Windows)
354
                $this->set('PMA_IS_WINDOWS', 1);
355
            }
356
        }
357
    }
358
359
    /**
360
     * detects if Git revision
361
     * @param string $git_location (optional) verified git directory
362
     * @return boolean
363
     */
364
    public function isGitRevision(&$git_location = null): bool
365
    {
366
        // PMA config check
367
        if (! $this->get('ShowGitRevision')) {
368
            return false;
369
        }
370
371
        // caching
372
        if (isset($_SESSION['is_git_revision'])
373
            && array_key_exists('git_location', $_SESSION)
374
        ) {
375
            // Define location using cached value
376
            $git_location = $_SESSION['git_location'];
377
            return $_SESSION['is_git_revision'];
378
        }
379
380
        // find out if there is a .git folder
381
        // or a .git file (--separate-git-dir)
382
        $git = '.git';
383
        if (is_dir($git)) {
384
            if (@is_file($git . '/config')) {
385
                $git_location = $git;
386
            } else {
387
                $_SESSION['git_location'] = null;
388
                $_SESSION['is_git_revision'] = false;
389
                return false;
390
            }
391
        } elseif (is_file($git)) {
392
            $contents = file_get_contents($git);
393
            $gitmatch = [];
394
            // Matches expected format
395
            if (! preg_match(
396
                '/^gitdir: (.*)$/',
397
                $contents,
398
                $gitmatch
399
            )) {
400
                $_SESSION['git_location'] = null;
401
                $_SESSION['is_git_revision'] = false;
402
                return false;
403
            } elseif (@is_dir($gitmatch[1])) {
404
                //Detected git external folder location
405
                $git_location = $gitmatch[1];
406
            } else {
407
                $_SESSION['git_location'] = null;
408
                $_SESSION['is_git_revision'] = false;
409
                return false;
410
            }
411
        } else {
412
            $_SESSION['git_location'] = null;
413
            $_SESSION['is_git_revision'] = false;
414
            return false;
415
        }
416
        // Define session for caching
417
        $_SESSION['git_location'] = $git_location;
418
        $_SESSION['is_git_revision'] = true;
419
        return true;
420
    }
421
422
    /**
423
     * detects Git revision, if running inside repo
424
     *
425
     * @return void
426
     */
427
    public function checkGitRevision(): void
428
    {
429
        // find out if there is a .git folder
430
        $git_folder = '';
431
        if (! $this->isGitRevision($git_folder)) {
432
            $this->set('PMA_VERSION_GIT', 0);
433
            return;
434
        }
435
436
        if (! $ref_head = @file_get_contents($git_folder . '/HEAD')) {
437
            $this->set('PMA_VERSION_GIT', 0);
438
            return;
439
        }
440
441
        if ($common_dir_contents = @file_get_contents($git_folder . '/commondir')) {
442
            $git_folder .= DIRECTORY_SEPARATOR . trim($common_dir_contents);
443
        }
444
445
        $branch = false;
446
        // are we on any branch?
447
        if (false !== strpos($ref_head, '/')) {
448
            // remove ref: prefix
449
            $ref_head = substr(trim($ref_head), 5);
450
            if (0 === strpos($ref_head, 'refs/heads/')) {
451
                $branch = substr($ref_head, 11);
452
            } else {
453
                $branch = basename($ref_head);
454
            }
455
456
            $ref_file = $git_folder . '/' . $ref_head;
457
            if (@file_exists($ref_file)) {
458
                $hash = @file_get_contents($ref_file);
459
                if (! $hash) {
460
                    $this->set('PMA_VERSION_GIT', 0);
461
                    return;
462
                }
463
                $hash = trim($hash);
464
            } else {
465
                // deal with packed refs
466
                $packed_refs = @file_get_contents($git_folder . '/packed-refs');
467
                if (! $packed_refs) {
468
                    $this->set('PMA_VERSION_GIT', 0);
469
                    return;
470
                }
471
                // split file to lines
472
                $ref_lines = explode(PHP_EOL, $packed_refs);
473
                foreach ($ref_lines as $line) {
474
                    // skip comments
475
                    if ($line[0] == '#') {
476
                        continue;
477
                    }
478
                    // parse line
479
                    $parts = explode(' ', $line);
480
                    // care only about named refs
481
                    if (count($parts) != 2) {
482
                        continue;
483
                    }
484
                    // have found our ref?
485
                    if ($parts[1] == $ref_head) {
486
                        $hash = $parts[0];
487
                        break;
488
                    }
489
                }
490
                if (! isset($hash)) {
491
                    $this->set('PMA_VERSION_GIT', 0);
492
                    // Could not find ref
493
                    return;
494
                }
495
            }
496
        } else {
497
            $hash = trim($ref_head);
498
        }
499
500
        $commit = false;
501
        if (! preg_match('/^[0-9a-f]{40}$/i', $hash)) {
502
            $commit = false;
503
        } elseif (isset($_SESSION['PMA_VERSION_COMMITDATA_' . $hash])) {
504
            $commit = $_SESSION['PMA_VERSION_COMMITDATA_' . $hash];
505
        } elseif (function_exists('gzuncompress')) {
506
            $git_file_name = $git_folder . '/objects/'
507
                . substr($hash, 0, 2) . '/' . substr($hash, 2);
508
            if (@file_exists($git_file_name)) {
509
                if (! $commit = @file_get_contents($git_file_name)) {
510
                    $this->set('PMA_VERSION_GIT', 0);
511
                    return;
512
                }
513
                $commit = explode("\0", gzuncompress($commit), 2);
514
                $commit = explode("\n", $commit[1]);
515
                $_SESSION['PMA_VERSION_COMMITDATA_' . $hash] = $commit;
516
            } else {
517
                $pack_names = [];
518
                // work with packed data
519
                $packs_file = $git_folder . '/objects/info/packs';
520
                if (@file_exists($packs_file)
521
                    && $packs = @file_get_contents($packs_file)
522
                ) {
523
                    // File exists. Read it, parse the file to get the names of the
524
                    // packs. (to look for them in .git/object/pack directory later)
525
                    foreach (explode("\n", $packs) as $line) {
526
                        // skip blank lines
527
                        if (strlen(trim($line)) == 0) {
528
                            continue;
529
                        }
530
                        // skip non pack lines
531
                        if ($line[0] != 'P') {
532
                            continue;
533
                        }
534
                        // parse names
535
                        $pack_names[] = substr($line, 2);
536
                    }
537
                } else {
538
                    // '.git/objects/info/packs' file can be missing
539
                    // (atlease in mysGit)
540
                    // File missing. May be we can look in the .git/object/pack
541
                    // directory for all the .pack files and use that list of
542
                    // files instead
543
                    $dirIterator = new DirectoryIterator(
544
                        $git_folder . '/objects/pack'
545
                    );
546
                    foreach ($dirIterator as $file_info) {
547
                        $file_name = $file_info->getFilename();
548
                        // if this is a .pack file
549
                        if ($file_info->isFile() && substr($file_name, -5) == '.pack'
550
                        ) {
551
                            $pack_names[] = $file_name;
552
                        }
553
                    }
554
                }
555
                $hash = strtolower($hash);
556
                foreach ($pack_names as $pack_name) {
557
                    $index_name = str_replace('.pack', '.idx', $pack_name);
558
559
                    // load index
560
                    $index_data = @file_get_contents(
561
                        $git_folder . '/objects/pack/' . $index_name
562
                    );
563
                    if (! $index_data) {
564
                        continue;
565
                    }
566
                    // check format
567
                    if (substr($index_data, 0, 4) != "\377tOc") {
568
                        continue;
569
                    }
570
                    // check version
571
                    $version = unpack('N', substr($index_data, 4, 4));
572
                    if ($version[1] != 2) {
573
                        continue;
574
                    }
575
                    // parse fanout table
576
                    $fanout = unpack(
577
                        "N*",
578
                        substr($index_data, 8, 256 * 4)
579
                    );
580
581
                    // find where we should search
582
                    $firstbyte = intval(substr($hash, 0, 2), 16);
583
                    // array is indexed from 1 and we need to get
584
                    // previous entry for start
585
                    if ($firstbyte == 0) {
586
                        $start = 0;
587
                    } else {
588
                        $start = $fanout[$firstbyte];
589
                    }
590
                    $end = $fanout[$firstbyte + 1];
591
592
                    // stupid linear search for our sha
593
                    $found = false;
594
                    $offset = 8 + (256 * 4);
595
                    for ($position = $start; $position < $end; $position++) {
596
                        $sha = strtolower(
597
                            bin2hex(
598
                                substr($index_data, $offset + ($position * 20), 20)
599
                            )
600
                        );
601
                        if ($sha == $hash) {
602
                            $found = true;
603
                            break;
604
                        }
605
                    }
606
                    if (! $found) {
607
                        continue;
608
                    }
609
                    // read pack offset
610
                    $offset = 8 + (256 * 4) + (24 * $fanout[256]);
611
                    $pack_offset = unpack(
612
                        'N',
613
                        substr($index_data, $offset + ($position * 4), 4)
614
                    );
615
                    $pack_offset = $pack_offset[1];
616
617
                    // open pack file
618
                    $pack_file = fopen(
619
                        $git_folder . '/objects/pack/' . $pack_name,
620
                        'rb'
621
                    );
622
                    if ($pack_file === false) {
623
                        continue;
624
                    }
625
                    // seek to start
626
                    fseek($pack_file, $pack_offset);
627
628
                    // parse header
629
                    $header = ord(fread($pack_file, 1));
630
                    $type = ($header >> 4) & 7;
631
                    $hasnext = ($header & 128) >> 7;
632
                    $size = $header & 0xf;
633
                    $offset = 4;
634
635
                    while ($hasnext) {
636
                        $byte = ord(fread($pack_file, 1));
637
                        $size |= ($byte & 0x7f) << $offset;
638
                        $hasnext = ($byte & 128) >> 7;
639
                        $offset += 7;
640
                    }
641
642
                    // we care only about commit objects
643
                    if ($type != 1) {
644
                        continue;
645
                    }
646
647
                    // read data
648
                    $commit = fread($pack_file, $size);
649
                    $commit = gzuncompress($commit);
650
                    $commit = explode("\n", $commit);
651
                    $_SESSION['PMA_VERSION_COMMITDATA_' . $hash] = $commit;
652
                    fclose($pack_file);
653
                }
654
            }
655
        }
656
657
        $httpRequest = new HttpRequest();
658
659
        // check if commit exists in Github
660
        if ($commit !== false
661
            && isset($_SESSION['PMA_VERSION_REMOTECOMMIT_' . $hash])
662
        ) {
663
            $is_remote_commit = $_SESSION['PMA_VERSION_REMOTECOMMIT_' . $hash];
664
        } else {
665
            $link = 'https://www.phpmyadmin.net/api/commit/' . $hash . '/';
666
            $is_found = $httpRequest->create($link, 'GET');
667
            switch ($is_found) {
668
                case false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $is_found of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
669
                    $is_remote_commit = false;
670
                    $_SESSION['PMA_VERSION_REMOTECOMMIT_' . $hash] = false;
671
                    break;
672
                case null:
673
                    // no remote link for now, but don't cache this as Github is down
674
                    $is_remote_commit = false;
675
                    break;
676
                default:
677
                    $is_remote_commit = true;
678
                    $_SESSION['PMA_VERSION_REMOTECOMMIT_' . $hash] = true;
679
                    if ($commit === false) {
680
                        // if no local commit data, try loading from Github
681
                        $commit_json = json_decode($is_found);
682
                    }
683
                    break;
684
            }
685
        }
686
687
        $is_remote_branch = false;
688
        if ($is_remote_commit && $branch !== false) {
689
            // check if branch exists in Github
690
            if (isset($_SESSION['PMA_VERSION_REMOTEBRANCH_' . $hash])) {
691
                $is_remote_branch = $_SESSION['PMA_VERSION_REMOTEBRANCH_' . $hash];
692
            } else {
693
                $link = 'https://www.phpmyadmin.net/api/tree/' . $branch . '/';
694
                $is_found = $httpRequest->create($link, 'GET', true);
695
                switch ($is_found) {
696
                    case true:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $is_found of type string to the boolean true. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
697
                        $is_remote_branch = true;
698
                        $_SESSION['PMA_VERSION_REMOTEBRANCH_' . $hash] = true;
699
                        break;
700
                    case false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $is_found of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
701
                        $is_remote_branch = false;
702
                        $_SESSION['PMA_VERSION_REMOTEBRANCH_' . $hash] = false;
703
                        break;
704
                    case null:
705
                        // no remote link for now, but don't cache this as Github is down
706
                        $is_remote_branch = false;
707
                        break;
708
                }
709
            }
710
        }
711
712
        if ($commit !== false) {
713
            $author = [
714
                'name' => '',
715
                'email' => '',
716
                'date' => '',
717
            ];
718
            $committer = [
719
                'name' => '',
720
                'email' => '',
721
                'date' => '',
722
            ];
723
724
            do {
725
                $dataline = array_shift($commit);
726
                $datalinearr = explode(' ', $dataline, 2);
727
                $linetype = $datalinearr[0];
728
                if (in_array($linetype, ['author', 'committer'])) {
729
                    $user = $datalinearr[1];
730
                    preg_match('/([^<]+)<([^>]+)> ([0-9]+)( [^ ]+)?/', $user, $user);
0 ignored issues
show
Bug introduced by
$user of type string is incompatible with the type array|null expected by parameter $matches of preg_match(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

730
                    preg_match('/([^<]+)<([^>]+)> ([0-9]+)( [^ ]+)?/', $user, /** @scrutinizer ignore-type */ $user);
Loading history...
731
                    $user2 = [
732
                        'name' => trim($user[1]),
733
                        'email' => trim($user[2]),
734
                        'date' => date('Y-m-d H:i:s', (int) $user[3]),
735
                    ];
736
                    if (isset($user[4])) {
737
                        $user2['date'] .= $user[4];
738
                    }
739
                    $$linetype = $user2;
740
                }
741
            } while ($dataline != '');
742
            $message = trim(implode(' ', $commit));
743
        } elseif (isset($commit_json, $commit_json->author, $commit_json->committer, $commit_json->message)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $commit_json does not seem to be defined for all execution paths leading up to this point.
Loading history...
744
            $author = [
745
                'name' => $commit_json->author->name,
746
                'email' => $commit_json->author->email,
747
                'date' => $commit_json->author->date,
748
            ];
749
            $committer = [
750
                'name' => $commit_json->committer->name,
751
                'email' => $commit_json->committer->email,
752
                'date' => $commit_json->committer->date,
753
            ];
754
            $message = trim($commit_json->message);
755
        } else {
756
            $this->set('PMA_VERSION_GIT', 0);
757
            return;
758
        }
759
760
        $this->set('PMA_VERSION_GIT', 1);
761
        $this->set('PMA_VERSION_GIT_COMMITHASH', $hash);
762
        $this->set('PMA_VERSION_GIT_BRANCH', $branch);
763
        $this->set('PMA_VERSION_GIT_MESSAGE', $message);
764
        $this->set('PMA_VERSION_GIT_AUTHOR', $author);
765
        $this->set('PMA_VERSION_GIT_COMMITTER', $committer);
766
        $this->set('PMA_VERSION_GIT_ISREMOTECOMMIT', $is_remote_commit);
767
        $this->set('PMA_VERSION_GIT_ISREMOTEBRANCH', $is_remote_branch);
768
    }
769
770
    /**
771
     * loads default values from default source
772
     *
773
     * @return boolean     success
774
     */
775
    public function loadDefaults(): bool
776
    {
777
        $cfg = [];
778
        if (! @file_exists($this->default_source)) {
779
            $this->error_config_default_file = true;
780
            return false;
781
        }
782
        $old_error_reporting = error_reporting(0);
783
        ob_start();
784
        $GLOBALS['pma_config_loading'] = true;
785
        $eval_result = include $this->default_source;
786
        $GLOBALS['pma_config_loading'] = false;
787
        ob_end_clean();
788
        error_reporting($old_error_reporting);
789
790
        if ($eval_result === false) {
791
            $this->error_config_default_file = true;
792
            return false;
793
        }
794
795
        $this->default_source_mtime = filemtime($this->default_source);
796
797
        $this->default_server = $cfg['Servers'][1];
798
        unset($cfg['Servers']);
799
800
        $this->default = $cfg;
801
        $this->settings = array_replace_recursive($this->settings, $cfg);
802
803
        $this->error_config_default_file = false;
804
805
        return true;
806
    }
807
808
    /**
809
     * loads configuration from $source, usually the config file
810
     * should be called on object creation
811
     *
812
     * @param string $source config file
813
     *
814
     * @return bool
815
     */
816
    public function load(?string $source = null): bool
817
    {
818
        $this->loadDefaults();
819
820
        if (null !== $source) {
821
            $this->setSource($source);
822
        }
823
824
        if (! $this->checkConfigSource()) {
825
            return false;
826
        }
827
828
        $cfg = [];
829
830
        /**
831
         * Parses the configuration file, we throw away any errors or
832
         * output.
833
         */
834
        $old_error_reporting = error_reporting(0);
835
        ob_start();
836
        $GLOBALS['pma_config_loading'] = true;
837
        $eval_result = include $this->getSource();
838
        $GLOBALS['pma_config_loading'] = false;
839
        ob_end_clean();
840
        error_reporting($old_error_reporting);
841
842
        if ($eval_result === false) {
843
            $this->error_config_file = true;
844
        } else {
845
            $this->error_config_file = false;
846
            $this->source_mtime = filemtime($this->getSource());
847
        }
848
849
        /**
850
         * Ignore keys with / as we do not use these
851
         *
852
         * These can be confusing for user configuration layer as it
853
         * flatten array using / and thus don't see difference between
854
         * $cfg['Export/method'] and $cfg['Export']['method'], while rest
855
         * of thre code uses the setting only in latter form.
856
         *
857
         * This could be removed once we consistently handle both values
858
         * in the functional code as well.
859
         *
860
         * It could use array_filter(...ARRAY_FILTER_USE_KEY), but it's not
861
         * supported on PHP 5.5 and HHVM.
862
         */
863
        $matched_keys = array_filter(
864
            array_keys($cfg),
865
            function ($key) {
866
                return strpos($key, '/') === false;
867
            }
868
        );
869
870
        $cfg = array_intersect_key($cfg, array_flip($matched_keys));
871
872
        $this->settings = array_replace_recursive($this->settings, $cfg);
873
874
        return true;
875
    }
876
877
    /**
878
     * Sets the connection collation
879
     *
880
     * @return void
881
     */
882
    private function _setConnectionCollation(): void
883
    {
884
        $collation_connection = $this->get('DefaultConnectionCollation');
885
        if (! empty($collation_connection)
886
            && $collation_connection != $GLOBALS['collation_connection']
887
        ) {
888
            $GLOBALS['dbi']->setCollation($collation_connection);
889
        }
890
    }
891
892
    /**
893
     * Loads user preferences and merges them with current config
894
     * must be called after control connection has been established
895
     *
896
     * @return void
897
     */
898
    public function loadUserPreferences(): void
899
    {
900
        $userPreferences = new UserPreferences();
901
        // index.php should load these settings, so that phpmyadmin.css.php
902
        // will have everything available in session cache
903
        $server = isset($GLOBALS['server'])
904
            ? $GLOBALS['server']
905
            : (! empty($GLOBALS['cfg']['ServerDefault'])
906
                ? $GLOBALS['cfg']['ServerDefault']
907
                : 0);
908
        $cache_key = 'server_' . $server;
909
        if ($server > 0 && ! defined('PMA_MINIMUM_COMMON')) {
910
            $config_mtime = max($this->default_source_mtime, $this->source_mtime);
911
            // cache user preferences, use database only when needed
912
            if (! isset($_SESSION['cache'][$cache_key]['userprefs'])
913
                || $_SESSION['cache'][$cache_key]['config_mtime'] < $config_mtime
914
            ) {
915
                $prefs = $userPreferences->load();
916
                $_SESSION['cache'][$cache_key]['userprefs']
917
                    = $userPreferences->apply($prefs['config_data']);
918
                $_SESSION['cache'][$cache_key]['userprefs_mtime'] = $prefs['mtime'];
919
                $_SESSION['cache'][$cache_key]['userprefs_type'] = $prefs['type'];
920
                $_SESSION['cache'][$cache_key]['config_mtime'] = $config_mtime;
921
            }
922
        } elseif ($server == 0
923
            || ! isset($_SESSION['cache'][$cache_key]['userprefs'])
924
        ) {
925
            $this->set('user_preferences', false);
926
            return;
927
        }
928
        $config_data = $_SESSION['cache'][$cache_key]['userprefs'];
929
        // type is 'db' or 'session'
930
        $this->set(
931
            'user_preferences',
932
            $_SESSION['cache'][$cache_key]['userprefs_type']
933
        );
934
        $this->set(
935
            'user_preferences_mtime',
936
            $_SESSION['cache'][$cache_key]['userprefs_mtime']
937
        );
938
939
        // load config array
940
        $this->settings = array_replace_recursive($this->settings, $config_data);
941
        $GLOBALS['cfg'] = array_replace_recursive($GLOBALS['cfg'], $config_data);
942
        if (defined('PMA_MINIMUM_COMMON')) {
943
            return;
944
        }
945
946
        // settings below start really working on next page load, but
947
        // changes are made only in index.php so everything is set when
948
        // in frames
949
950
        // save theme
951
        /** @var ThemeManager $tmanager */
952
        $tmanager = ThemeManager::getInstance();
953
        if ($tmanager->getThemeCookie() || isset($_REQUEST['set_theme'])) {
954
            if ((! isset($config_data['ThemeDefault'])
955
                && $tmanager->theme->getId() != 'original')
956
                || isset($config_data['ThemeDefault'])
957
                && $config_data['ThemeDefault'] != $tmanager->theme->getId()
958
            ) {
959
                // new theme was set in common.inc.php
960
                $this->setUserValue(
961
                    null,
962
                    'ThemeDefault',
963
                    $tmanager->theme->getId(),
964
                    'original'
965
                );
966
            }
967
        } else {
968
            // no cookie - read default from settings
969
            if ($this->settings['ThemeDefault'] != $tmanager->theme->getId()
970
                && $tmanager->checkTheme($this->settings['ThemeDefault'])
971
            ) {
972
                $tmanager->setActiveTheme($this->settings['ThemeDefault']);
973
                $tmanager->setThemeCookie();
974
            }
975
        }
976
977
        // save language
978
        if (isset($_COOKIE['pma_lang']) || isset($_POST['lang'])) {
979
            if ((! isset($config_data['lang'])
980
                && $GLOBALS['lang'] != 'en')
981
                || isset($config_data['lang'])
982
                && $GLOBALS['lang'] != $config_data['lang']
983
            ) {
984
                $this->setUserValue(null, 'lang', $GLOBALS['lang'], 'en');
985
            }
986
        } else {
987
            // read language from settings
988
            if (isset($config_data['lang'])) {
989
                $language = LanguageManager::getInstance()->getLanguage(
990
                    $config_data['lang']
991
                );
992
                if ($language !== false) {
993
                    $language->activate();
994
                    $this->setCookie('pma_lang', $language->getCode());
995
                }
996
            }
997
        }
998
999
        // set connection collation
1000
        $this->_setConnectionCollation();
1001
    }
1002
1003
    /**
1004
     * Sets config value which is stored in user preferences (if available)
1005
     * or in a cookie.
1006
     *
1007
     * If user preferences are not yet initialized, option is applied to
1008
     * global config and added to a update queue, which is processed
1009
     * by {@link loadUserPreferences()}
1010
     *
1011
     * @param string|null $cookie_name   can be null
1012
     * @param string      $cfg_path      configuration path
1013
     * @param mixed       $new_cfg_value new value
1014
     * @param mixed       $default_value default value
1015
     *
1016
     * @return true|Message
1017
     */
1018
    public function setUserValue(
1019
        ?string $cookie_name,
1020
        string $cfg_path,
1021
        $new_cfg_value,
1022
        $default_value = null
1023
    ) {
1024
        $userPreferences = new UserPreferences();
1025
        $result = true;
1026
        // use permanent user preferences if possible
1027
        $prefs_type = $this->get('user_preferences');
1028
        if ($prefs_type) {
1029
            if ($default_value === null) {
1030
                $default_value = Core::arrayRead($cfg_path, $this->default);
1031
            }
1032
            $result = $userPreferences->persistOption($cfg_path, $new_cfg_value, $default_value);
1033
        }
1034
        if ($prefs_type != 'db' && $cookie_name) {
1035
            // fall back to cookies
1036
            if ($default_value === null) {
1037
                $default_value = Core::arrayRead($cfg_path, $this->settings);
1038
            }
1039
            $this->setCookie($cookie_name, $new_cfg_value, $default_value);
1040
        }
1041
        Core::arrayWrite($cfg_path, $GLOBALS['cfg'], $new_cfg_value);
1042
        Core::arrayWrite($cfg_path, $this->settings, $new_cfg_value);
1043
        return $result;
1044
    }
1045
1046
    /**
1047
     * Reads value stored by {@link setUserValue()}
1048
     *
1049
     * @param string $cookie_name cookie name
1050
     * @param mixed  $cfg_value   config value
1051
     *
1052
     * @return mixed
1053
     */
1054
    public function getUserValue(string $cookie_name, $cfg_value)
1055
    {
1056
        $cookie_exists = ! empty($_COOKIE[$cookie_name]);
1057
        $prefs_type = $this->get('user_preferences');
1058
        if ($prefs_type == 'db') {
1059
            // permanent user preferences value exists, remove cookie
1060
            if ($cookie_exists) {
1061
                $this->removeCookie($cookie_name);
1062
            }
1063
        } elseif ($cookie_exists) {
1064
            return $_COOKIE[$cookie_name];
1065
        }
1066
        // return value from $cfg array
1067
        return $cfg_value;
1068
    }
1069
1070
    /**
1071
     * set source
1072
     *
1073
     * @param string $source source
1074
     *
1075
     * @return void
1076
     */
1077
    public function setSource(string $source): void
1078
    {
1079
        $this->source = trim($source);
1080
    }
1081
1082
    /**
1083
     * check config source
1084
     *
1085
     * @return boolean whether source is valid or not
1086
     */
1087
    public function checkConfigSource(): bool
1088
    {
1089
        if (! $this->getSource()) {
1090
            // no configuration file set at all
1091
            return false;
1092
        }
1093
1094
        if (! @file_exists($this->getSource())) {
1095
            $this->source_mtime = 0;
1096
            return false;
1097
        }
1098
1099
        if (! @is_readable($this->getSource())) {
1100
            // manually check if file is readable
1101
            // might be bug #3059806 Supporting running from CIFS/Samba shares
1102
1103
            $contents = false;
1104
            $handle = @fopen($this->getSource(), 'r');
1105
            if ($handle !== false) {
1106
                $contents = @fread($handle, 1); // reading 1 byte is enough to test
1107
                fclose($handle);
1108
            }
1109
            if ($contents === false) {
1110
                $this->source_mtime = 0;
1111
                Core::fatalError(
1112
                    sprintf(
1113
                        function_exists('__')
1114
                        ? __('Existing configuration file (%s) is not readable.')
1115
                        : 'Existing configuration file (%s) is not readable.',
1116
                        $this->getSource()
1117
                    )
1118
                );
1119
                return false;
1120
            }
1121
        }
1122
1123
        return true;
1124
    }
1125
1126
    /**
1127
     * verifies the permissions on config file (if asked by configuration)
1128
     * (must be called after config.inc.php has been merged)
1129
     *
1130
     * @return void
1131
     */
1132
    public function checkPermissions(): void
1133
    {
1134
        // Check for permissions (on platforms that support it):
1135
        if ($this->get('CheckConfigurationPermissions') && @file_exists($this->getSource())) {
1136
            $perms = @fileperms($this->getSource());
1137
            if (! ($perms === false) && ($perms & 2)) {
1138
                // This check is normally done after loading configuration
1139
                $this->checkWebServerOs();
1140
                if ($this->get('PMA_IS_WINDOWS') == 0) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->get('PMA_IS_WINDOWS') of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
1141
                    $this->source_mtime = 0;
1142
                    Core::fatalError(
1143
                        __(
1144
                            'Wrong permissions on configuration file, '
1145
                            . 'should not be world writable!'
1146
                        )
1147
                    );
1148
                }
1149
            }
1150
        }
1151
    }
1152
1153
    /**
1154
     * Checks for errors
1155
     * (must be called after config.inc.php has been merged)
1156
     *
1157
     * @return void
1158
     */
1159
    public function checkErrors(): void
1160
    {
1161
        if ($this->error_config_default_file) {
1162
            Core::fatalError(
1163
                sprintf(
1164
                    __('Could not load default configuration from: %1$s'),
1165
                    $this->default_source
1166
                )
1167
            );
1168
        }
1169
1170
        if ($this->error_config_file) {
1171
            $error = '[strong]' . __('Failed to read configuration file!') . '[/strong]'
1172
                . '[br][br]'
1173
                . __(
1174
                    'This usually means there is a syntax error in it, '
1175
                    . 'please check any errors shown below.'
1176
                )
1177
                . '[br][br]'
1178
                . '[conferr]';
1179
            trigger_error($error, E_USER_ERROR);
1180
        }
1181
    }
1182
1183
    /**
1184
     * returns specific config setting
1185
     *
1186
     * @param string $setting config setting
1187
     *
1188
     * @return mixed value
1189
     */
1190
    public function get(string $setting)
1191
    {
1192
        if (isset($this->settings[$setting])) {
1193
            return $this->settings[$setting];
1194
        }
1195
        return null;
1196
    }
1197
1198
    /**
1199
     * sets configuration variable
1200
     *
1201
     * @param string $setting configuration option
1202
     * @param mixed  $value   new value for configuration option
1203
     *
1204
     * @return void
1205
     */
1206
    public function set(string $setting, $value): void
1207
    {
1208
        if (! isset($this->settings[$setting])
1209
            || $this->settings[$setting] !== $value
1210
        ) {
1211
            $this->settings[$setting] = $value;
1212
            $this->set_mtime = time();
1213
        }
1214
    }
1215
1216
    /**
1217
     * returns source for current config
1218
     *
1219
     * @return string  config source
1220
     */
1221
    public function getSource(): string
1222
    {
1223
        return $this->source;
1224
    }
1225
1226
    /**
1227
     * returns a unique value to force a CSS reload if either the config
1228
     * or the theme changes
1229
     *
1230
     * @return int Summary of unix timestamps, to be unique on theme parameters
1231
     *             change
1232
     */
1233
    public function getThemeUniqueValue(): int
1234
    {
1235
        return (int) (
1236
            $this->source_mtime +
1237
            $this->default_source_mtime +
1238
            $this->get('user_preferences_mtime') +
1239
            $GLOBALS['PMA_Theme']->mtime_info +
1240
            $GLOBALS['PMA_Theme']->filesize_info
1241
        );
1242
    }
1243
1244
    /**
1245
     * checks if upload is enabled
1246
     *
1247
     * @return void
1248
     */
1249
    public function checkUpload(): void
1250
    {
1251
        if (! ini_get('file_uploads')) {
1252
            $this->set('enable_upload', false);
1253
            return;
1254
        }
1255
1256
        $this->set('enable_upload', true);
1257
        // if set "php_admin_value file_uploads Off" in httpd.conf
1258
        // ini_get() also returns the string "Off" in this case:
1259
        if ('off' == strtolower(ini_get('file_uploads'))) {
1260
            $this->set('enable_upload', false);
1261
        }
1262
    }
1263
1264
    /**
1265
     * Maximum upload size as limited by PHP
1266
     * Used with permission from Moodle (https://moodle.org/) by Martin Dougiamas
1267
     *
1268
     * this section generates $max_upload_size in bytes
1269
     *
1270
     * @return void
1271
     */
1272
    public function checkUploadSize(): void
1273
    {
1274
        if (! $filesize = ini_get('upload_max_filesize')) {
1275
            $filesize = "5M";
1276
        }
1277
1278
        if ($postsize = ini_get('post_max_size')) {
1279
            $this->set(
1280
                'max_upload_size',
1281
                min(Core::getRealSize($filesize), Core::getRealSize($postsize))
1282
            );
1283
        } else {
1284
            $this->set('max_upload_size', Core::getRealSize($filesize));
1285
        }
1286
    }
1287
1288
    /**
1289
     * Checks if protocol is https
1290
     *
1291
     * This function checks if the https protocol on the active connection.
1292
     *
1293
     * @return bool
1294
     */
1295
    public function isHttps(): bool
1296
    {
1297
        if (null !== $this->get('is_https')) {
1298
            return $this->get('is_https');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get('is_https') could return the type null which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
1299
        }
1300
1301
        $url = $this->get('PmaAbsoluteUri');
1302
1303
        $is_https = false;
1304
        if (! empty($url) && parse_url($url, PHP_URL_SCHEME) === 'https') {
1305
            $is_https = true;
1306
        } elseif (strtolower(Core::getenv('HTTP_SCHEME')) == 'https') {
1307
            $is_https = true;
1308
        } elseif (strtolower(Core::getenv('HTTPS')) == 'on') {
1309
            $is_https = true;
1310
        } elseif (strtolower(substr(Core::getenv('REQUEST_URI'), 0, 6)) == 'https:') {
1311
            $is_https = true;
1312
        } elseif (strtolower(Core::getenv('HTTP_HTTPS_FROM_LB')) == 'on') {
1313
            // A10 Networks load balancer
1314
            $is_https = true;
1315
        } elseif (strtolower(Core::getenv('HTTP_FRONT_END_HTTPS')) == 'on') {
1316
            $is_https = true;
1317
        } elseif (strtolower(Core::getenv('HTTP_X_FORWARDED_PROTO')) == 'https') {
1318
            $is_https = true;
1319
        } elseif (Core::getenv('SERVER_PORT') == 443) {
1320
            $is_https = true;
1321
        }
1322
1323
        $this->set('is_https', $is_https);
1324
1325
        return $is_https;
1326
    }
1327
1328
    /**
1329
     * Get phpMyAdmin root path
1330
     *
1331
     * @return string
1332
     */
1333
    public function getRootPath(): string
1334
    {
1335
        static $cookie_path = null;
1336
1337
        if (null !== $cookie_path && ! defined('TESTSUITE')) {
1338
            return $cookie_path;
1339
        }
1340
1341
        $url = $this->get('PmaAbsoluteUri');
1342
1343
        if (! empty($url)) {
1344
            $path = parse_url($url, PHP_URL_PATH);
1345
            if (! empty($path)) {
1346
                if (substr($path, -1) != '/') {
1347
                    return $path . '/';
1348
                }
1349
                return $path;
1350
            }
1351
        }
1352
1353
        $parsed_url = parse_url($GLOBALS['PMA_PHP_SELF']);
1354
1355
        $parts = explode(
1356
            '/',
1357
            rtrim(str_replace('\\', '/', $parsed_url['path']), '/')
1358
        );
1359
1360
        /* Remove filename */
1361
        if (substr($parts[count($parts) - 1], -4) == '.php') {
1362
            $parts = array_slice($parts, 0, count($parts) - 1);
1363
        }
1364
1365
        /* Remove extra path from javascript calls */
1366
        if (defined('PMA_PATH_TO_BASEDIR')) {
1367
            $parts = array_slice($parts, 0, count($parts) - 1);
1368
        }
1369
1370
        $parts[] = '';
1371
1372
        return implode('/', $parts);
1373
    }
1374
1375
    /**
1376
     * enables backward compatibility
1377
     *
1378
     * @return void
1379
     */
1380
    public function enableBc(): void
1381
    {
1382
        $GLOBALS['cfg']             = $this->settings;
1383
        $GLOBALS['default_server']  = $this->default_server;
1384
        unset($this->default_server);
1385
        $GLOBALS['is_upload']       = $this->get('enable_upload');
1386
        $GLOBALS['max_upload_size'] = $this->get('max_upload_size');
1387
        $GLOBALS['is_https']        = $this->get('is_https');
1388
1389
        $defines = [
1390
            'PMA_VERSION',
1391
            'PMA_MAJOR_VERSION',
1392
            'PMA_THEME_VERSION',
1393
            'PMA_THEME_GENERATION',
1394
            'PMA_IS_WINDOWS',
1395
            'PMA_IS_GD2',
1396
            'PMA_USR_OS',
1397
            'PMA_USR_BROWSER_VER',
1398
            'PMA_USR_BROWSER_AGENT',
1399
        ];
1400
1401
        foreach ($defines as $define) {
1402
            if (! defined($define)) {
1403
                define($define, $this->get($define));
1404
            }
1405
        }
1406
    }
1407
1408
    /**
1409
     * removes cookie
1410
     *
1411
     * @param string $cookie name of cookie to remove
1412
     *
1413
     * @return boolean result of setcookie()
1414
     */
1415
    public function removeCookie(string $cookie): bool
1416
    {
1417
        if (defined('TESTSUITE')) {
1418
            if (isset($_COOKIE[$cookie])) {
1419
                unset($_COOKIE[$cookie]);
1420
            }
1421
            return true;
1422
        }
1423
        return setcookie(
1424
            $cookie,
1425
            '',
1426
            time() - 3600,
1427
            $this->getRootPath(),
1428
            '',
1429
            $this->isHttps()
1430
        );
1431
    }
1432
1433
    /**
1434
     * sets cookie if value is different from current cookie value,
1435
     * or removes if value is equal to default
1436
     *
1437
     * @param string $cookie   name of cookie to remove
1438
     * @param mixed  $value    new cookie value
1439
     * @param string $default  default value
1440
     * @param int    $validity validity of cookie in seconds (default is one month)
1441
     * @param bool   $httponly whether cookie is only for HTTP (and not for scripts)
1442
     *
1443
     * @return boolean result of setcookie()
1444
     */
1445
    public function setCookie(
1446
        string $cookie,
1447
        $value,
1448
        ?string $default = null,
1449
        ?int $validity = null,
1450
        bool $httponly = true
1451
    ): bool {
1452
        if (strlen($value) > 0 && null !== $default && $value === $default
1453
        ) {
1454
            // default value is used
1455
            if (isset($_COOKIE[$cookie])) {
1456
                // remove cookie
1457
                return $this->removeCookie($cookie);
1458
            }
1459
            return false;
1460
        }
1461
1462
        if (strlen($value) === 0 && isset($_COOKIE[$cookie])) {
1463
            // remove cookie, value is empty
1464
            return $this->removeCookie($cookie);
1465
        }
1466
1467
        if (! isset($_COOKIE[$cookie]) || $_COOKIE[$cookie] !== $value) {
1468
            // set cookie with new value
1469
            /* Calculate cookie validity */
1470
            if ($validity === null) {
1471
                /* Valid for one month */
1472
                $validity = time() + 2592000;
1473
            } elseif ($validity == 0) {
1474
                /* Valid for session */
1475
                $validity = 0;
1476
            } else {
1477
                $validity = time() + $validity;
1478
            }
1479
            if (defined('TESTSUITE')) {
1480
                $_COOKIE[$cookie] = $value;
1481
                return true;
1482
            }
1483
            return setcookie(
1484
                $cookie,
1485
                $value,
1486
                $validity,
1487
                $this->getRootPath(),
1488
                '',
1489
                $this->isHttps(),
1490
                $httponly
1491
            );
1492
        }
1493
1494
        // cookie has already $value as value
1495
        return true;
1496
    }
1497
1498
1499
    /**
1500
     * Error handler to catch fatal errors when loading configuration
1501
     * file
1502
     *
1503
     * @return void
1504
     */
1505
    public static function fatalErrorHandler(): void
1506
    {
1507
        if (! isset($GLOBALS['pma_config_loading'])
1508
            || ! $GLOBALS['pma_config_loading']
1509
        ) {
1510
            return;
1511
        }
1512
1513
        $error = error_get_last();
1514
        if ($error === null) {
1515
            return;
1516
        }
1517
1518
        Core::fatalError(
1519
            sprintf(
1520
                'Failed to load phpMyAdmin configuration (%s:%s): %s',
1521
                Error::relPath($error['file']),
1522
                $error['line'],
1523
                $error['message']
1524
            )
1525
        );
1526
    }
1527
1528
    /**
1529
     * Wrapper for footer/header rendering
1530
     *
1531
     * @param string $filename File to check and render
1532
     * @param string $id       Div ID
1533
     *
1534
     * @return string
1535
     */
1536
    private static function _renderCustom(string $filename, string $id): string
1537
    {
1538
        $retval = '';
1539
        if (@file_exists($filename)) {
1540
            $retval .= '<div id="' . $id . '">';
1541
            ob_start();
1542
            include $filename;
1543
            $retval .= ob_get_contents();
1544
            ob_end_clean();
1545
            $retval .= '</div>';
1546
        }
1547
        return $retval;
1548
    }
1549
1550
    /**
1551
     * Renders user configured footer
1552
     *
1553
     * @return string
1554
     */
1555
    public static function renderFooter(): string
1556
    {
1557
        return self::_renderCustom(CUSTOM_FOOTER_FILE, 'pma_footer');
1558
    }
1559
1560
    /**
1561
     * Renders user configured footer
1562
     *
1563
     * @return string
1564
     */
1565
    public static function renderHeader(): string
1566
    {
1567
        return self::_renderCustom(CUSTOM_HEADER_FILE, 'pma_header');
1568
    }
1569
1570
    /**
1571
     * Returns temporary dir path
1572
     *
1573
     * @param string $name Directory name
1574
     *
1575
     * @return string|null
1576
     */
1577
    public function getTempDir(string $name): ?string
1578
    {
1579
        static $temp_dir = [];
1580
1581
        if (isset($temp_dir[$name]) && ! defined('TESTSUITE')) {
1582
            return $temp_dir[$name];
1583
        }
1584
1585
        $path = $this->get('TempDir');
1586
        if (empty($path)) {
1587
            $path = null;
1588
        } else {
1589
            $path .= '/' . $name;
1590
            if (! @is_dir($path)) {
1591
                @mkdir($path, 0770, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1591
                /** @scrutinizer ignore-unhandled */ @mkdir($path, 0770, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1592
            }
1593
            if (! @is_dir($path) || ! @is_writable($path)) {
1594
                $path = null;
1595
            }
1596
        }
1597
1598
        $temp_dir[$name] = $path;
1599
        return $path;
1600
    }
1601
1602
    /**
1603
     * Returns temporary directory
1604
     *
1605
     * @return string|null
1606
     */
1607
    public function getUploadTempDir(): ?string
1608
    {
1609
        // First try configured temp dir
1610
        // Fallback to PHP upload_tmp_dir
1611
        $dirs = [
1612
            $this->getTempDir('upload'),
1613
            ini_get('upload_tmp_dir'),
1614
            sys_get_temp_dir(),
1615
        ];
1616
1617
        foreach ($dirs as $dir) {
1618
            if (! empty($dir) && @is_writable($dir)) {
1619
                return realpath($dir);
1620
            }
1621
        }
1622
1623
        return null;
1624
    }
1625
1626
    /**
1627
     * Selects server based on request parameters.
1628
     *
1629
     * @return integer
1630
     */
1631
    public function selectServer(): int
1632
    {
1633
        $request = empty($_REQUEST['server']) ? 0 : $_REQUEST['server'];
1634
1635
        /**
1636
         * Lookup server by name
1637
         * (see FAQ 4.8)
1638
         */
1639
        if (! is_numeric($request)) {
1640
            foreach ($this->settings['Servers'] as $i => $server) {
1641
                $verboseToLower = mb_strtolower($server['verbose']);
1642
                $serverToLower = mb_strtolower($request);
1643
                if ($server['host'] == $request
1644
                    || $server['verbose'] == $request
1645
                    || $verboseToLower == $serverToLower
1646
                    || md5($verboseToLower) === $serverToLower
1647
                ) {
1648
                    $request = $i;
1649
                    break;
1650
                }
1651
            }
1652
            if (is_string($request)) {
1653
                $request = 0;
1654
            }
1655
        }
1656
1657
        /**
1658
         * If no server is selected, make sure that $this->settings['Server'] is empty (so
1659
         * that nothing will work), and skip server authentication.
1660
         * We do NOT exit here, but continue on without logging into any server.
1661
         * This way, the welcome page will still come up (with no server info) and
1662
         * present a choice of servers in the case that there are multiple servers
1663
         * and '$this->settings['ServerDefault'] = 0' is set.
1664
         */
1665
1666
        if (is_numeric($request) && ! empty($request) && ! empty($this->settings['Servers'][$request])) {
1667
            $server = $request;
1668
            $this->settings['Server'] = $this->settings['Servers'][$server];
1669
        } else {
1670
            if (! empty($this->settings['Servers'][$this->settings['ServerDefault']])) {
1671
                $server = $this->settings['ServerDefault'];
1672
                $this->settings['Server'] = $this->settings['Servers'][$server];
1673
            } else {
1674
                $server = 0;
1675
                $this->settings['Server'] = [];
1676
            }
1677
        }
1678
1679
        return (int) $server;
1680
    }
1681
1682
    /**
1683
     * Checks whether Servers configuration is valid and possibly apply fixups.
1684
     *
1685
     * @return void
1686
     */
1687
    public function checkServers(): void
1688
    {
1689
        // Do we have some server?
1690
        if (! isset($this->settings['Servers']) || count($this->settings['Servers']) === 0) {
1691
            // No server => create one with defaults
1692
            $this->settings['Servers'] = [1 => $this->default_server];
1693
        } else {
1694
            // We have server(s) => apply default configuration
1695
            $new_servers = [];
1696
1697
            foreach ($this->settings['Servers'] as $server_index => $each_server) {
1698
                // Detect wrong configuration
1699
                if (! is_int($server_index) || $server_index < 1) {
1700
                    trigger_error(
1701
                        sprintf(__('Invalid server index: %s'), $server_index),
1702
                        E_USER_ERROR
1703
                    );
1704
                }
1705
1706
                $each_server = array_merge($this->default_server, $each_server);
1707
1708
                // Final solution to bug #582890
1709
                // If we are using a socket connection
1710
                // and there is nothing in the verbose server name
1711
                // or the host field, then generate a name for the server
1712
                // in the form of "Server 2", localized of course!
1713
                if (empty($each_server['host']) && empty($each_server['verbose'])) {
1714
                    $each_server['verbose'] = sprintf(__('Server %d'), $server_index);
1715
                }
1716
1717
                $new_servers[$server_index] = $each_server;
1718
            }
1719
            $this->settings['Servers'] = $new_servers;
1720
        }
1721
    }
1722
}
1723
1724
if (! defined('TESTSUITE')) {
1725
    register_shutdown_function([Config::class, 'fatalErrorHandler']);
1726
}
1727