From f2482bb3ce2e9a7f291e049ba88c6b0a6bf6482d Mon Sep 17 00:00:00 2001 From: James Mortemore Date: Mon, 9 Feb 2026 10:40:00 +0000 Subject: [PATCH] feat: added litebans convertor --- .../common/commands/ImportCommand.java | 12 + .../common/data/PlayerHistoryData.java | 17 + .../common/storage/conversion/LiteBans.java | 1004 +++++++++++++++++ common/src/main/resources/messages.yml | 3 + .../conversion/LiteBansImportTest.java | 388 +++++++ e2e/platforms/bukkit/configs/messages.yml | 3 + .../bungee/configs/banmanager/messages.yml | 3 + e2e/platforms/fabric/configs/messages.yml | 3 + .../sponge/configs/banmanager/messages.yml | 3 + .../sponge7/configs/banmanager/messages.yml | 3 + .../velocity/configs/banmanager/messages.yml | 3 + 11 files changed, 1442 insertions(+) create mode 100644 common/src/main/java/me/confuser/banmanager/common/storage/conversion/LiteBans.java create mode 100644 common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/ImportCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/ImportCommand.java index 8458e71c3..871468389 100644 --- a/common/src/main/java/me/confuser/banmanager/common/commands/ImportCommand.java +++ b/common/src/main/java/me/confuser/banmanager/common/commands/ImportCommand.java @@ -9,6 +9,7 @@ import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.storage.conversion.AdvancedBan; import me.confuser.banmanager.common.storage.conversion.H2; +import me.confuser.banmanager.common.storage.conversion.LiteBans; import me.confuser.banmanager.common.util.IPUtils; import me.confuser.banmanager.common.util.Message; import me.confuser.banmanager.common.util.UUIDUtils; @@ -32,6 +33,7 @@ public class ImportCommand extends CommonCommand { add("ips"); add("advancedban"); add("h2"); + add("litebans"); }}; public ImportCommand(BanManagerPlugin plugin) { @@ -84,6 +86,16 @@ public boolean onCommand(CommonSender sender, CommandParser parser) { finishedMessage = Message.getString("import.h2.finished"); new H2(getPlugin(), parser.args[1]); + } else if (parser.args[0].equals("litebans")) { + if (parser.args.length < 5) { + sender.sendMessage("/bmimport litebans [password] [tablePrefix]"); + return; + } + + sender.sendMessage(Message.getString("import.litebans.started")); + finishedMessage = Message.getString("import.litebans.finished"); + + new LiteBans(getPlugin(), parser.args); } if (sender != null) { diff --git a/common/src/main/java/me/confuser/banmanager/common/data/PlayerHistoryData.java b/common/src/main/java/me/confuser/banmanager/common/data/PlayerHistoryData.java index eefb60460..6debd5f1e 100644 --- a/common/src/main/java/me/confuser/banmanager/common/data/PlayerHistoryData.java +++ b/common/src/main/java/me/confuser/banmanager/common/data/PlayerHistoryData.java @@ -53,4 +53,21 @@ public PlayerHistoryData(PlayerData player, boolean logIp) { this.ip = logIp ? player.getIp() : null; this.join = System.currentTimeMillis() / 1000L; } + + /** + * Create a session history record with an explicit IP address and timestamps. + * Useful for importing data where the IP and timestamps are known. + * + * @param player The player data + * @param ip The IP address to record (can be null) + * @param join The join timestamp in seconds + * @param leave The leave timestamp in seconds + */ + public PlayerHistoryData(PlayerData player, IPAddress ip, long join, long leave) { + this.player = player; + this.name = player.getName(); + this.ip = ip; + this.join = join; + this.leave = leave; + } } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/conversion/LiteBans.java b/common/src/main/java/me/confuser/banmanager/common/storage/conversion/LiteBans.java new file mode 100644 index 000000000..63b7217fe --- /dev/null +++ b/common/src/main/java/me/confuser/banmanager/common/storage/conversion/LiteBans.java @@ -0,0 +1,1004 @@ +package me.confuser.banmanager.common.storage.conversion; + +import me.confuser.banmanager.common.BanManagerPlugin; +import me.confuser.banmanager.common.configs.DatabaseConfig; +import me.confuser.banmanager.common.data.*; +import me.confuser.banmanager.common.ipaddr.IPAddress; +import me.confuser.banmanager.common.ipaddr.IPAddressSeqRange; +import me.confuser.banmanager.common.ipaddr.IPAddressString; +import me.confuser.banmanager.common.ormlite.stmt.StatementBuilder; +import me.confuser.banmanager.common.ormlite.support.ConnectionSource; +import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; +import me.confuser.banmanager.common.ormlite.support.DatabaseResults; +import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig; + +import java.io.File; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.UUID; + +public class LiteBans implements IConverter { + private ConnectionSource connection; + private BanManagerPlugin plugin; + private String tablePrefix; + + private int importedBans = 0; + private int importedBanRecords = 0; + private int importedMutes = 0; + private int importedMuteRecords = 0; + private int importedWarnings = 0; + private int importedKicks = 0; + private int importedHistory = 0; + private int skipped = 0; + + /** + * Constructor for testing - uses an existing connection source. + */ + public LiteBans(BanManagerPlugin plugin, ConnectionSource connection, String tablePrefix) { + this.plugin = plugin; + this.connection = connection; + this.tablePrefix = tablePrefix; + + plugin.getLogger().info("Using existing connection for LiteBans import..."); + + runImport(); + + // Don't close external connections + logResults(); + } + + public LiteBans(BanManagerPlugin plugin, String[] args) { + this.plugin = plugin; + + if (args.length < 5) { + plugin.getLogger().severe("Usage: /bmimport litebans [password] [tablePrefix]"); + return; + } + + String host = args[1]; + int port; + try { + port = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + plugin.getLogger().severe("Invalid port number: " + args[2]); + return; + } + String database = args[3]; + String username = args[4]; + String password = args.length >= 6 ? args[5] : ""; + this.tablePrefix = args.length >= 7 ? args[6] : "litebans_"; + + LiteBansConfig config = new LiteBansConfig( + "mysql", host, port, database, username, password, + false, false, true, true, 2, 0, 1800000, 30000, + new HashMap<>(), plugin.getDataFolder() + ); + + try { + connection = plugin.createConnection(config, "litebans-import"); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to connect to LiteBans database"); + return; + } + + plugin.getLogger().info("Connected to LiteBans database, starting import..."); + + runImport(); + + connection.closeQuietly(); + + logResults(); + } + + /** + * Run all imports using the interface methods. + */ + private void runImport() { + importPlayerBans(); + importIpBans(); + importIpRangeBans(); + importPlayerMutes(); + importIpMutes(); + importPlayerWarnings(); + importKicks(); + importHistory(); + } + + private void logResults() { + plugin.getLogger().info("LiteBans import complete:"); + plugin.getLogger().info(" - Bans: " + importedBans + " active, " + importedBanRecords + " records"); + plugin.getLogger().info(" - Mutes: " + importedMutes + " active, " + importedMuteRecords + " records"); + plugin.getLogger().info(" - Warnings: " + importedWarnings); + plugin.getLogger().info(" - Kicks: " + importedKicks); + plugin.getLogger().info(" - History: " + importedHistory); + plugin.getLogger().info(" - Skipped: " + skipped); + } + + /** + * Resolve an actor from LiteBans UUID and name fields. + * Handles CONSOLE, invalid UUIDs, and name lookups. + */ + private PlayerData resolveActor(String uuidStr, String name) { + if (uuidStr == null || uuidStr.equals("CONSOLE") || uuidStr.isEmpty()) { + return plugin.getPlayerStorage().getConsole(); + } + + if (uuidStr.equals("#offline#")) { + // Try to lookup by name + if (name != null && !name.isEmpty()) { + PlayerData actor = plugin.getPlayerStorage().retrieve(name, false); + if (actor != null) return actor; + } + return plugin.getPlayerStorage().getConsole(); + } + + if (uuidStr.length() != 36) { + plugin.getLogger().warning("Invalid operator UUID '" + uuidStr + "', attempting name lookup"); + if (name != null && !name.isEmpty()) { + PlayerData actor = plugin.getPlayerStorage().retrieve(name, false); + if (actor != null) return actor; + } + return plugin.getPlayerStorage().getConsole(); + } + + try { + UUID uuid = UUID.fromString(uuidStr); + // Use the UUID to create or get the actor, with the name as a fallback identifier + PlayerData actor = plugin.getPlayerStorage().createIfNotExists(uuid, name != null ? name : "Unknown"); + if (actor != null) return actor; + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Could not parse UUID '" + uuidStr + "'"); + } catch (SQLException e) { + plugin.getLogger().warning("Failed to create/get actor with UUID '" + uuidStr + "'"); + } + + return plugin.getPlayerStorage().getConsole(); + } + + /** + * Truncate reason to 256 characters (BanManager limit). + */ + private String truncateReason(String reason) { + if (reason == null) return ""; + if (reason.length() > 256) { + plugin.getLogger().info("Truncated reason from " + reason.length() + " to 256 chars"); + return reason.substring(0, 256); + } + return reason; + } + + /** + * Parse IP address from string, handling LiteBans special values. + */ + private IPAddress parseIp(String ipString) { + if (ipString == null || ipString.equals("#offline#") || ipString.equals("#") || ipString.equals("#undefined#")) { + return null; + } + + try { + IPAddressString ipAddrStr = new IPAddressString(ipString); + if (ipAddrStr.isValid()) { + return ipAddrStr.getAddress(); + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Get IP range from wildcard string. + */ + private IPAddressSeqRange getIpRange(String ipString) { + if (ipString == null) return null; + + try { + IPAddressString ipAddrStr = new IPAddressString(ipString); + if (ipAddrStr.isSequential()) { + return ipAddrStr.getSequentialRange(); + } + return null; + } catch (Exception e) { + return null; + } + } + + /** + * Convert LiteBans expiry to BanManager format. + * LiteBans uses milliseconds, -1 or 0 for permanent. + * BanManager uses seconds, 0 for permanent. + */ + private long convertExpiry(long liteBansUntil) { + if (liteBansUntil == -1L || liteBansUntil == 0L) { + return 0L; // Permanent + } + return liteBansUntil / 1000L; + } + + @Override + public void importPlayerBans() { + plugin.getLogger().info("Importing player bans from " + tablePrefix + "bans..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for player bans import"); + return; + } + + // Only select player bans (ipban = 0) + String sql = "SELECT `id`, `uuid`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`removed_by_uuid`, `removed_by_name`, `time`, `until`, " + + "`silent`, `active` FROM `" + tablePrefix + "bans` WHERE `ipban` = 0"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans bans table for player bans"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + processPlayerBanRow(results); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + private void processPlayerBanRow(DatabaseResults results) throws SQLException { + int id = results.getInt(0); + String uuidStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + String removedByUuid = results.getString(5); + String removedByName = results.getString(6); + long time = results.getLong(7); + long until = results.getLong(8); + boolean silent = results.getBoolean(9); + boolean active = results.getBoolean(10); + + if (uuidStr == null || uuidStr.equals("#offline#")) { + plugin.getLogger().warning("Skipping player ban " + id + " with invalid UUID: " + uuidStr); + skipped++; + return; + } + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + if (active) { + try { + UUID uuid = UUID.fromString(uuidStr); + + if (!plugin.getPlayerBanStorage().isBanned(uuid)) { + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + PlayerBanData data = new PlayerBanData(player, actor, reason, silent, expires, created); + plugin.getPlayerBanStorage().ban(data); + importedBans++; + } + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Skipping player ban " + id + " - invalid UUID: " + uuidStr); + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import player ban " + id); + } + } else { + PlayerData removedByActor = resolveActor(removedByUuid, removedByName); + + try { + UUID uuid = UUID.fromString(uuidStr); + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + + PlayerBanRecord existing = plugin.getPlayerBanRecordStorage().queryBuilder() + .where().eq("player_id", player) + .and().eq("pastCreated", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + return; + } + + PlayerBanData tempData = new PlayerBanData(player, actor, reason, silent, expires, created); + PlayerBanRecord record = new PlayerBanRecord(tempData, removedByActor, "Imported from LiteBans"); + plugin.getPlayerBanRecordStorage().create(record); + importedBanRecords++; + } catch (IllegalArgumentException e) { + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import player ban record " + id + ": " + e.getMessage()); + e.printStackTrace(); + } + } + } + + @Override + public void importIpBans() { + plugin.getLogger().info("Importing IP bans from " + tablePrefix + "bans..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for IP bans import"); + return; + } + + // Only select IP bans (ipban = 1, ipban_wildcard = 0) + String sql = "SELECT `id`, `ip`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`removed_by_uuid`, `removed_by_name`, `time`, `until`, " + + "`silent`, `active` FROM `" + tablePrefix + "bans` WHERE `ipban` = 1 AND `ipban_wildcard` = 0"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans bans table for IP bans"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + processIpBanRow(results); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + private void processIpBanRow(DatabaseResults results) throws SQLException { + int id = results.getInt(0); + String ipStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + String removedByUuid = results.getString(5); + String removedByName = results.getString(6); + long time = results.getLong(7); + long until = results.getLong(8); + boolean silent = results.getBoolean(9); + boolean active = results.getBoolean(10); + + IPAddress ip = parseIp(ipStr); + if (ip == null) { + plugin.getLogger().warning("Skipping IP ban " + id + " - invalid IP: " + ipStr); + skipped++; + return; + } + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + if (active) { + try { + if (!plugin.getIpBanStorage().isBanned(ip)) { + IpBanData data = new IpBanData(ip, actor, reason, silent, expires, created); + plugin.getIpBanStorage().ban(data); + importedBans++; + } + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP ban " + id); + } + } else { + PlayerData removedByActor = resolveActor(removedByUuid, removedByName); + + try { + IpBanRecord existing = plugin.getIpBanRecordStorage().queryBuilder() + .where().eq("ip", ip) + .and().eq("pastCreated", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + return; + } + + IpBanData tempData = new IpBanData(ip, actor, reason, silent, expires, created); + IpBanRecord record = new IpBanRecord(tempData, removedByActor, "Imported from LiteBans"); + plugin.getIpBanRecordStorage().create(record); + importedBanRecords++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP ban record " + id); + } + } + } + + @Override + public void importIpRangeBans() { + plugin.getLogger().info("Importing IP range bans from " + tablePrefix + "bans..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for IP range bans import"); + return; + } + + // Only select IP range bans (ipban = 1, ipban_wildcard = 1) + String sql = "SELECT `id`, `ip`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`removed_by_uuid`, `removed_by_name`, `time`, `until`, " + + "`silent`, `active` FROM `" + tablePrefix + "bans` WHERE `ipban` = 1 AND `ipban_wildcard` = 1"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans bans table for IP range bans"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + processIpRangeBanRow(results); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + private void processIpRangeBanRow(DatabaseResults results) throws SQLException { + int id = results.getInt(0); + String ipStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + String removedByUuid = results.getString(5); + String removedByName = results.getString(6); + long time = results.getLong(7); + long until = results.getLong(8); + boolean silent = results.getBoolean(9); + boolean active = results.getBoolean(10); + + IPAddressSeqRange range = getIpRange(ipStr); + if (range == null) { + plugin.getLogger().warning("Skipping IP range ban " + id + " - could not parse range: " + ipStr); + skipped++; + return; + } + + IPAddress fromIp = range.getLower(); + IPAddress toIp = range.getUpper(); + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + if (active) { + try { + if (!plugin.getIpRangeBanStorage().isBanned(fromIp) && !plugin.getIpRangeBanStorage().isBanned(toIp)) { + IpRangeBanData data = new IpRangeBanData(fromIp, toIp, actor, reason, silent, expires, created); + plugin.getIpRangeBanStorage().ban(data); + importedBans++; + } + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP range ban " + id); + } + } else { + PlayerData removedByActor = resolveActor(removedByUuid, removedByName); + + try { + IpRangeBanRecord existing = plugin.getIpRangeBanRecordStorage().queryBuilder() + .where().eq("fromIp", fromIp) + .and().eq("toIp", toIp) + .and().eq("pastCreated", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + return; + } + + IpRangeBanData tempData = new IpRangeBanData(fromIp, toIp, actor, reason, silent, expires, created); + IpRangeBanRecord record = new IpRangeBanRecord(tempData, removedByActor, "Imported from LiteBans"); + plugin.getIpRangeBanRecordStorage().create(record); + importedBanRecords++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP range ban record " + id); + } + } + } + + @Override + public void importPlayerMutes() { + plugin.getLogger().info("Importing player mutes from " + tablePrefix + "mutes..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for player mutes import"); + return; + } + + // Only select player mutes (ipban = 0) + String sql = "SELECT `id`, `uuid`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`removed_by_uuid`, `removed_by_name`, `time`, `until`, " + + "`silent`, `active` FROM `" + tablePrefix + "mutes` WHERE `ipban` = 0"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans mutes table for player mutes"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + processPlayerMuteRow(results); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + private void processPlayerMuteRow(DatabaseResults results) throws SQLException { + int id = results.getInt(0); + String uuidStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + String removedByUuid = results.getString(5); + String removedByName = results.getString(6); + long time = results.getLong(7); + long until = results.getLong(8); + boolean silent = results.getBoolean(9); + boolean active = results.getBoolean(10); + + if (uuidStr == null || uuidStr.equals("#offline#")) { + plugin.getLogger().warning("Skipping player mute " + id + " with invalid UUID: " + uuidStr); + skipped++; + return; + } + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + if (active) { + try { + UUID uuid = UUID.fromString(uuidStr); + + if (!plugin.getPlayerMuteStorage().isMuted(uuid)) { + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + PlayerMuteData data = new PlayerMuteData(player, actor, reason, silent, false, expires, created); + plugin.getPlayerMuteStorage().mute(data); + importedMutes++; + } + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Skipping player mute " + id + " - invalid UUID: " + uuidStr); + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import player mute " + id); + } + } else { + PlayerData removedByActor = resolveActor(removedByUuid, removedByName); + + try { + UUID uuid = UUID.fromString(uuidStr); + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + + PlayerMuteRecord existing = plugin.getPlayerMuteRecordStorage().queryBuilder() + .where().eq("player_id", player) + .and().eq("pastCreated", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + return; + } + + PlayerMuteData tempData = new PlayerMuteData(player, actor, reason, silent, false, expires, created); + PlayerMuteRecord record = new PlayerMuteRecord(tempData, removedByActor, "Imported from LiteBans"); + plugin.getPlayerMuteRecordStorage().create(record); + importedMuteRecords++; + } catch (IllegalArgumentException e) { + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import player mute record " + id); + } + } + } + + /** + * Import IP mutes from litebans_mutes table. + * Note: Wildcard IP mutes are skipped as BanManager doesn't support them. + */ + private void importIpMutes() { + plugin.getLogger().info("Importing IP mutes from " + tablePrefix + "mutes..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for IP mutes import"); + return; + } + + // Only select IP mutes (ipban = 1, ipban_wildcard = 0) - wildcard IP mutes not supported + String sql = "SELECT `id`, `ip`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`removed_by_uuid`, `removed_by_name`, `time`, `until`, " + + "`silent`, `active` FROM `" + tablePrefix + "mutes` WHERE `ipban` = 1 AND `ipban_wildcard` = 0"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans mutes table for IP mutes"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + processIpMuteRow(results); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + private void processIpMuteRow(DatabaseResults results) throws SQLException { + int id = results.getInt(0); + String ipStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + String removedByUuid = results.getString(5); + String removedByName = results.getString(6); + long time = results.getLong(7); + long until = results.getLong(8); + boolean silent = results.getBoolean(9); + boolean active = results.getBoolean(10); + + IPAddress ip = parseIp(ipStr); + if (ip == null) { + plugin.getLogger().warning("Skipping IP mute " + id + " - invalid IP: " + ipStr); + skipped++; + return; + } + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + if (active) { + try { + if (!plugin.getIpMuteStorage().isMuted(ip)) { + IpMuteData data = new IpMuteData(ip, actor, reason, silent, false, expires, created); + plugin.getIpMuteStorage().mute(data); + importedMutes++; + } + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP mute " + id); + } + } else { + PlayerData removedByActor = resolveActor(removedByUuid, removedByName); + + try { + IpMuteRecord existing = plugin.getIpMuteRecordStorage().queryBuilder() + .where().eq("ip", ip) + .and().eq("pastCreated", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + return; + } + + IpMuteData tempData = new IpMuteData(ip, actor, reason, silent, false, expires, created); + IpMuteRecord record = new IpMuteRecord(tempData, removedByActor, "Imported from LiteBans"); + plugin.getIpMuteRecordStorage().create(record); + importedMuteRecords++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import IP mute record " + id); + } + } + } + + @Override + public void importPlayerWarnings() { + plugin.getLogger().info("Importing warnings from " + tablePrefix + "warnings..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for warnings import"); + return; + } + + String sql = "SELECT `id`, `uuid`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`time`, `until`, `warned` FROM `" + tablePrefix + "warnings`"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans warnings table"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + int id = results.getInt(0); + String uuidStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + long time = results.getLong(5); + long until = results.getLong(6); + boolean warned = results.getBoolean(7); + + if (uuidStr == null || uuidStr.equals("#offline#")) { + plugin.getLogger().warning("Skipping warning " + id + " with invalid UUID: " + uuidStr); + skipped++; + continue; + } + + try { + UUID uuid = UUID.fromString(uuidStr); + // Create or get player by UUID - use "Unknown" as name since LiteBans doesn't store warned player name + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + long expires = convertExpiry(until); + + // Check for duplicate: same player, actor, and created timestamp + PlayerWarnData existing = plugin.getPlayerWarnStorage().queryBuilder() + .where().eq("player_id", player) + .and().eq("actor_id", actor) + .and().eq("created", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + continue; + } + + // Map 'warned' to 'read' - if player was warned, they've seen it + PlayerWarnData data = new PlayerWarnData(player, actor, reason, warned, expires, created); + plugin.getPlayerWarnStorage().create(data); + importedWarnings++; + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Skipping warning " + id + " - invalid UUID: " + uuidStr); + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import warning " + id); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + /** + * Import kicks from litebans_kicks table. + */ + private void importKicks() { + plugin.getLogger().info("Importing kicks from " + tablePrefix + "kicks..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for kicks import"); + return; + } + + String sql = "SELECT `id`, `uuid`, `reason`, `banned_by_uuid`, `banned_by_name`, " + + "`time` FROM `" + tablePrefix + "kicks`"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans kicks table"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + int id = results.getInt(0); + String uuidStr = results.getString(1); + String reason = truncateReason(results.getString(2)); + String bannedByUuid = results.getString(3); + String bannedByName = results.getString(4); + long time = results.getLong(5); + + if (uuidStr == null || uuidStr.equals("#offline#")) { + plugin.getLogger().warning("Skipping kick " + id + " with invalid UUID: " + uuidStr); + skipped++; + continue; + } + + try { + UUID uuid = UUID.fromString(uuidStr); + // Create or get player by UUID + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, "Unknown"); + + PlayerData actor = resolveActor(bannedByUuid, bannedByName); + long created = time / 1000L; + + // Check for duplicate: same player, actor, and created timestamp + PlayerKickData existing = plugin.getPlayerKickStorage().queryBuilder() + .where().eq("player_id", player) + .and().eq("actor_id", actor) + .and().eq("created", created) + .queryForFirst(); + + if (existing != null) { + skipped++; + continue; + } + + PlayerKickData data = new PlayerKickData(player, actor, reason, created); + plugin.getPlayerKickStorage().create(data); + importedKicks++; + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Skipping kick " + id + " - invalid UUID: " + uuidStr); + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import kick " + id); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + /** + * Import history from litebans_history table. + */ + private void importHistory() { + plugin.getLogger().info("Importing history from " + tablePrefix + "history..."); + + DatabaseConnection read; + try { + read = connection.getReadOnlyConnection(""); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to get database connection for history import"); + return; + } + + String sql = "SELECT `id`, `uuid`, `name`, `ip`, `date` FROM `" + tablePrefix + "history`"; + + DatabaseResults results; + try { + results = read.compileStatement(sql, StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false).runQuery(null); + } catch (SQLException e) { + e.printStackTrace(); + plugin.getLogger().severe("Failed to query LiteBans history table"); + read.closeQuietly(); + return; + } + + try { + while (results.next()) { + int id = results.getInt(0); + String uuidStr = results.getString(1); + String name = results.getString(2); + String ipStr = results.getString(3); + Timestamp date = results.getTimestamp(4); + + if (uuidStr == null || uuidStr.equals("#offline#")) { + skipped++; + continue; + } + + try { + UUID uuid = UUID.fromString(uuidStr); + // Create or get player by UUID and name from history + PlayerData player = plugin.getPlayerStorage().createIfNotExists(uuid, name != null ? name : "Unknown"); + + // Parse IP if valid + IPAddress ip = null; + if (ipStr != null && !ipStr.equals("#") && !ipStr.equals("#offline#") && !ipStr.equals("#undefined#")) { + ip = parseIp(ipStr); + } + + long timestamp = date != null ? date.getTime() / 1000L : System.currentTimeMillis() / 1000L; + + // Check for duplicate: same player and join timestamp + PlayerHistoryData existing = plugin.getPlayerHistoryStorage().queryBuilder() + .where().eq("player_id", player) + .and().eq("join", timestamp) + .queryForFirst(); + + if (existing != null) { + skipped++; + continue; + } + + // Create history entry with the parsed IP address and timestamps + PlayerHistoryData data = new PlayerHistoryData(player, ip, timestamp, timestamp); + + plugin.getPlayerHistoryStorage().create(data); + importedHistory++; + } catch (IllegalArgumentException e) { + skipped++; + } catch (SQLException e) { + plugin.getLogger().severe("Failed to import history " + id); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + results.closeQuietly(); + read.closeQuietly(); + } + } + + /** + * Config class for LiteBans database connection. + */ + static class LiteBansConfig extends DatabaseConfig { + public LiteBansConfig(String storageType, String host, int port, String name, String user, String password, + boolean useSSL, boolean verifyServerCertificate, boolean allowPublicKeyRetrieval, + boolean isEnabled, int maxConnections, int leakDetection, int maxLifetime, + int connectionTimeout, HashMap> tables, File dataFolder) { + super(storageType, host, port, name, user, password, useSSL, verifyServerCertificate, allowPublicKeyRetrieval, + isEnabled, maxConnections, leakDetection, maxLifetime, connectionTimeout, tables, dataFolder); + } + } +} diff --git a/common/src/main/resources/messages.yml b/common/src/main/resources/messages.yml index 5e0f824cd..2f34a7498 100644 --- a/common/src/main/resources/messages.yml +++ b/common/src/main/resources/messages.yml @@ -85,6 +85,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java new file mode 100644 index 000000000..de767113b --- /dev/null +++ b/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java @@ -0,0 +1,388 @@ +package me.confuser.banmanager.common.storage.conversion; + +import me.confuser.banmanager.common.BasePluginDbTest; +import me.confuser.banmanager.common.data.*; +import me.confuser.banmanager.common.ipaddr.IPAddress; +import me.confuser.banmanager.common.ipaddr.IPAddressString; +import me.confuser.banmanager.common.ormlite.dao.CloseableIterator; +import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.UUID; + +import static org.junit.Assert.*; + +/** + * Tests for the LiteBans to BanManager import functionality. + * Creates a mock LiteBans database schema and populates it with test data + * to verify the import process works correctly. + */ +public class LiteBansImportTest extends BasePluginDbTest { + + private static final String TABLE_PREFIX = "litebans_"; + + // Test UUIDs + private static final String PLAYER_UUID_1 = "550e8400-e29b-41d4-a716-446655440001"; + private static final String PLAYER_UUID_2 = "550e8400-e29b-41d4-a716-446655440002"; + private static final String PLAYER_UUID_3 = "550e8400-e29b-41d4-a716-446655440003"; + private static final String ACTOR_UUID = "550e8400-e29b-41d4-a716-446655440099"; + + @Before + public void setupLiteBansTables() throws SQLException, IOException { + createLiteBansSchema(); + insertTestData(); + } + + /** + * Creates the LiteBans table schema in the test database. + */ + private void createLiteBansSchema() throws SQLException, IOException { + try (DatabaseConnection conn = plugin.getLocalConn().getReadWriteConnection("")) { + // Create bans table + conn.executeStatement( + "CREATE TABLE IF NOT EXISTS " + TABLE_PREFIX + "bans (" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "uuid VARCHAR(36)," + + "ip VARCHAR(45)," + + "reason VARCHAR(2048)," + + "banned_by_uuid VARCHAR(36)," + + "banned_by_name VARCHAR(128)," + + "removed_by_uuid VARCHAR(36)," + + "removed_by_name VARCHAR(128)," + + "removed_by_date TIMESTAMP NULL," + + "time BIGINT," + + "until BIGINT," + + "server_scope VARCHAR(32)," + + "server_origin VARCHAR(32)," + + "silent BIT DEFAULT 0," + + "ipban BIT DEFAULT 0," + + "ipban_wildcard BIT DEFAULT 0," + + "active BIT DEFAULT 1" + + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Create mutes table + conn.executeStatement( + "CREATE TABLE IF NOT EXISTS " + TABLE_PREFIX + "mutes (" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "uuid VARCHAR(36)," + + "ip VARCHAR(45)," + + "reason VARCHAR(2048)," + + "banned_by_uuid VARCHAR(36)," + + "banned_by_name VARCHAR(128)," + + "removed_by_uuid VARCHAR(36)," + + "removed_by_name VARCHAR(128)," + + "removed_by_date TIMESTAMP NULL," + + "time BIGINT," + + "until BIGINT," + + "server_scope VARCHAR(32)," + + "server_origin VARCHAR(32)," + + "silent BIT DEFAULT 0," + + "ipban BIT DEFAULT 0," + + "ipban_wildcard BIT DEFAULT 0," + + "active BIT DEFAULT 1" + + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Create warnings table + conn.executeStatement( + "CREATE TABLE IF NOT EXISTS " + TABLE_PREFIX + "warnings (" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "uuid VARCHAR(36)," + + "ip VARCHAR(45)," + + "reason VARCHAR(2048)," + + "banned_by_uuid VARCHAR(36)," + + "banned_by_name VARCHAR(128)," + + "removed_by_uuid VARCHAR(36)," + + "removed_by_name VARCHAR(128)," + + "removed_by_date TIMESTAMP NULL," + + "time BIGINT," + + "until BIGINT," + + "server_scope VARCHAR(32)," + + "server_origin VARCHAR(32)," + + "warned BIT DEFAULT 0," + + "active BIT DEFAULT 1" + + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Create kicks table + conn.executeStatement( + "CREATE TABLE IF NOT EXISTS " + TABLE_PREFIX + "kicks (" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "uuid VARCHAR(36)," + + "ip VARCHAR(45)," + + "reason VARCHAR(2048)," + + "banned_by_uuid VARCHAR(36)," + + "banned_by_name VARCHAR(128)," + + "time BIGINT," + + "until BIGINT," + + "server_scope VARCHAR(32)," + + "server_origin VARCHAR(32)," + + "silent BIT DEFAULT 0" + + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Create history table + conn.executeStatement( + "CREATE TABLE IF NOT EXISTS " + TABLE_PREFIX + "history (" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "uuid VARCHAR(36)," + + "name VARCHAR(16)," + + "ip VARCHAR(45)," + + "date TIMESTAMP" + + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + } + } + + /** + * Inserts test data into the LiteBans tables. + */ + private void insertTestData() throws SQLException, IOException { + long currentTime = System.currentTimeMillis(); + long pastTime = currentTime - 86400000L; // 1 day ago + + try (DatabaseConnection conn = plugin.getLocalConn().getReadWriteConnection("")) { + // Active player ban + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "bans " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_1 + "', '192.168.1.1', 'Test ban reason', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ", 0, 0, 0, 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Active IP ban + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "bans " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_2 + "', '10.0.0.1', 'IP ban reason', 'CONSOLE', 'Console', " + pastTime + ", 0, 1, 1, 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Active wildcard IP ban (IP range) + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "bans " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_3 + "', '172.16.0.0/16', 'Range ban reason', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ", 0, 0, 1, 1, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Inactive (removed) player ban - should create a ban record + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "bans " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, removed_by_uuid, removed_by_name, removed_by_date, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('550e8400-e29b-41d4-a716-446655440004', '192.168.1.4', 'Old ban reason', '" + ACTOR_UUID + "', 'TestActor', 'CONSOLE', 'Console', NOW(), " + pastTime + ", 0, 0, 0, 0, 0)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Active player mute + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "mutes " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_1 + "', '192.168.1.1', 'Test mute reason', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ", 0, 0, 0, 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Active IP mute + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "mutes " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_2 + "', '10.0.0.2', 'IP mute reason', 'CONSOLE', 'Console', " + pastTime + ", 0, 0, 1, 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Wildcard IP mute - should be skipped (unsupported) + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "mutes " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('" + PLAYER_UUID_3 + "', '192.168.0.0/24', 'Wildcard mute - should skip', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ", 0, 0, 1, 1, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Warning - player was notified (warned=1) + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "warnings " + + "(uuid, reason, banned_by_uuid, banned_by_name, time, until, warned) VALUES " + + "('" + PLAYER_UUID_1 + "', 'Test warning', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ", 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Warning - player was NOT notified (warned=0) + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "warnings " + + "(uuid, reason, banned_by_uuid, banned_by_name, time, until, warned) VALUES " + + "('" + PLAYER_UUID_2 + "', 'Unread warning', 'CONSOLE', 'Console', " + pastTime + ", 0, 0)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Kick + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "kicks " + + "(uuid, reason, banned_by_uuid, banned_by_name, time) VALUES " + + "('" + PLAYER_UUID_1 + "', 'Test kick', '" + ACTOR_UUID + "', 'TestActor', " + pastTime + ")", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // History entry + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "history " + + "(uuid, name, ip, date) VALUES " + + "('" + PLAYER_UUID_1 + "', 'TestPlayer1', '192.168.1.1', NOW())", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + + // Entry with invalid UUID (should be skipped) + conn.executeStatement( + "INSERT INTO " + TABLE_PREFIX + "bans " + + "(uuid, ip, reason, banned_by_uuid, banned_by_name, time, until, silent, ipban, ipban_wildcard, active) VALUES " + + "('#offline#', '192.168.1.99', 'Invalid player', 'CONSOLE', 'Console', " + pastTime + ", 0, 0, 0, 0, 1)", + DatabaseConnection.DEFAULT_RESULT_FLAGS + ); + } + } + + @Test + public void shouldImportActivePlayerBan() throws SQLException { + runImport(); + + UUID playerUuid = UUID.fromString(PLAYER_UUID_1); + assertTrue("Player should be banned", plugin.getPlayerBanStorage().isBanned(playerUuid)); + + PlayerBanData ban = plugin.getPlayerBanStorage().getBan(playerUuid); + assertNotNull("Ban data should exist", ban); + assertEquals("Test ban reason", ban.getReason()); + assertFalse("Ban should not be silent", ban.isSilent()); + } + + @Test + public void shouldImportActiveIpBan() { + runImport(); + + // Check that IP ban was imported + // The IP 10.0.0.1 should be banned + IPAddress ip = new IPAddressString("10.0.0.1").getAddress(); + assertTrue("IP should be banned", plugin.getIpBanStorage().isBanned(ip)); + } + + @Test + public void shouldImportActiveIpRangeBan() { + runImport(); + + // Check that an IP within the range 172.16.0.0/16 is banned + IPAddress ip = new IPAddressString("172.16.1.1").getAddress(); + assertTrue("IP in range should be banned", plugin.getIpRangeBanStorage().isBanned(ip)); + } + + @Test + public void shouldImportActivePlayerMute() throws SQLException { + runImport(); + + UUID playerUuid = UUID.fromString(PLAYER_UUID_1); + assertTrue("Player should be muted", plugin.getPlayerMuteStorage().isMuted(playerUuid)); + + PlayerMuteData mute = plugin.getPlayerMuteStorage().getMute(playerUuid); + assertNotNull("Mute data should exist", mute); + assertEquals("Test mute reason", mute.getReason()); + } + + @Test + public void shouldImportActiveIpMute() { + runImport(); + + IPAddress ip = new IPAddressString("10.0.0.2").getAddress(); + assertTrue("IP should be muted", plugin.getIpMuteStorage().isMuted(ip)); + } + + @Test + public void shouldImportWarningWithCorrectReadStatus() throws SQLException, IOException { + runImport(); + + // Check warnings for PLAYER_UUID_1 - should have warned=true (read=true) + UUID playerUuid1 = UUID.fromString(PLAYER_UUID_1); + PlayerData player = plugin.getPlayerStorage().createIfNotExists(playerUuid1, "Unknown"); + + try (CloseableIterator warnings = plugin.getPlayerWarnStorage().getWarnings(player)) { + boolean foundReadWarning = false; + while (warnings.hasNext()) { + PlayerWarnData warn = warnings.next(); + if (warn.getReason().equals("Test warning")) { + assertTrue("Warning should be marked as read", warn.isRead()); + foundReadWarning = true; + } + } + assertTrue("Should have found the read warning", foundReadWarning); + } + } + + @Test + public void shouldImportKick() throws SQLException { + runImport(); + + UUID playerUuid = UUID.fromString(PLAYER_UUID_1); + PlayerData player = plugin.getPlayerStorage().createIfNotExists(playerUuid, "Unknown"); + + // Verify at least one kick was imported for this player + long kickCount = plugin.getPlayerKickStorage().getCount(player); + assertTrue("Should have at least one kick", kickCount > 0); + } + + @Test + public void shouldSkipInvalidUuids() { + // The entry with #offline# UUID should be skipped without error + runImport(); + + // Verify import completed successfully by checking valid entries exist + assertTrue("Valid player ban should exist", + plugin.getPlayerBanStorage().isBanned(UUID.fromString(PLAYER_UUID_1))); + } + + @Test + public void shouldHandleConsoleAsActor() { + runImport(); + + // The IP ban was created by CONSOLE, verify it imported correctly + IPAddress ip = new IPAddressString("10.0.0.1").getAddress(); + assertTrue("IP ban by console should exist", plugin.getIpBanStorage().isBanned(ip)); + + IpBanData ban = plugin.getIpBanStorage().getBan(ip); + assertNotNull("Ban should exist", ban); + assertEquals("Actor should be console", + plugin.getPlayerStorage().getConsole().getUUID(), + ban.getActor().getUUID()); + } + + @Test + public void shouldCreateBanRecordForRemovedBan() throws SQLException { + runImport(); + + // The inactive ban for UUID 550e8400-e29b-41d4-a716-446655440004 should create a record + UUID removedPlayerUuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440004"); + + // Player should NOT be actively banned (it was removed) + assertFalse("Player should not be actively banned", + plugin.getPlayerBanStorage().isBanned(removedPlayerUuid)); + + // But there should be a ban record + PlayerData player = plugin.getPlayerStorage().createIfNotExists(removedPlayerUuid, "Unknown"); + long recordCount = plugin.getPlayerBanRecordStorage().getCount(player); + assertTrue("Should have a ban record", recordCount > 0); + } + + /** + * Run the LiteBans import using the plugin's existing connection. + * This allows tests to work with H2 in-memory databases where + * separate connections don't share tables. + */ + private void runImport() { + // Use the test constructor that accepts an existing connection + new LiteBans(plugin, plugin.getLocalConn(), TABLE_PREFIX); + } +} diff --git a/e2e/platforms/bukkit/configs/messages.yml b/e2e/platforms/bukkit/configs/messages.yml index 949f98466..09382e935 100644 --- a/e2e/platforms/bukkit/configs/messages.yml +++ b/e2e/platforms/bukkit/configs/messages.yml @@ -79,6 +79,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: diff --git a/e2e/platforms/bungee/configs/banmanager/messages.yml b/e2e/platforms/bungee/configs/banmanager/messages.yml index df95a89be..50203aa0a 100644 --- a/e2e/platforms/bungee/configs/banmanager/messages.yml +++ b/e2e/platforms/bungee/configs/banmanager/messages.yml @@ -76,6 +76,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: invalidIndex: '&cInvalid player option used' diff --git a/e2e/platforms/fabric/configs/messages.yml b/e2e/platforms/fabric/configs/messages.yml index 949f98466..09382e935 100644 --- a/e2e/platforms/fabric/configs/messages.yml +++ b/e2e/platforms/fabric/configs/messages.yml @@ -79,6 +79,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: diff --git a/e2e/platforms/sponge/configs/banmanager/messages.yml b/e2e/platforms/sponge/configs/banmanager/messages.yml index 7eb5bbf8c..cd7f4290c 100644 --- a/e2e/platforms/sponge/configs/banmanager/messages.yml +++ b/e2e/platforms/sponge/configs/banmanager/messages.yml @@ -76,6 +76,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: invalidIndex: '&cInvalid player option used' diff --git a/e2e/platforms/sponge7/configs/banmanager/messages.yml b/e2e/platforms/sponge7/configs/banmanager/messages.yml index 7eb5bbf8c..cd7f4290c 100644 --- a/e2e/platforms/sponge7/configs/banmanager/messages.yml +++ b/e2e/platforms/sponge7/configs/banmanager/messages.yml @@ -76,6 +76,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: invalidIndex: '&cInvalid player option used' diff --git a/e2e/platforms/velocity/configs/banmanager/messages.yml b/e2e/platforms/velocity/configs/banmanager/messages.yml index df95a89be..50203aa0a 100644 --- a/e2e/platforms/velocity/configs/banmanager/messages.yml +++ b/e2e/platforms/velocity/configs/banmanager/messages.yml @@ -76,6 +76,9 @@ messages: h2: started: '&aH2 import started' finished: '&aH2 import finished, please restart the server' + litebans: + started: '&aLiteBans import started' + finished: '&aLiteBans import finished' info: error: invalidIndex: '&cInvalid player option used'