*/ 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); } /** * @return array{host:string,port:int,database:string,user:string,password:string,socket:string} */ public static function loadMysqlCredentials(string $path): array { if (!is_readable($path)) { throw new RuntimeException('Brak pliku z danymi MySQL: ' . $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]) : '3306'), 'database' => trim($parts[2]) !== '' ? trim($parts[2]) : 'mysql', 'user' => trim($parts[3]) !== '' ? trim($parts[3]) : 'root', 'password' => trim(implode(':', array_slice($parts, 4))), 'socket' => '', ]; } } $host = trim((string)($kv['HOST'] ?? $kv['MYSQL_HOST'] ?? '')); if ($host === '') { $host = 'localhost'; } $portRaw = trim((string)($kv['PORT'] ?? $kv['MYSQL_PORT'] ?? '3306')); $port = preg_match('/^[0-9]+$/', $portRaw) ? (int)$portRaw : 3306; if ($port < 1 || $port > 65535) { $port = 3306; } $database = trim((string)($kv['DATABASE'] ?? $kv['MYSQL_DATABASE'] ?? $kv['DB'] ?? 'mysql')); if ($database === '' || $database === '*') { $database = 'mysql'; } $user = trim((string)($kv['USER'] ?? $kv['MYSQL_USER'] ?? 'root')); $password = trim((string)($kv['PASSWORD'] ?? $kv['PASSWD'] ?? $kv['MYSQL_PASSWORD'] ?? '')); $socket = trim((string)($kv['SOCKET'] ?? $kv['MYSQL_SOCKET'] ?? '')); if ($user === '') { throw new RuntimeException('Brak użytkownika MySQL w pliku: ' . $path); } return [ 'host' => $host, 'port' => $port, 'database' => $database, 'user' => $user, 'password' => $password, 'socket' => $socket, ]; } public function mysqlCredentialsFile(): string { $path = trim($this->getString('MYSQL_PLUGIN_CREDENTIALS_FILE', '/usr/local/directadmin/conf/alt-mysql.conf')); return $path !== '' ? $path : '/usr/local/directadmin/conf/alt-mysql.conf'; } public function mysqlClientBinDir(): string { return rtrim(trim($this->getString('MYSQL_PLUGIN_CLIENT_BIN_DIR', '')), '/'); } 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('MYSQL_PLUGIN_DEFAULT_DB_LIMIT', 5); return $limit < 0 ? 0 : $limit; } public function alwaysEnabled(): bool { return $this->getBool('always_enabled', true); } public function maxHostsPerUser(): int { $limit = $this->getInt('MYSQL_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'], false); } if (array_key_exists('MYSQL_PLUGIN_ALLOW_REMOTE_HOSTS', $this->values)) { return self::toBool($this->values['MYSQL_PLUGIN_ALLOW_REMOTE_HOSTS'], false); } return false; } public function modifyServerConfiguration(): bool { if ($this->allowRemoteHosts()) { return true; } if (array_key_exists('modify_server_configuration', $this->values)) { return self::toBool($this->values['modify_server_configuration'], false); } if (array_key_exists('MYSQL_PLUGIN_MODIFY_SERVER_CONFIGURATION', $this->values)) { return self::toBool($this->values['MYSQL_PLUGIN_MODIFY_SERVER_CONFIGURATION'], false); } return false; } public function hostSyncScript(): string { $default = '/usr/local/directadmin/plugins/alt-mysql/scripts/setup/mysql_host_sync.sh'; $path = $this->getString('MYSQL_PLUGIN_SUDO_SYNC_HOSTS', $default); return $path !== '' ? $path : $default; } 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_PHPMYADMIN', $this->getBool('ENABLE_ADMINER', true))) { return false; } return $this->isPhpMyAdminInstalled(); } public function phpMyAdminInstallDir(): string { $path = trim($this->getString('PHPMYADMIN_INSTALL_DIR', '/var/www/html/alt-mysql-phpmyadmin')); return $path !== '' ? rtrim($path, '/') : '/var/www/html/alt-mysql-phpmyadmin'; } public function adminerUrlPath(): string { $path = trim($this->getString('PHPMYADMIN_URL_PATH', $this->getString('ADMINER_URL_PATH', '/alt-mysql-phpmyadmin'))); if ($path === '') { return '/alt-mysql-phpmyadmin'; } if ($path[0] !== '/') { $path = '/' . $path; } $path = rtrim($path, '/'); return $path !== '' ? $path : '/alt-mysql-phpmyadmin'; } public function adminerPublicBaseUrl(): string { $url = trim($this->getString('PHPMYADMIN_PUBLIC_BASE_URL', $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('PHPMYADMIN_PUBLIC', $this->getBool('ADMINER_PUBLIC', false)); } public function adminerSsoDir(): string { $path = trim($this->getString('PHPMYADMIN_SSO_DIR', $this->getString('ADMINER_SSO_DIR', $this->phpMyAdminInstallDir() . '/runtime'))); if ($path === '') { $path = $this->phpMyAdminInstallDir() . '/runtime'; } return rtrim($path, '/'); } public function adminerTicketTtl(): int { $ttl = $this->getInt('PHPMYADMIN_TICKET_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('PHPMYADMIN_SESSION_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('PHPMYADMIN_ROLE_TTL', $this->getInt('ADMINER_ROLE_TTL', 1200)); if ($ttl < 120) { return 120; } if ($ttl > 7200) { return 7200; } return $ttl; } public function phpMyAdminIntegrationWarning(): string { if (!$this->getBool('ENABLE_PHPMYADMIN', $this->getBool('ENABLE_ADMINER', true))) { return ''; } $dir = $this->phpMyAdminInstallDir(); if (!is_dir($dir)) { return 'Prywatna kopia phpMyAdmin dla pluginu nie jest zainstalowana. Uruchom scripts/setup/phpmyadmin_private_install.sh jako root.'; } $config = $dir . '/config.inc.php'; $signon = $dir . '/da_signon.php'; $signonUrl = $dir . '/da_login.php'; if (!is_readable($config)) { return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale nie można odczytać config.inc.php.'; } if (!is_readable($signon)) { return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale brakuje da_signon.php. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.'; } if (!is_readable($signonUrl)) { return 'Prywatny phpMyAdmin pluginu jest zainstalowany, ale brakuje da_login.php. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.'; } $contents = file_get_contents($config); if (!is_string($contents) || strpos($contents, 'DA_MYSQL_PMA_DEFAULT_CONF') === false) { return 'Prywatny phpMyAdmin pluginu nie ma konfiguracji alt-mysql. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.'; } if (strpos($contents, "SignonURL'] = da_mysql_phpmyadmin_signon_url") === false) { return 'Prywatny phpMyAdmin pluginu nie ma wymaganej konfiguracji SignonURL. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.'; } $signonUrlContents = file_get_contents($signonUrl); if (!is_string($signonUrlContents) || strpos($signonUrlContents, 'da_mysql_login_consume_ticket') === false) { return 'Prywatny phpMyAdmin pluginu nie obsługuje jednorazowych ticketów SSO. Uruchom scripts/setup/phpmyadmin_repair.sh jako root.'; } return ''; } 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/mysql_backup'); if ($path === '') { $path = '/home/admin/mysql_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/mysql_backup'); if ($path === '') { $path = '/home/DA_USER/mysql_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 . '|alt-mysql'); } 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 function isPhpMyAdminInstalled(): bool { $dir = $this->phpMyAdminInstallDir(); return is_dir($dir) && is_readable($dir . '/index.php') && is_readable($dir . '/config.inc.php'); } 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); } }