Bearsampp 2026.7.11
Loading...
Searching...
No Matches
class.bin.postgresql.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (c) 2021-2024 Bearsampp
4 * License: GNU General Public License version 3 or later; see LICENSE.txt
5 * Author: Bear
6 * Website: https://bearsampp.com
7 * Github: https://github.com/Bearsampp
8 */
9
16class BinPostgresql extends Module
17{
18 const SERVICE_NAME = 'bearsampppostgresql';
19
20 const ROOT_CFG_ENABLE = 'postgresqlEnable';
21 const ROOT_CFG_VERSION = 'postgresqlVersion';
22
23 const LOCAL_CFG_CTL_EXE = 'postgresqlCtlExe';
24 const LOCAL_CFG_CLI_EXE = 'postgresqlCliExe';
25 const LOCAL_CFG_DUMP_EXE = 'postgresqlDumpExe';
26 const LOCAL_CFG_DUMP_ALL_EXE = 'postgresqlDumpAllExe';
27 const LOCAL_CFG_CONF = 'postgresqlConf';
28 const LOCAL_CFG_HBA_CONF = 'postgresqlUserConf';
29 const LOCAL_CFG_ALT_CONF = 'postgresqlAltConf';
30 const LOCAL_CFG_ALT_HBA_CONF = 'postgresqlAltUserConf';
31 const LOCAL_CFG_PORT = 'postgresqlPort';
32 const LOCAL_CFG_ROOT_USER = 'postgresqlRootUser';
33 const LOCAL_CFG_ROOT_PWD = 'postgresqlRootPwd';
34
35 const CMD_VERSION = '--version';
36
37 private $service;
38 private $errorLog;
39 private $ctlExe;
40 private $cliExe;
41 private $dumpExe;
42 private $dumpAllExe;
43 private $conf;
44 private $hbaConf;
45 private $altConf;
46 private $altHbaConf;
47 private $port;
48 private $rootUser;
49 private $rootPwd;
50
57 public function __construct($id, $type)
58 {
59 Log::initClass( $this );
60 $this->reload( $id, $type );
61 }
62
69 public function reload($id = null, $type = null)
70 {
72 Log::reloadClass( $this );
73
74 $this->name = $bearsamppLang->getValue( Lang::POSTGRESQL );
75 $this->version = $bearsamppConfig->getRaw( self::ROOT_CFG_VERSION );
76 parent::reload( $id, $type );
77
78 $this->enable = $this->enable && $bearsamppConfig->getRaw( self::ROOT_CFG_ENABLE );
79 $this->service = new Win32Service( self::SERVICE_NAME );
80 $this->errorLog = Path::getLogsPath() . '/postgresql.log';
81
82 if ( $this->bearsamppConfRaw !== false ) {
83 $this->ctlExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CTL_EXE];
84 $this->cliExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CLI_EXE];
85 $this->dumpExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_DUMP_EXE];
86 $this->dumpAllExe = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_DUMP_ALL_EXE];
87 $this->conf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_CONF];
88 $this->hbaConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_HBA_CONF];
89 $this->altConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_ALT_CONF];
90 $this->altHbaConf = $this->symlinkPath . '/' . $this->bearsamppConfRaw[self::LOCAL_CFG_ALT_HBA_CONF];
91 $this->port = $this->bearsamppConfRaw[self::LOCAL_CFG_PORT];
92 $this->rootUser = isset( $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_USER] ) ? $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_USER] : 'postgres';
93 $this->rootPwd = isset( $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_PWD] ) ? $this->bearsamppConfRaw[self::LOCAL_CFG_ROOT_PWD] : '';
94 }
95
96 if ( !$this->enable ) {
97 Log::info( $this->name . ' is not enabled!' );
98
99 return;
100 }
101 if ( !is_dir( $this->currentPath ) ) {
102 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->currentPath ) );
103
104 return;
105 }
106 if ( !is_dir( $this->symlinkPath ) ) {
107 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_FILE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->symlinkPath ) );
108
109 return;
110 }
111 if ( !is_file( $this->bearsamppConf ) ) {
112 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->bearsamppConf ) );
113
114 return;
115 }
116 if ( !file_exists( $this->conf ) ) {
117 $this->conf = $this->altConf;
118 }
119 if ( !file_exists( $this->hbaConf ) ) {
120 $this->hbaConf = $this->altHbaConf;
121 }
122
123 if ( !is_file( $this->ctlExe ) ) {
124 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->ctlExe ) );
125
126 return;
127 }
128 if ( !is_file( $this->cliExe ) ) {
129 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->cliExe ) );
130
131 return;
132 }
133 if ( !is_file( $this->dumpExe ) ) {
134 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->dumpExe ) );
135
136 return;
137 }
138 if ( !is_file( $this->dumpAllExe ) ) {
139 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_EXE_NOT_FOUND ), $this->name . ' ' . $this->version, $this->dumpAllExe ) );
140
141 return;
142 }
143 if ( !is_file( $this->conf ) ) {
144 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->conf ) );
145
146 return;
147 }
148 if ( !is_file( $this->hbaConf ) ) {
149 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_CONF_NOT_FOUND ), $this->name . ' ' . $this->version, $this->hbaConf ) );
150
151 return;
152 }
153 if ( !is_numeric( $this->port ) || $this->port <= 0 ) {
154 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_PORT, $this->port ) );
155
156 return;
157 }
158 if ( empty( $this->rootUser ) ) {
159 Log::error( sprintf( $bearsamppLang->getValue( Lang::ERROR_INVALID_PARAMETER ), self::LOCAL_CFG_ROOT_USER, $this->rootUser ) );
160
161 return;
162 }
163
164 $this->service->setDisplayName( APP_TITLE . ' ' . $this->getName() );
165 $this->service->setBinPath( $this->ctlExe );
166 $this->service->setStartType( Win32Service::SERVICE_DEMAND_START );
167 $this->service->setErrorControl( Win32Service::SERVER_ERROR_NORMAL );
168 }
169
175 protected function replaceAll($params)
176 {
177 $content = file_get_contents( $this->bearsamppConf );
178
179 foreach ( $params as $key => $value ) {
180 $content = preg_replace( '|' . $key . ' = .*|', $key . ' = ' . '"' . $value . '"', $content );
181 $this->bearsamppConfRaw[$key] = $value;
182 switch ( $key ) {
183 case self::LOCAL_CFG_PORT:
184 $this->port = $value;
185 break;
186 case self::LOCAL_CFG_ROOT_USER:
187 $this->rootUser = $value;
188 break;
189 case self::LOCAL_CFG_ROOT_PWD:
190 $this->rootPwd = $value;
191 break;
192 }
193 }
194
195 file_put_contents( $this->bearsamppConf, $content );
196 }
197
207 public function changePort($port, $checkUsed = false, $wbProgressBar = null)
208 {
209 global $bearsamppWinbinder;
210
211 if ( !Util::isValidPort( $port ) ) {
212 Log::error( $this->getName() . ' port not valid: ' . $port );
213
214 return false;
215 }
216
217 $port = intval( $port );
218 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
219
220 $isPortInUse = Util::isPortInUse( $port );
221 if ( !$checkUsed || $isPortInUse === false ) {
222 // bearsampp.conf
223 $this->setPort( $port );
224 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
225
226 // conf
227 $this->update();
228 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
229
230 return true;
231 }
232
233 Log::debug( $this->getName() . ' port in used: ' . $port . ' - ' . $isPortInUse );
234
235 return $isPortInUse;
236 }
237
244 public function checkPort($port, $showWindow = false)
245 {
246 global $bearsamppLang, $bearsamppWinbinder;
247 $boxTitle = sprintf($bearsamppLang->getValue(Lang::CHECK_PORT_TITLE), $this->getName(), $port);
248
249 if (!Util::isValidPort($port)) {
250 Log::error($this->getName() . ' port not valid: ' . $port);
251 return false;
252 }
253
254 $dbLink = @pg_connect("host=127.0.0.1 port=$port user={$this->rootUser} password={$this->rootPwd}");
255 if (!$dbLink) {
256 $lastError = error_get_last();
257 $errorMsg = $lastError ? $lastError['message'] : 'Unknown error';
258 Log::debug($this->getName() . ' connection failed: ' . $errorMsg);
259 return $this->handleNonPostgresUsage($port, $showWindow, $boxTitle);
260 }
261
262 // Verify active PostgreSQL connection
263 $connectionStatus = pg_connection_status($dbLink);
264 $isPostgres = $connectionStatus === PGSQL_CONNECTION_OK;
265 pg_close($dbLink);
266
267 if ($isPostgres) {
268 Log::debug($this->getName() . " port $port is used by PostgreSQL");
269 if ($showWindow) {
270 $bearsamppWinbinder->messageBoxInfo(
271 sprintf($bearsamppLang->getValue(Lang::PORT_USED_BY), $port, 'PostgreSQL'),
272 $boxTitle
273 );
274 }
275 return true;
276 }
277
278 return $this->handleNonPostgresUsage($port, $showWindow, $boxTitle);
279 }
280
284 private function handleNonPostgresUsage($port, $showWindow, $boxTitle)
285 {
286 global $bearsamppLang, $bearsamppWinbinder;
287
289 Log::debug($this->getName() . " port $port used by non-PostgreSQL service");
290 if ($showWindow) {
291 $bearsamppWinbinder->messageBoxWarning(
293 $boxTitle
294 );
295 }
296 return false;
297 }
298
299 Log::debug($this->getName() . " port $port not in use");
300 if ($showWindow) {
301 $bearsamppWinbinder->messageBoxError(
302 sprintf($bearsamppLang->getValue(Lang::PORT_NOT_USED), $port),
303 $boxTitle
304 );
305 }
306 return false;
307 }
308
317 public function changeRootPassword($currentPwd, $newPwd, $wbProgressBar = null)
318 {
319 global $bearsamppWinbinder;
320 $error = null;
321
322 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
323 $dbLink = pg_connect( 'host=127.0.0.1 port=' . $this->port . ' user=' . $this->rootUser . ' password=' . $currentPwd );
324
325 if ( !$dbLink ) {
326 $error = error_get_last()['message'];
327 }
328
329 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
330 $pgr = pg_query_params( $dbLink, 'SELECT quote_ident($1)', array(pg_escape_string( $this->rootUser )) );
331 list( $quoted_user ) = pg_fetch_array( $pgr );
332 $password = pg_escape_string( $newPwd );
333 $result = pg_query( $dbLink, "ALTER USER $quoted_user WITH PASSWORD '$password'" );
334 if ( empty( $error ) && !$result ) {
335 $error = pg_last_error( $dbLink );
336 }
337
338 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
339 if ( $dbLink ) {
340 pg_close( $dbLink );
341 }
342
343 if ( !empty( $error ) ) {
344 return $error;
345 }
346
347 // bearsampp.conf
348 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
349 $this->setRootPwd( $newPwd );
350
351 // conf
352 $this->update();
353 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
354
355 return true;
356 }
357
366 public function checkRootPassword($currentPwd = null, $wbProgressBar = null)
367 {
368 global $bearsamppWinbinder;
369 $currentPwd = $currentPwd == null ? $this->rootPwd : $currentPwd;
370 $error = null;
371
372 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
373 $dbLink = pg_connect( 'host=127.0.0.1 port=' . $this->port . ' user=' . $this->rootUser . ' password=' . $currentPwd );
374 if ( !$dbLink ) {
375 $error = error_get_last()['message'];
376 }
377
378 $bearsamppWinbinder->incrProgressBar( $wbProgressBar );
379 if ( $dbLink ) {
380 pg_close( $dbLink );
381 }
382
383 if ( !empty( $error ) ) {
384 return $error;
385 }
386
387 return true;
388 }
389
398 public function switchVersion($version, $showWindow = false)
399 {
400 Log::debug( 'Switch ' . $this->name . ' version to ' . $version );
401
402 return $this->updateConfig( $version, 0, $showWindow );
403 }
404
414 protected function updateConfig($version = null, $sub = 0, $showWindow = false)
415 {
416 global $bearsamppLang, $bearsamppApps, $bearsamppWinbinder;
417
418 if ( !$this->enable ) {
419 return true;
420 }
421
422 $version = $version == null ? $this->version : $version;
423 Log::debug( ($sub > 0 ? str_repeat( ' ', 2 * $sub ) : '') . 'Update ' . $this->name . ' ' . $version . ' config' );
424
425 $boxTitle = sprintf( $bearsamppLang->getValue( Lang::SWITCH_VERSION_TITLE ), $this->getName(), $version );
426
427 $currentPath = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, Path::getModuleCurrentPath($this) );
428 $conf = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, $this->getConf() );
429 $bearsamppConf = str_replace( 'postgresql' . $this->getVersion(), 'postgresql' . $version, $this->bearsamppConf );
430
431 if ( $this->version != $version ) {
432 $this->initData( $currentPath );
433 }
434
435 if ( !file_exists( $conf ) || !file_exists( $bearsamppConf ) ) {
436 Log::error( 'bearsampp config files not found for ' . $this->getName() . ' ' . $version );
437 if ( $showWindow ) {
438 $bearsamppWinbinder->messageBoxError(
439 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_NOT_FOUND_ERROR ), $this->getName() . ' ' . $version ),
440 $boxTitle
441 );
442 }
443
444 return false;
445 }
446
447 $bearsamppConfRaw = parse_ini_file( $bearsamppConf );
448 if ( $bearsamppConfRaw === false || !isset( $bearsamppConfRaw[self::ROOT_CFG_VERSION] ) || $bearsamppConfRaw[self::ROOT_CFG_VERSION] != $version ) {
449 Log::error( 'bearsampp config file malformed for ' . $this->getName() . ' ' . $version );
450 if ( $showWindow ) {
451 $bearsamppWinbinder->messageBoxError(
452 sprintf( $bearsamppLang->getValue( Lang::BEARSAMPP_CONF_MALFORMED_ERROR ), $this->getName() . ' ' . $version ),
453 $boxTitle
454 );
455 }
456
457 return false;
458 }
459
460 // bearsampp.conf
461 $this->setVersion( $version );
462
463 // conf
464 Util::replaceInFile( $this->getConf(), array(
465 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
466 ) );
467
468 // phppgadmin
469 $bearsamppApps->getPhppgadmin()->update( $sub + 1 );
470
471 return true;
472 }
473
479 public function initData($path = null)
480 {
481 $path = $path != null ? $path : Path::getModuleCurrentPath($this);
482
483 if ( file_exists( $path . '/data' ) ) {
484 // Even if it exists, ensure placeholders are replaced in the whole folder
485 $filesToScan = Util::getFilesToScan( array(
486 array(
487 'path' => $path,
488 'includes' => array( '.conf', '.bat', '.ber' ),
489 'recursive' => true
490 )
491 ) );
492 if ( !empty( $filesToScan ) ) {
493 Path::changePath( $filesToScan );
494 }
495 return;
496 }
497
499
500 // Replace placeholders in the newly created data directory and the whole version folder
501 $filesToScan = Util::getFilesToScan( array(
502 array(
503 'path' => $path,
504 'includes' => array( '.conf', '.bat', '.ber' ),
505 'recursive' => true
506 )
507 ) );
508 if ( !empty( $filesToScan ) ) {
509 Path::changePath( $filesToScan );
510 }
511 }
512
516 public function rebuildConf()
517 {
518 Util::replaceInFile( $this->conf, array(
519 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
520 ) );
521 Util::replaceInFile( $this->altConf, array(
522 '/^port(.*?)=(.*?)(\d+)/' => 'port = ' . $this->port
523 ) );
524 }
525
533 public function getCmdLineOutput($cmd)
534 {
535 $result = null;
536
537 $bin = $this->getCliExe();
538 if ( file_exists( $bin ) ) {
539 $tmpResult = Batch::exec( 'postgresqlGetCmdLineOutput', '"' . $bin . '" ' . $cmd );
540 if ( $tmpResult !== false && is_array( $tmpResult ) ) {
541 $result = trim( str_replace( $bin, '', implode( PHP_EOL, $tmpResult ) ) );
542 }
543 }
544
545 return $result;
546 }
547
553 public function setVersion($version)
554 {
555 global $bearsamppConfig;
556 $this->version = $version;
557 $bearsamppConfig->replace( self::ROOT_CFG_VERSION, $version );
558 $this->reload();
559 }
560
566 public function getService()
567 {
568 return $this->service;
569 }
570
577 public function setEnable($enabled, $showWindow = false)
578 {
579 global $bearsamppConfig, $bearsamppLang, $bearsamppWinbinder;
580
581 if ( $enabled == Config::ENABLED && !is_dir( $this->currentPath ) ) {
582 Log::debug( $this->getName() . ' cannot be enabled because bundle ' . $this->getVersion() . ' does not exist in ' . $this->currentPath );
583 if ( $showWindow ) {
584 $bearsamppWinbinder->messageBoxError(
585 sprintf( $bearsamppLang->getValue( Lang::ENABLE_BUNDLE_NOT_EXIST ), $this->getName(), $this->getVersion(), $this->currentPath ),
586 sprintf( $bearsamppLang->getValue( Lang::ENABLE_TITLE ), $this->getName() )
587 );
588 }
589 $enabled = Config::DISABLED;
590 }
591
592 Log::info( $this->getName() . ' switched to ' . ($enabled == Config::ENABLED ? 'enabled' : 'disabled') );
593 $this->enable = $enabled == Config::ENABLED;
594 $bearsamppConfig->replace( self::ROOT_CFG_ENABLE, $enabled );
595
596 $this->reload();
597 if ( $this->enable ) {
598 Util::installService( $this, $this->port, null, $showWindow );
599 }
600 else {
601 Util::removeService( $this->service, $this->name );
602 }
603 }
604
610 public function getErrorLog()
611 {
612 return $this->errorLog;
613 }
614
620 public function getCtlExe()
621 {
622 return $this->ctlExe;
623 }
624
630 public function getCliExe()
631 {
632 return $this->cliExe;
633 }
634
640 public function getDumpExe()
641 {
642 return $this->dumpExe;
643 }
644
650 public function getDumpAllExe()
651 {
652 return $this->dumpAllExe;
653 }
654
660 public function getConf()
661 {
662 return $this->conf;
663 }
664
670 public function getHbaConf()
671 {
672 return $this->hbaConf;
673 }
674
680 public function getPort()
681 {
682 return $this->port;
683 }
684
690 public function setPort($port)
691 {
692 $this->replace( self::LOCAL_CFG_PORT, $port );
693 }
694
700 public function getRootUser()
701 {
702 return $this->rootUser;
703 }
704
710 public function setRootUser($rootUser)
711 {
712 $this->replace( self::LOCAL_CFG_ROOT_USER, $rootUser );
713 }
714
720 public function getRootPwd()
721 {
722 return $this->rootPwd;
723 }
724
730 public function setRootPwd($rootPwd)
731 {
732 $this->replace( self::LOCAL_CFG_ROOT_PWD, $rootPwd );
733 }
734}
$result
global $bearsamppLang
global $bearsamppRoot
static initializePostgresql($path)
static exec($basename, $content, $timeout=true, $catchOutput=true, $standalone=false, $silent=true, $rebuild=true)
handleNonPostgresUsage($port, $showWindow, $boxTitle)
updateConfig($version=null, $sub=0, $showWindow=false)
changeRootPassword($currentPwd, $newPwd, $wbProgressBar=null)
switchVersion($version, $showWindow=false)
checkPort($port, $showWindow=false)
changePort($port, $checkUsed=false, $wbProgressBar=null)
setEnable($enabled, $showWindow=false)
reload($id=null, $type=null)
checkRootPassword($currentPwd=null, $wbProgressBar=null)
const DISABLED
const ENABLED
const ENABLE_BUNDLE_NOT_EXIST
const BEARSAMPP_CONF_MALFORMED_ERROR
const ERROR_EXE_NOT_FOUND
const ERROR_CONF_NOT_FOUND
const POSTGRESQL
const BEARSAMPP_CONF_NOT_FOUND_ERROR
const PORT_NOT_USED
const ERROR_INVALID_PARAMETER
const ENABLE_TITLE
const SWITCH_VERSION_TITLE
const CHECK_PORT_TITLE
const PORT_USED_BY
const PORT_USED_BY_ANOTHER_DBMS
const ERROR_FILE_NOT_FOUND
static info($data, $file=null)
static debug($data, $file=null)
static reloadClass($classInstance)
static error($data, $file=null)
static initClass($classInstance)
update($sub=0, $showWindow=false)
replace($key, $value)
static getLogsPath($aetrayPath=false)
static changePath($filesToScan, $rootPath=null)
static getModuleCurrentPath($module)
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static removeService($service, $name)
static isValidPort($port)
static isPortInUse($port)
static getFilesToScan($path=null, $useCache=true, $forceRefresh=false)
static replaceInFile($path, $replaceList)
global $bearsamppConfig
Definition homepage.php:41
const APP_TITLE
Definition root.php:13