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: |
|
|
|
|
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: |
|
|
|
|
697
|
|
|
$is_remote_branch = true; |
698
|
|
|
$_SESSION['PMA_VERSION_REMOTEBRANCH_' . $hash] = true; |
699
|
|
|
break; |
700
|
|
|
case false: |
|
|
|
|
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); |
|
|
|
|
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)) { |
|
|
|
|
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) { |
|
|
|
|
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'); |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|