1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Function library read in upon startup |
5
|
|
|
* |
6
|
|
|
* Release: lib.inc.php,v 1.123 2008/04/06 01:10:35 xzilla Exp $ |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
defined('BASE_PATH') or define('BASE_PATH', dirname(__DIR__)); |
10
|
|
|
|
11
|
|
|
define('THEME_PATH', BASE_PATH . '/assets/themes'); |
12
|
|
|
// Enforce PHP environment |
13
|
|
|
ini_set('arg_separator.output', '&'); |
14
|
|
|
|
15
|
|
|
if (!is_writable(BASE_PATH . '/temp')) { |
16
|
|
|
die('Your temp folder must have write permissions (use chmod 777 temp -R on linux)'); |
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
// Check to see if the configuration file exists, if not, explain |
20
|
|
|
if (file_exists(BASE_PATH . '/config.inc.php')) { |
21
|
|
|
$conf = []; |
22
|
|
|
include BASE_PATH . '/config.inc.php'; |
23
|
|
|
} else { |
24
|
|
|
die('Configuration error: Copy config.inc.php-dist to config.inc.php and edit appropriately.'); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
if (isset($conf['error_log'])) { |
28
|
|
|
ini_set('error_log', BASE_PATH . '/' . $conf['error_log']); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
$debugmode = (!isset($conf['debugmode'])) ? false : boolval($conf['debugmode']); |
32
|
|
|
define('DEBUGMODE', $debugmode); |
33
|
|
|
|
34
|
|
|
require_once BASE_PATH . '/vendor/autoload.php'; |
35
|
|
|
|
36
|
|
|
if (!defined('ADODB_ERROR_HANDLER_TYPE')) { |
37
|
|
|
define('ADODB_ERROR_HANDLER_TYPE', E_USER_ERROR); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
if (!defined('ADODB_ERROR_HANDLER')) { |
41
|
|
|
define('ADODB_ERROR_HANDLER', '\PHPPgAdmin\ADOdbException::adodb_throw'); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
// Start session (if not auto-started) |
45
|
|
|
if (!ini_get('session.auto_start')) { |
46
|
|
|
session_name('PPA_ID'); |
47
|
|
|
session_start(); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
function maybeRenderIframes($c, $response, $subject, $query_string) |
|
|
|
|
51
|
|
|
{ |
52
|
|
|
$in_test = $c->view->offsetGet('in_test'); |
53
|
|
|
|
54
|
|
|
if ($in_test === '1') { |
55
|
|
|
$className = '\PHPPgAdmin\Controller\\' . ucfirst($subject) . 'Controller'; |
56
|
|
|
$controller = new $className($c); |
57
|
|
|
return $controller->render(); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$viewVars = [ |
61
|
|
|
'url' => '/src/views/' . $subject . ($query_string ? '?' . $query_string : ''), |
62
|
|
|
'headertemplate' => 'header.twig', |
63
|
|
|
]; |
64
|
|
|
|
65
|
|
|
return $c->view->render($response, 'iframe_view.twig', $viewVars); |
66
|
|
|
}; |
67
|
|
|
|
68
|
|
|
$handler = PhpConsole\Handler::getInstance(); |
69
|
|
|
\Kint::$enabled_mode = DEBUGMODE; |
70
|
|
|
ini_set('display_errors', intval(DEBUGMODE)); |
71
|
|
|
ini_set('display_startup_errors', intval(DEBUGMODE)); |
72
|
|
|
if (DEBUGMODE) { |
73
|
|
|
error_reporting(E_ALL); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
if (isset($_SERVER['HTTP_USER_AGENT'])) { |
77
|
|
|
$useragent = $_SERVER['HTTP_USER_AGENT']; |
78
|
|
|
if (strpos($useragent, 'Chrome') === false) { |
79
|
|
|
$conf['php_console'] = false; |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
if (!isset($conf['php_console']) || $conf['php_console'] !== true) { |
84
|
|
|
$handler->setHandleErrors(false); // disable errors handling |
85
|
|
|
$handler->setHandleExceptions(false); // disable exceptions handling |
86
|
|
|
$handler->setCallOldHandlers(true); // disable passing errors & exceptions to prviously defined handlers |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$composerinfo = json_decode(file_get_contents(BASE_PATH . '/composer.json')); |
90
|
|
|
$appVersion = $composerinfo->version; |
91
|
|
|
|
92
|
|
|
$config = [ |
93
|
|
|
'msg' => '', |
94
|
|
|
'appThemes' => [ |
95
|
|
|
'default' => 'Default', |
96
|
|
|
'cappuccino' => 'Cappuccino', |
97
|
|
|
'gotar' => 'Blue/Green', |
98
|
|
|
'bootstrap' => 'Bootstrap3', |
99
|
|
|
], |
100
|
|
|
'settings' => [ |
101
|
|
|
'displayErrorDetails' => DEBUGMODE, |
102
|
|
|
'determineRouteBeforeAppMiddleware' => true, |
103
|
|
|
'base_path' => BASE_PATH, |
104
|
|
|
'debug' => DEBUGMODE, |
105
|
|
|
|
106
|
|
|
'routerCacheFile' => BASE_PATH . '/temp/route.cache.php', |
107
|
|
|
|
108
|
|
|
// Configuration file version. If this is greater than that in config.inc.php, then |
109
|
|
|
// the app will refuse to run. This and $conf['version'] should be incremented whenever |
110
|
|
|
// backwards incompatible changes are made to config.inc.php-dist. |
111
|
|
|
'base_version' => 60, |
112
|
|
|
// Application version |
113
|
|
|
'appVersion' => 'v' . $appVersion, |
114
|
|
|
// Application name |
115
|
|
|
'appName' => 'phpPgAdmin6', |
116
|
|
|
|
117
|
|
|
// PostgreSQL and PHP minimum version |
118
|
|
|
'postgresqlMinVer' => '9.3', |
119
|
|
|
'phpMinVer' => '5.6', |
120
|
|
|
'displayErrorDetails' => DEBUGMODE, |
121
|
|
|
'addContentLengthHeader' => false, |
122
|
|
|
], |
123
|
|
|
]; |
124
|
|
|
|
125
|
|
|
$app = new \Slim\App($config); |
126
|
|
|
|
127
|
|
|
// Fetch DI Container |
128
|
|
|
$container = $app->getContainer(); |
129
|
|
|
|
130
|
|
|
if ($container instanceof \Psr\Container\ContainerInterface) { |
131
|
|
|
\PhpConsole\Helper::register(); // it will register global PC class |
132
|
|
|
if (PHP_SAPI == 'cli-server') { |
133
|
|
|
$subfolder = '/index.php'; |
134
|
|
|
} elseif (isset($conf['subfolder']) && is_string($conf['subfolder'])) { |
135
|
|
|
$subfolder = $conf['subfolder']; |
136
|
|
|
} else { |
137
|
|
|
$normalized_php_self = str_replace('/src/views', '', $container->environment->get('PHP_SELF')); |
|
|
|
|
138
|
|
|
$subfolder = str_replace('/' . basename($normalized_php_self), '', $normalized_php_self); |
139
|
|
|
} |
140
|
|
|
define('SUBFOLDER', $subfolder); |
141
|
|
|
} else { |
142
|
|
|
trigger_error("App Container must be an instance of \Psr\Container\ContainerInterface", E_USER_ERROR); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$container['version'] = 'v' . $appVersion; |
146
|
|
|
$container['errors'] = []; |
147
|
|
|
$container['requestobj'] = $container['request']; |
148
|
|
|
$container['responseobj'] = $container['response']; |
149
|
|
|
$container['php_console'] = $handler; |
150
|
|
|
|
151
|
|
|
// This should be deprecated once we're sure no php scripts are required directly |
152
|
|
|
$container->offsetSet('server', isset($_REQUEST['server']) ? $_REQUEST['server'] : null); |
153
|
|
|
$container->offsetSet('database', isset($_REQUEST['database']) ? $_REQUEST['database'] : null); |
154
|
|
|
$container->offsetSet('schema', isset($_REQUEST['schema']) ? $_REQUEST['schema'] : null); |
155
|
|
|
|
156
|
|
|
$container['flash'] = function () { |
157
|
|
|
return new \Slim\Flash\Messages(); |
158
|
|
|
}; |
159
|
|
|
|
160
|
|
|
$container['utils'] = function ($c) { |
161
|
|
|
$utils = new \PHPPgAdmin\ContainerUtils($c); |
162
|
|
|
return $utils; |
163
|
|
|
}; |
164
|
|
|
|
165
|
|
|
$container['conf'] = function ($c) use ($conf) { |
|
|
|
|
166
|
|
|
|
167
|
|
|
//\Kint::dump($conf); |
168
|
|
|
// Plugins are removed |
169
|
|
|
$conf['plugins'] = []; |
170
|
|
|
|
171
|
|
|
foreach ($conf['servers'] as &$server) { |
172
|
|
|
if (!isset($server['port'])) { |
173
|
|
|
$server['port'] = 5432; |
174
|
|
|
} |
175
|
|
|
if (!isset($server['sslmode'])) { |
176
|
|
|
$server['sslmode'] = 'unspecified'; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
return $conf; |
180
|
|
|
}; |
181
|
|
|
|
182
|
|
|
$container['lang'] = function ($c) { |
183
|
|
|
$translations = new \PHPPgAdmin\Translations($c); |
184
|
|
|
|
185
|
|
|
return $translations->lang; |
186
|
|
|
}; |
187
|
|
|
|
188
|
|
|
$container['plugin_manager'] = function ($c) { |
189
|
|
|
$plugin_manager = new \PHPPgAdmin\PluginManager($c); |
190
|
|
|
return $plugin_manager; |
191
|
|
|
}; |
192
|
|
|
|
193
|
|
|
$container['serializer'] = function ($c) { |
194
|
|
|
$serializerbuilder = \JMS\Serializer\SerializerBuilder::create(); |
195
|
|
|
$serializer = $serializerbuilder |
196
|
|
|
->setCacheDir(BASE_PATH . '/temp/jms') |
197
|
|
|
->setDebug($c->get('settings')['debug']) |
198
|
|
|
->build(); |
199
|
|
|
return $serializer; |
200
|
|
|
}; |
201
|
|
|
|
202
|
|
|
// Create Misc class references |
203
|
|
|
$container['misc'] = function ($c) { |
204
|
|
|
$misc = new \PHPPgAdmin\Misc($c); |
205
|
|
|
|
206
|
|
|
$conf = $c->get('conf'); |
207
|
|
|
|
208
|
|
|
// 4. Check for theme by server/db/user |
209
|
|
|
$_server_info = $misc->getServerInfo(); |
210
|
|
|
|
211
|
|
|
//\PC::debug($_server_info, 'server info'); |
212
|
|
|
|
213
|
|
|
/* starting with PostgreSQL 9.0, we can set the application name */ |
214
|
|
|
if (isset($_server_info['pgVersion']) && $_server_info['pgVersion'] >= 9) { |
215
|
|
|
putenv('PGAPPNAME=' . $c->get('settings')['appName'] . '_' . $c->get('settings')['appVersion']); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
$themefolders = []; |
219
|
|
|
if ($gestor = opendir(THEME_PATH)) { |
220
|
|
|
|
221
|
|
|
/* This is the right way to iterate on a folder */ |
222
|
|
|
while (false !== ($foldername = readdir($gestor))) { |
223
|
|
|
if ($foldername == '.' || $foldername == '..') { |
224
|
|
|
continue; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
$folderpath = THEME_PATH . DIRECTORY_SEPARATOR . $foldername; |
228
|
|
|
|
229
|
|
|
// if $folderpath if indeed a folder and contains a global.css file, then it's a theme |
230
|
|
|
if (is_dir($folderpath) && is_file($folderpath . DIRECTORY_SEPARATOR . 'global.css')) { |
231
|
|
|
$themefolders[$foldername] = $folderpath; |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
closedir($gestor); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
//\PC::debug($themefolders, 'themefolders'); |
239
|
|
|
/* select the theme */ |
240
|
|
|
unset($_theme); |
241
|
|
|
|
242
|
|
|
// List of themes |
243
|
|
|
if (!isset($conf['theme'])) { |
244
|
|
|
$conf['theme'] = 'default'; |
245
|
|
|
} |
246
|
|
|
// 1. Check for the theme from a request var. |
247
|
|
|
// This happens when you use the selector in the intro screen |
248
|
|
|
if (isset($_REQUEST['theme']) && array_key_exists($_REQUEST['theme'], $themefolders)) { |
249
|
|
|
$_theme = $_REQUEST['theme']; |
250
|
|
|
} elseif (!isset($_theme) && isset($_SESSION['ppaTheme']) && array_key_exists($_SESSION['ppaTheme'], $themefolders)) { |
|
|
|
|
251
|
|
|
// 2. Check for theme session var |
252
|
|
|
$_theme = $_SESSION['ppaTheme']; |
253
|
|
|
} elseif (!isset($_theme) && isset($_COOKIE['ppaTheme']) && array_key_exists($_COOKIE['ppaTheme'], $themefolders)) { |
254
|
|
|
// 3. Check for theme in cookie var |
255
|
|
|
$_theme = $_COOKIE['ppaTheme']; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
if (!isset($_theme) && !is_null($_server_info) && array_key_exists('theme', $_server_info)) { |
259
|
|
|
$server_theme = $_server_info['theme']; |
260
|
|
|
|
261
|
|
|
if (isset($server_theme['default']) && array_key_exists($server_theme['default'], $themefolders)) { |
262
|
|
|
$_theme = $server_theme['default']; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
View Code Duplication |
if (isset($_REQUEST['database']) |
|
|
|
|
266
|
|
|
&& isset($server_theme['db'][$_REQUEST['database']]) |
267
|
|
|
&& array_key_exists($server_theme['db'][$_REQUEST['database']], $themefolders) |
268
|
|
|
) { |
269
|
|
|
$_theme = $server_theme['db'][$_REQUEST['database']]; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
View Code Duplication |
if (isset($_server_info['username']) |
|
|
|
|
273
|
|
|
&& isset($server_theme['user'][$_server_info['username']]) |
274
|
|
|
&& array_key_exists($server_theme['user'][$_server_info['username']], $themefolders) |
275
|
|
|
) { |
276
|
|
|
$_theme = $server_theme['user'][$_server_info['username']]; |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
// if any of the above conditions had set the $_theme variable |
280
|
|
|
// then we store it in the session and a cookie |
281
|
|
|
// and we overwrite $conf['theme'] with its value |
282
|
|
|
if (isset($_theme)) { |
283
|
|
|
/* save the selected theme in cookie for a year */ |
284
|
|
|
setcookie('ppaTheme', $_theme, time() + 31536000, '/'); |
285
|
|
|
$_SESSION['ppaTheme'] = $_theme; |
286
|
|
|
$conf['theme'] = $_theme; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
$misc->setConf('theme', $conf['theme']); |
290
|
|
|
|
291
|
|
|
return $misc; |
292
|
|
|
}; |
293
|
|
|
|
294
|
|
|
// Register Twig View helper |
295
|
|
|
$container['view'] = function ($c) { |
296
|
|
|
$conf = $c->get('conf'); |
|
|
|
|
297
|
|
|
$misc = $c->misc; |
298
|
|
|
|
299
|
|
|
$view = new \Slim\Views\Twig(BASE_PATH . '/assets/templates', [ |
300
|
|
|
'cache' => BASE_PATH . '/temp/twigcache', |
301
|
|
|
'auto_reload' => $c->get('settings')['debug'], |
302
|
|
|
'debug' => $c->get('settings')['debug'], |
303
|
|
|
]); |
304
|
|
|
$environment = $c->get('environment'); |
305
|
|
|
$base_script_trailing_str = substr($environment['SCRIPT_NAME'], 1); |
306
|
|
|
$request_basepath = $c['request']->getUri()->getBasePath(); |
307
|
|
|
// Instantiate and add Slim specific extension |
308
|
|
|
$basePath = rtrim(str_ireplace($base_script_trailing_str, '', $request_basepath), '/'); |
309
|
|
|
|
310
|
|
|
$view->addExtension(new Slim\Views\TwigExtension($c['router'], $basePath)); |
311
|
|
|
|
312
|
|
|
$view->offsetSet('subfolder', SUBFOLDER); |
313
|
|
|
$view->offsetSet('theme', $c->misc->getConf('theme')); |
314
|
|
|
$view->offsetSet('Favicon', $c->misc->icon('Favicon')); |
315
|
|
|
$view->offsetSet('Introduction', $c->misc->icon('Introduction')); |
316
|
|
|
$view->offsetSet('lang', $c->lang); |
317
|
|
|
|
318
|
|
|
$view->offsetSet('applangdir', $c->lang['applangdir']); |
319
|
|
|
|
320
|
|
|
$view->offsetSet('appName', $c->get('settings')['appName']); |
321
|
|
|
|
322
|
|
|
$misc->setView($view); |
323
|
|
|
|
324
|
|
|
//\PC::debug($c->conf, 'conf'); |
325
|
|
|
//\PC::debug($c->view->offsetGet('subfolder'), 'subfolder'); |
326
|
|
|
//\PC::debug($c->view->offsetGet('theme'), 'theme'); |
327
|
|
|
|
328
|
|
|
return $view; |
329
|
|
|
}; |
330
|
|
|
|
331
|
|
|
$container['haltHandler'] = function ($c) { |
332
|
|
|
return function ($request, $response, $exits, $status = 500) use ($c) { |
333
|
|
|
$title = 'PHPPgAdmin Error'; |
334
|
|
|
|
335
|
|
|
$html = '<p>The application could not run because of the following error:</p>'; |
336
|
|
|
|
337
|
|
|
$output = sprintf( |
338
|
|
|
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . |
339
|
|
|
'<title>%s</title><style>' . |
340
|
|
|
'body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif;}' . |
341
|
|
|
'h3{margin:0;font-size:28px;font-weight:normal;line-height:30px;}' . |
342
|
|
|
'span{display:inline-block;font-size:16px;}' . |
343
|
|
|
'</style></head><body><h3>%s</h3><p>%s</p><span>%s</span></body></html>', |
344
|
|
|
$title, |
345
|
|
|
$title, |
346
|
|
|
$html, |
347
|
|
|
implode('<br>', $exits) |
348
|
|
|
); |
349
|
|
|
|
350
|
|
|
$body = $response->getBody(); //new \Slim\Http\Body(fopen('php://temp', 'r+')); |
351
|
|
|
$body->write($output); |
352
|
|
|
|
353
|
|
|
return $response |
354
|
|
|
->withStatus($status) |
355
|
|
|
->withHeader('Content-type', 'text/html') |
356
|
|
|
->withBody($body); |
357
|
|
|
}; |
358
|
|
|
}; |
359
|
|
|
|
360
|
|
|
// Set the requestobj and responseobj properties of the container |
361
|
|
|
// as the value of $request and $response, which already contain the route |
362
|
|
|
$app->add( |
363
|
|
|
function ($request, $response, $next) use ($handler) { |
364
|
|
|
$handler->start(); // initialize handlers*/ |
365
|
|
|
|
366
|
|
|
$this['requestobj'] = $request; |
|
|
|
|
367
|
|
|
$this['responseobj'] = $response; |
368
|
|
|
|
369
|
|
|
$this['server'] = $request->getParam('server'); |
370
|
|
|
$this['database'] = $request->getParam('database'); |
371
|
|
|
$this['schema'] = $request->getParam('schema'); |
372
|
|
|
$misc = $this->get('misc'); |
373
|
|
|
|
374
|
|
|
$misc->setHREF(); |
375
|
|
|
$misc->setForm(); |
376
|
|
|
|
377
|
|
|
$this->view->offsetSet('METHOD', $request->getMethod()); |
378
|
|
|
if ($request->getAttribute('route')) { |
379
|
|
|
$this->view->offsetSet('subject', $request->getAttribute('route')->getArgument('subject')); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
$query_string = $request->getUri()->getQuery(); |
383
|
|
|
$this->view->offsetSet('query_string', $query_string); |
384
|
|
|
$path = (SUBFOLDER ? (SUBFOLDER . '/') : '') . $request->getUri()->getPath() . ($query_string ? '?' . $query_string : ''); |
385
|
|
|
$this->view->offsetSet('path', $path); |
386
|
|
|
|
387
|
|
|
$params = $request->getParams(); |
388
|
|
|
|
389
|
|
|
$viewparams = []; |
390
|
|
|
|
391
|
|
|
foreach ($params as $key => $value) { |
392
|
|
|
if (is_scalar($value)) { |
393
|
|
|
$viewparams[$key] = $value; |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
if (isset($_COOKIE['IN_TEST'])) { |
398
|
|
|
$in_test = (string) $_COOKIE['IN_TEST']; |
399
|
|
|
} else { |
400
|
|
|
$in_test = '0'; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
// remove tabs and linebreaks from query |
404
|
|
|
if (isset($params['query'])) { |
405
|
|
|
$viewparams['query'] = str_replace(["\r", "\n", "\t"], ' ', $params['query']); |
406
|
|
|
} |
407
|
|
|
$this->view->offsetSet('params', $viewparams); |
408
|
|
|
$this->view->offsetSet('in_test', $in_test); |
409
|
|
|
|
410
|
|
|
if (count($this['errors']) > 0) { |
411
|
|
|
return ($this->haltHandler)($this->requestobj, $this->responseobj, $this['errors'], 412); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$messages = $this->flash->getMessages(); |
415
|
|
|
if (!empty($messages)) { |
416
|
|
|
foreach ($messages as $key => $message) { |
417
|
|
|
\PC::debug($message, 'Flash: ' . $key); |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
// First execute anything else |
421
|
|
|
$response = $next($request, $response); |
422
|
|
|
|
423
|
|
|
// Any other request, pass on current response |
424
|
|
|
return $response; |
425
|
|
|
} |
426
|
|
|
); |
427
|
|
|
|
428
|
|
|
$container['action'] = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; |
429
|
|
|
|
430
|
|
|
if (!isset($msg)) { |
431
|
|
|
$msg = ''; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
$container['msg'] = $msg; |
435
|
|
|
|
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.