*/ private array $values; private function __construct(array $values) { $this->values = $values; } public static function load(string $path): self { $values = []; if (is_readable($path)) { $lines = file($path, FILE_IGNORE_NEW_LINES); if ($lines !== false) { foreach ($lines as $line) { $line = trim($line); if ($line === '' || $line[0] === '#') { continue; } if (!preg_match('/^([A-Za-z0-9_]+)=(.*)$/', $line, $m)) { continue; } $values[$m[1]] = self::parseShValue((string)$m[2]); } } } return new self($values); } public static function loadPostgresqlCredentials(string $path): array { if (!is_readable($path)) { throw new RuntimeException('Brak pliku z danymi PostgreSQL: ' . $path); } $lines = file($path, FILE_IGNORE_NEW_LINES); if ($lines === false) { throw new RuntimeException('Nie można odczytać pliku: ' . $path); } $kv = []; foreach ($lines as $line) { $line = trim($line); if ($line === '' || $line[0] === '#') { continue; } if (strpos($line, '=') !== false) { [$k, $v] = array_map('trim', explode('=', $line, 2)); if ($k !== '') { $kv[strtoupper($k)] = self::parseShValue($v); } continue; } $parts = explode(':', $line); if (count($parts) >= 5) { return [ 'host' => trim($parts[0]) !== '' ? trim($parts[0]) : 'localhost', 'port' => (int)(trim($parts[1]) !== '' ? trim($parts[1]) : '5432'), 'database' => trim($parts[2]) !== '' ? trim($parts[2]) : 'postgres', 'user' => trim($parts[3]), 'password' => trim(implode(':', array_slice($parts, 4))), ]; } } if (!empty($kv['PG_HOST']) || !empty($kv['PG_USER']) || !empty($kv['PG_PASSWORD'])) { return [ 'host' => $kv['PG_HOST'] ?? 'localhost', 'port' => (int)($kv['PG_PORT'] ?? '5432'), 'database' => $kv['PG_DATABASE'] ?? ($kv['PG_DBNAME'] ?? 'postgres'), 'user' => $kv['PG_USER'] ?? 'postgres', 'password' => $kv['PG_PASSWORD'] ?? '', ]; } throw new RuntimeException('Niepoprawny format pliku danych PostgreSQL: ' . $path); } public function getString(string $key, string $default = ''): string { return isset($this->values[$key]) ? trim($this->values[$key]) : $default; } public function getInt(string $key, int $default = 0): int { if (!isset($this->values[$key])) { return $default; } $value = trim($this->values[$key]); if ($value === '' || !preg_match('/^-?[0-9]+$/', $value)) { return $default; } return (int)$value; } public function getBool(string $key, bool $default = false): bool { if (!isset($this->values[$key])) { return $default; } return self::toBool($this->values[$key], $default); } public function defaultDatabasesLimit(): int { $limit = $this->getInt('PG_PLUGIN_DEFAULT_DB_LIMIT', 5); return $limit < 0 ? 0 : $limit; } public function maxHostsPerUser(): int { $limit = $this->getInt('PG_PLUGIN_MAX_HOSTS_PER_USER', 30); return $limit < 1 ? 1 : $limit; } public function allowRemoteHosts(): bool { if (array_key_exists('allow_remote_hosts', $this->values)) { return self::toBool($this->values['allow_remote_hosts'], true); } return $this->getBool('PG_PLUGIN_ALLOW_REMOTE_HOSTS', true); } public function hbaSyncScript(): string { $path = $this->getString('PG_PLUGIN_SUDO_SYNC_HBA', '/usr/local/directadmin/plugins/da-postgresql/scripts/setup/pg_hba_sync.sh'); return $path !== '' ? $path : '/usr/local/directadmin/plugins/da-postgresql/scripts/setup/pg_hba_sync.sh'; } public function enableBackup(): bool { return $this->getBool('ENABLE_BACKUP', true); } public function enableUploadBackup(): bool { return $this->getBool('ENABLE_UPLOAD_BACKUP', true); } public function enableAdminer(): bool { if (!$this->getBool('ENABLE_ADMINER', false)) { return false; } return $this->isAdminerInstalled(); } public function adminerUrlPath(): string { $path = trim($this->getString('ADMINER_URL_PATH', '/adminer')); if ($path === '') { return '/adminer'; } if ($path[0] !== '/') { $path = '/' . $path; } $path = rtrim($path, '/'); return $path !== '' ? $path : '/adminer'; } public function adminerPublicBaseUrl(): string { $url = trim($this->getString('ADMINER_PUBLIC_BASE_URL', '')); if ($url === '') { return ''; } $url = rtrim($url, '/'); if (!preg_match('#^https?://#i', $url)) { return ''; } return $url; } public function adminerPublic(): bool { return $this->getBool('ADMINER_PUBLIC', false); } public function adminerSsoDir(): string { $path = trim($this->getString('ADMINER_SSO_DIR', '/var/www/html/adminer/runtime')); if ($path === '') { $path = '/var/www/html/adminer/runtime'; } return rtrim($path, '/'); } public function adminerTicketTtl(): int { $ttl = $this->getInt('ADMINER_TICKET_TTL', 120); if ($ttl < 10) { return 10; } if ($ttl > 300) { return 300; } return $ttl; } public function adminerSessionTtl(): int { $ttl = $this->getInt('ADMINER_SESSION_TTL', 900); if ($ttl < 60) { return 60; } if ($ttl > 3600) { return 3600; } return $ttl; } public function adminerRoleTtl(): int { $ttl = $this->getInt('ADMINER_ROLE_TTL', 1200); if ($ttl < 120) { return 120; } if ($ttl > 7200) { return 7200; } return $ttl; } private function isAdminerInstalled(): bool { $indexFile = '/var/www/html/adminer/index.php'; $coreFile = '/var/www/html/adminer/adminer-core.php'; return is_file($indexFile) && is_readable($indexFile) && is_file($coreFile) && is_readable($coreFile); } public function allowRestoreToOtherDatabase(): bool { return $this->getBool('ALLOW_RESTORE_TO_OTHER_DATABASE', true); } public function enableTableCount(): bool { return $this->getBool('ENABLE_TABLE_COUNT', true); } public function countDatabaseSize(): bool { return $this->getBool('COUNT_DATABASE_SIZE', true); } public function compressBackup(): bool { return $this->getBool('COMPRESS_BACKUP', true); } public function hitmeBackupLocation(string $daUser = ''): string { $path = $this->getString('HITME_BACKUP_LOCATION', '/home/admin/postgres_backup'); if ($path === '') { $path = '/home/admin/postgres_backup'; } if ($daUser !== '') { $path = str_replace(['DA_USER', 'da_user'], $daUser, $path); } return rtrim($path, '/'); } public function userBaseBackupDir(string $daUser = ''): string { $path = $this->getString('USER_BASE_BACKUP_DIR', '/home/DA_USER/postgres_backup'); if ($path === '') { $path = '/home/DA_USER/postgres_backup'; } if ($daUser !== '') { $path = str_replace(['DA_USER', 'da_user'], $daUser, $path); } return rtrim($path, '/'); } public function userBackupDateFormat(): string { $format = $this->getString('USER_BACKUP_DATE_FORMAT', 'd.m.Y-H.i'); return $format !== '' ? $format : 'd.m.Y-H.i'; } public function loadOrCreateSecret(string $path): string { if (is_readable($path)) { $value = trim((string)file_get_contents($path)); if ($value !== '') { return $value; } } $secret = bin2hex(random_bytes(32)); $dir = dirname($path); if (!is_dir($dir)) { @mkdir($dir, 0700, true); } if (@file_put_contents($path, $secret) !== false) { @chmod($path, 0600); return $secret; } return hash('sha256', __FILE__ . '|' . php_uname('n') . '|' . $path . '|da-postgresql'); } public static function toBool(string $value, bool $default = false): bool { $v = strtolower(trim($value)); if ($v === '') { return $default; } if (in_array($v, ['1', 'true', 'yes', 'y', 'on', 'checked'], true)) { return true; } if (in_array($v, ['0', 'false', 'no', 'n', 'off', 'unchecked'], true)) { return false; } return $default; } private static function parseShValue(string $value): string { $value = trim($value); if ($value === '') { return ''; } if ($value[0] === '\'' && substr($value, -1) === '\'') { return substr($value, 1, -1); } if ($value[0] === '"' && substr($value, -1) === '"') { return stripcslashes(substr($value, 1, -1)); } $value = preg_replace('/\s+#.*$/', '', $value); return trim((string)$value); } }