diff --git a/README.md b/README.md index ff0b7f4dd..5f82e5241 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ McStats: http://mcstats.org/plugin/AuthMe #####Running Requirements: >- Java 1.7 (should work also with Java 1.8) ->- Spigot or CraftBukkit (1.7.10 or 1.8.X) +>- Spigot or CraftBukkit (1.7.10, 1.8.X or 1.9-pre1) >- ProtocolLib (optional, required by the protectInventory feature)
diff --git a/pom.xml b/pom.xml index e5d70cd19..82dfd0b40 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 1.7 - 1.8.8-R0.1-SNAPSHOT + 1.9-pre1-SNAPSHOT @@ -305,10 +305,10 @@ https://hub.spigotmc.org/nexus/content/repositories/snapshots - + ess-repo - http://ci.drtshock.net/plugin/repository/everything + http://repo.ess3.net/content/groups/essentials @@ -376,6 +376,19 @@ compile true + + + org.xerial + sqlite-jdbc + 3.8.11.2 + test + + + com.h2database + h2 + 1.4.191 + test + @@ -651,16 +664,20 @@ true - + net.ess3 - EssentialsX - 2.0.1-SNAPSHOT + Essentials + 2.13-SNAPSHOT provided - org.spigotmc - spigot-api + org.bukkit + bukkit + + + org.bukkit + craftbukkit true diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 56580cf91..622ceafe4 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -64,7 +64,6 @@ import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Server; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -76,6 +75,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -104,15 +104,14 @@ public class AuthMe extends JavaPlugin { // Private Instances private static AuthMe plugin; private static Server server; - private Management management; - private CommandHandler commandHandler = null; - private PermissionsManager permsMan = null; - private NewSetting newSettings; - private Messages messages; - private JsonCache playerBackup; - private PasswordSecurity passwordSecurity; - private DataSource database; - + /* + * Maps and stuff + * TODO: Clean up and Move into a manager + */ + public final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); + public final ConcurrentHashMap captcha = new ConcurrentHashMap<>(); + public final ConcurrentHashMap cap = new ConcurrentHashMap<>(); + public final ConcurrentHashMap realIp = new ConcurrentHashMap<>(); /* * Public Instances * TODO #432: Encapsulation @@ -122,7 +121,6 @@ public class AuthMe extends JavaPlugin { public DataManager dataManager; public OtherAccounts otherAccounts; public Location essentialsSpawn; - /* * Plugin Hooks * TODO: Move into modules @@ -133,15 +131,14 @@ public class AuthMe extends JavaPlugin { public AuthMeInventoryPacketAdapter inventoryProtector; public AuthMeTabCompletePacketAdapter tabComplete; public AuthMeTablistPacketAdapter tablistHider; - - /* - * Maps and stuff - * TODO: Clean up and Move into a manager - */ - public final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); - public final ConcurrentHashMap captcha = new ConcurrentHashMap<>(); - public final ConcurrentHashMap cap = new ConcurrentHashMap<>(); - public final ConcurrentHashMap realIp = new ConcurrentHashMap<>(); + private Management management; + private CommandHandler commandHandler = null; + private PermissionsManager permsMan = null; + private NewSetting newSettings; + private Messages messages; + private JsonCache playerBackup; + private PasswordSecurity passwordSecurity; + private DataSource database; /** * Get the plugin's instance. @@ -494,11 +491,41 @@ public class AuthMe extends JavaPlugin { if (newSettings != null) { new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP); } + new Thread(new Runnable() { + @Override + public void run() { + List pendingTasks = new ArrayList<>(); + for (BukkitTask pendingTask : getServer().getScheduler().getPendingTasks()) { + if (pendingTask.getOwner().equals(plugin) && !pendingTask.isSync()) { + pendingTasks.add(pendingTask.getTaskId()); + } + } + ConsoleLogger.info("Waiting for " + pendingTasks.size() + " tasks to finish"); + int progress = 0; + for (int taskId : pendingTasks) { + int maxTries = 5; + while (getServer().getScheduler().isCurrentlyRunning(taskId)) { + if (maxTries <= 0) { + ConsoleLogger.info("Async task " + taskId + " times out after to many tries"); + break; + } + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } + maxTries--; + } + + progress++; + ConsoleLogger.info("Progress: " + progress + " / " + pendingTasks.size()); + } + if (database != null) { + database.close(); + } + } + }, "AuthMe-DataSource#close").start(); // Close the database - if (database != null) { - database.close(); - } // Disabled correctly ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); @@ -518,6 +545,7 @@ public class AuthMe extends JavaPlugin { * Sets up the data source. * * @param settings The settings instance + * * @see AuthMe#database */ public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException { @@ -653,6 +681,7 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); Settings.protectInventoryBeforeLogInEnabled = false; newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false); + newSettings.save(); } return; } @@ -664,13 +693,19 @@ public class AuthMe extends JavaPlugin { inventoryProtector.unregister(); inventoryProtector = null; } - if (tabComplete == null && newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN)) { + if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) { tabComplete = new AuthMeTabCompletePacketAdapter(this); tabComplete.register(); + } else if (tabComplete != null) { + tabComplete.unregister(); + tabComplete = null; } - if (tablistHider == null && newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) { + if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) { tablistHider = new AuthMeTablistPacketAdapter(this); tablistHider.register(); + } else if (tablistHider != null) { + tablistHider.unregister(); + tablistHider = null; } } @@ -737,61 +772,9 @@ public class AuthMe extends JavaPlugin { } // Return the spawn location of a player + @Deprecated public Location getSpawnLocation(Player player) { - World world = player.getWorld(); - String[] spawnPriority = Settings.spawnPriority.split(","); - Location spawnLoc = world.getSpawnLocation(); - for (int i = spawnPriority.length - 1; i >= 0; i--) { - String s = spawnPriority[i]; - if (s.equalsIgnoreCase("default") && getDefaultSpawn(world) != null) - spawnLoc = getDefaultSpawn(world); - if (s.equalsIgnoreCase("multiverse") && getMultiverseSpawn(world) != null) - spawnLoc = getMultiverseSpawn(world); - if (s.equalsIgnoreCase("essentials") && getEssentialsSpawn() != null) - spawnLoc = getEssentialsSpawn(); - if (s.equalsIgnoreCase("authme") && getAuthMeSpawn(player) != null) - spawnLoc = getAuthMeSpawn(player); - } - if (spawnLoc == null) { - spawnLoc = world.getSpawnLocation(); - } - return spawnLoc; - } - - // Return the default spawn point of a world - private Location getDefaultSpawn(World world) { - return world.getSpawnLocation(); - } - - // Return the multiverse spawn point of a world - private Location getMultiverseSpawn(World world) { - if (multiverse != null && Settings.multiverse) { - try { - return multiverse.getMVWorldManager().getMVWorld(world).getSpawnLocation(); - } catch (Exception e) { - e.printStackTrace(); - } - } - return null; - } - - // Return the essentials spawn point - private Location getEssentialsSpawn() { - if (essentialsSpawn != null) { - return essentialsSpawn; - } - return null; - } - - // Return the AuthMe spawn point - private Location getAuthMeSpawn(Player player) { - if ((!database.isAuthAvailable(player.getName().toLowerCase()) || !player.hasPlayedBefore()) - && (Spawn.getInstance().getFirstSpawn() != null)) { - return Spawn.getInstance().getFirstSpawn(); - } else if (Spawn.getInstance().getSpawn() != null) { - return Spawn.getInstance().getSpawn(); - } - return player.getWorld().getSpawnLocation(); + return Spawn.getInstance().getSpawnLocation(player); } private void scheduleRecallEmailTask() { diff --git a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java index 41672c0c9..c2bb4f704 100644 --- a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java @@ -1,12 +1,11 @@ package fr.xephi.authme.cache.auth; +import fr.xephi.authme.security.crypts.HashedPassword; +import org.bukkit.Location; + import static com.google.common.base.Objects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; -import org.bukkit.Location; - -import fr.xephi.authme.security.crypts.HashedPassword; - /** */ @@ -85,20 +84,6 @@ public class PlayerAuth { this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName); } - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param salt String - * @param ip String - * @param lastLogin long - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, String realName) { - this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName); - } - /** * Constructor for PlayerAuth. * @@ -118,44 +103,6 @@ public class PlayerAuth { this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName); } - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param salt String - * @param ip String - * @param lastLogin long - * @param x double - * @param y double - * @param z double - * @param world String - * @param email String - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, double x, double y, - double z, String world, String email, String realName) { - this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin, - x, y, z, world, email, realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param salt String - * @param groupId int - * @param ip String - * @param lastLogin long - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip, - long lastLogin, String realName) { - this(nickname, new HashedPassword(hash, salt), groupId, ip, lastLogin, - 0, 0, 0, "world", "your@email.com", realName); - } - /** * Constructor for PlayerAuth. * @@ -171,8 +118,8 @@ public class PlayerAuth { * @param email String * @param realName String */ - public PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, - double x, double y, double z, String world, String email, String realName) { + private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, + double x, double y, double z, String world, String email, String realName) { this.nickname = nickname.toLowerCase(); this.password = password; this.ip = ip; diff --git a/src/main/java/fr/xephi/authme/cache/backup/DataFileCache.java b/src/main/java/fr/xephi/authme/cache/backup/DataFileCache.java deleted file mode 100644 index bf5e7389b..000000000 --- a/src/main/java/fr/xephi/authme/cache/backup/DataFileCache.java +++ /dev/null @@ -1,38 +0,0 @@ -package fr.xephi.authme.cache.backup; - -/** - */ -public class DataFileCache { - - private final String group; - private final boolean operator; - - /** - * Constructor for DataFileCache. - * - * @param group String - * @param operator boolean - */ - public DataFileCache(String group, boolean operator) { - this.group = group; - this.operator = operator; - } - - /** - * Method getGroup. - * - * @return String - */ - public String getGroup() { - return group; - } - - /** - * Method getOperator. - * - * @return boolean - */ - public boolean getOperator() { - return operator; - } -} diff --git a/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java b/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java index 8e43241f3..177ccce56 100644 --- a/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java +++ b/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java @@ -2,7 +2,14 @@ package fr.xephi.authme.cache.backup; import com.google.common.base.Charsets; import com.google.common.io.Files; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.Settings; import org.bukkit.entity.Player; @@ -11,8 +18,6 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Type; -/** - */ public class JsonCache { private final Gson gson; @@ -24,31 +29,19 @@ public class JsonCache { ConsoleLogger.showError("Failed to create cache directory."); } gson = new GsonBuilder() - .registerTypeAdapter(DataFileCache.class, new PlayerDataSerializer()) - .registerTypeAdapter(DataFileCache.class, new PlayerDataDeserializer()) + .registerTypeAdapter(PlayerData.class, new PlayerDataSerializer()) + .registerTypeAdapter(PlayerData.class, new PlayerDataDeserializer()) .setPrettyPrinting() .create(); } - /** - * Method createCache. - * - * @param player Player - * @param playerData DataFileCache - */ - public void createCache(Player player, DataFileCache playerData) { + public void createCache(Player player, PlayerData playerData) { if (player == null) { return; } - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - - File file = new File(cacheDir, path + File.separator + "cache.json"); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); if (file.exists()) { return; } @@ -61,52 +54,29 @@ public class JsonCache { Files.touch(file); Files.write(data, file, Charsets.UTF_8); } catch (IOException e) { - e.printStackTrace(); + ConsoleLogger.writeStackTrace(e); } } - /** - * Method readCache. - * - * @param player Player - * - * @return DataFileCache - */ - public DataFileCache readCache(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - - File file = new File(cacheDir, path + File.separator + "cache.json"); + public PlayerData readCache(Player player) { + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); if (!file.exists()) { return null; } try { String str = Files.toString(file, Charsets.UTF_8); - return gson.fromJson(str, DataFileCache.class); - } catch (Exception e) { - e.printStackTrace(); + return gson.fromJson(str, PlayerData.class); + } catch (IOException e) { + ConsoleLogger.writeStackTrace(e); return null; } } - /** - * Method removeCache. - * - * @param player Player - */ public void removeCache(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - File file = new File(cacheDir, path); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name); if (file.exists()) { purgeDirectory(file); if (!file.delete()) { @@ -115,75 +85,47 @@ public class JsonCache { } } - /** - * Method doesCacheExist. - * - * @param player Player - * - * @return boolean - */ public boolean doesCacheExist(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - File file = new File(cacheDir, path + File.separator + "cache.json"); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); return file.exists(); } - /** - */ - private static class PlayerDataDeserializer implements JsonDeserializer { - /** - * Method deserialize. - * - * @param jsonElement JsonElement - * @param type Type - * @param jsonDeserializationContext JsonDeserializationContext - * - * @return DataFileCache * @throws JsonParseException * @see com.google.gson.JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext) - */ + private class PlayerDataDeserializer implements JsonDeserializer { @Override - public DataFileCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public PlayerData deserialize(JsonElement jsonElement, Type type, + JsonDeserializationContext context) { JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject == null) { return null; } - JsonElement e; String group = null; boolean operator = false; + boolean fly = false; + JsonElement e; if ((e = jsonObject.get("group")) != null) { group = e.getAsString(); } if ((e = jsonObject.get("operator")) != null) { operator = e.getAsBoolean(); } + if ((e = jsonObject.get("fly")) != null) { + fly = e.getAsBoolean(); + } - return new DataFileCache(group, operator); + return new PlayerData(group, operator, fly); } } - /** - */ - private class PlayerDataSerializer implements JsonSerializer { - /** - * Method serialize. - * - * @param dataFileCache DataFileCache - * @param type Type - * @param jsonSerializationContext JsonSerializationContext - * - * @return JsonElement - */ + private class PlayerDataSerializer implements JsonSerializer { @Override - public JsonElement serialize(DataFileCache dataFileCache, Type type, JsonSerializationContext jsonSerializationContext) { + public JsonElement serialize(PlayerData playerData, Type type, + JsonSerializationContext context) { JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("group", dataFileCache.getGroup()); - jsonObject.addProperty("operator", dataFileCache.getOperator()); - + jsonObject.addProperty("group", playerData.getGroup()); + jsonObject.addProperty("operator", playerData.getOperator()); + jsonObject.addProperty("fly", playerData.isFlyEnabled()); return jsonObject; } } diff --git a/src/main/java/fr/xephi/authme/cache/backup/PlayerData.java b/src/main/java/fr/xephi/authme/cache/backup/PlayerData.java new file mode 100644 index 000000000..a41b8aa95 --- /dev/null +++ b/src/main/java/fr/xephi/authme/cache/backup/PlayerData.java @@ -0,0 +1,26 @@ +package fr.xephi.authme.cache.backup; + +public class PlayerData { + + private final String group; + private final boolean operator; + private final boolean flyEnabled; + + public PlayerData(String group, boolean operator, boolean flyEnabled) { + this.group = group; + this.operator = operator; + this.flyEnabled = flyEnabled; + } + + public String getGroup() { + return group; + } + + public boolean getOperator() { + return operator; + } + + public boolean isFlyEnabled() { + return flyEnabled; + } +} diff --git a/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java b/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java index 083c962de..2679df641 100644 --- a/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java +++ b/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java @@ -1,9 +1,8 @@ package fr.xephi.authme.cache.limbo; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.cache.backup.DataFileCache; import fr.xephi.authme.cache.backup.JsonCache; +import fr.xephi.authme.cache.backup.PlayerData; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -19,7 +18,7 @@ public class LimboCache { private volatile static LimboCache singleton; public final ConcurrentHashMap cache; public final AuthMe plugin; - private final JsonCache playerData; + private final JsonCache jsonCache; /** * Constructor for LimboCache. @@ -29,7 +28,7 @@ public class LimboCache { private LimboCache(AuthMe plugin) { this.plugin = plugin; this.cache = new ConcurrentHashMap<>(); - this.playerData = new JsonCache(); + this.jsonCache = new JsonCache(); } /** @@ -52,44 +51,28 @@ public class LimboCache { public void addLimboPlayer(Player player) { String name = player.getName().toLowerCase(); Location loc = player.getLocation(); - boolean operator = false; + boolean operator = player.isOp(); + boolean flyEnabled = player.getAllowFlight(); String playerGroup = ""; + PermissionsManager permsMan = plugin.getPermissionsManager(); + if (permsMan.hasGroupSupport()) { + playerGroup = permsMan.getPrimaryGroup(player); + } - // Get the permissions manager, and make sure it's valid - PermissionsManager permsMan = this.plugin.getPermissionsManager(); - if (permsMan == null) - ConsoleLogger.showError("Unable to access permissions manager!"); - assert permsMan != null; - - if (playerData.doesCacheExist(player)) { - DataFileCache cache = playerData.readCache(player); + if (jsonCache.doesCacheExist(player)) { + PlayerData cache = jsonCache.readCache(player); if (cache != null) { playerGroup = cache.getGroup(); operator = cache.getOperator(); + flyEnabled = cache.isFlyEnabled(); } - } else { - operator = player.isOp(); - - // Check whether groups are supported - if (permsMan.hasGroupSupport()) - playerGroup = permsMan.getPrimaryGroup(player); } if (player.isDead()) { loc = plugin.getSpawnLocation(player); } - cache.put(name, new LimboPlayer(name, loc, operator, playerGroup)); - } - - /** - * Method addLimboPlayer. - * - * @param player Player - * @param group String - */ - public void addLimboPlayer(Player player, String group) { - cache.put(player.getName().toLowerCase(), new LimboPlayer(player.getName().toLowerCase(), group)); + cache.put(name, new LimboPlayer(name, loc, operator, playerGroup, flyEnabled)); } /** @@ -99,7 +82,11 @@ public class LimboCache { */ public void deleteLimboPlayer(String name) { checkNotNull(name); - cache.remove(name.toLowerCase()); + name = name.toLowerCase(); + if (cache.containsKey(name)) { + cache.get(name).clearTask(); + cache.remove(name); + } } /** diff --git a/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java b/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java index f61c62469..917a22bc1 100644 --- a/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java +++ b/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java @@ -8,37 +8,20 @@ import org.bukkit.scheduler.BukkitTask; public class LimboPlayer { private final String name; + private final boolean fly; private Location loc = null; private BukkitTask timeoutTaskId = null; private BukkitTask messageTaskId = null; private boolean operator = false; - private String group = ""; + private String group; - /** - * Constructor for LimboPlayer. - * - * @param name String - * @param loc Location - * @param operator boolean - * @param group String - */ - public LimboPlayer(String name, Location loc, - boolean operator, String group) { + public LimboPlayer(String name, Location loc, boolean operator, + String group, boolean fly) { this.name = name; this.loc = loc; this.operator = operator; this.group = group; - } - - /** - * Constructor for LimboPlayer. - * - * @param name String - * @param group String - */ - public LimboPlayer(String name, String group) { - this.name = name; - this.group = group; + this.fly = fly; } /** @@ -77,11 +60,10 @@ public class LimboPlayer { return group; } - /** - * Method getTimeoutTaskId. - * - * @return BukkitTask - */ + public boolean isFly() { + return fly; + } + public BukkitTask getTimeoutTaskId() { return timeoutTaskId; } @@ -121,7 +103,6 @@ public class LimboPlayer { /** * Method clearTask. - * */ public void clearTask() { if (messageTaskId != null) { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java index 3dce54e17..0c7adf2ff 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java @@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.settings.Spawn; import org.bukkit.command.CommandSender; import java.util.List; @@ -20,6 +21,7 @@ public class ReloadCommand implements ExecutableCommand { try { commandService.getSettings().reload(); commandService.reloadMessages(commandService.getSettings().getMessagesFile()); + Spawn.reload(); // TODO #432: We should not reload only certain plugin entities but actually reinitialize all elements, // i.e. here in the future we might not have setupDatabase() but Authme.onEnable(), maybe after // a call to some destructor method diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java index dc5cd4713..9ed65b0bc 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java @@ -1,14 +1,5 @@ package fr.xephi.authme.command.executable.authme; -import java.util.List; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; @@ -20,6 +11,14 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +import java.util.List; /** * Admin command to unregister a player. @@ -55,19 +54,20 @@ public class UnregisterAdminCommand implements ExecutableCommand { if (target != null && target.isOnline()) { Utils.teleportToSpawn(target); LimboCache.getInstance().addLimboPlayer(target); - int delay = Settings.getRegistrationTimeout * 20; + int timeOut = Settings.getRegistrationTimeout * 20; int interval = Settings.getWarnMessageInterval; BukkitScheduler scheduler = sender.getServer().getScheduler(); - if (delay != 0) { - BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), delay); + if (timeOut != 0) { + BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), timeOut); LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTaskId(id); } LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId( - scheduler.runTaskAsynchronously(plugin, - new MessageTask(plugin, playerNameLowerCase, commandService.retrieveMessage(MessageKey.REGISTER_MESSAGE), interval))); + scheduler.runTask( + plugin, new MessageTask(plugin, playerNameLowerCase, MessageKey.REGISTER_MESSAGE, interval) + ) + ); if (Settings.applyBlindEffect) { - target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, - Settings.getRegistrationTimeout * 20, 2)); + target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); } commandService.send(target, MessageKey.UNREGISTERED_SUCCESS); } diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 4d0d44365..ad02c99f2 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -4,14 +4,19 @@ import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.security.crypts.HashedPassword; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** @@ -20,6 +25,7 @@ public class CacheDataSource implements DataSource { private final DataSource source; private final LoadingCache> cachedAuths; + private final ListeningExecutorService executorService; /** * Constructor for CacheDataSource. @@ -27,25 +33,35 @@ public class CacheDataSource implements DataSource { * @param src DataSource */ public CacheDataSource(DataSource src) { - this.source = src; - this.cachedAuths = CacheBuilder.newBuilder() - .expireAfterWrite(8, TimeUnit.MINUTES) - .removalListener(new RemovalListener>() { + source = src; + executorService = MoreExecutors.listeningDecorator( + Executors.newCachedThreadPool(new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("AuthMe-CacheLoader") + .build()) + ); + cachedAuths = CacheBuilder.newBuilder() + .refreshAfterWrite(8, TimeUnit.MINUTES) + .build(new CacheLoader>() { @Override - public void onRemoval(RemovalNotification> removalNotification) { - String name = removalNotification.getKey(); - if (PlayerCache.getInstance().isAuthenticated(name)) { - cachedAuths.getUnchecked(name); - } + public Optional load(String key) { + return Optional.fromNullable(source.getAuth(key)); } - }) - .build( - new CacheLoader>() { - @Override - public Optional load(String key) { - return Optional.fromNullable(source.getAuth(key)); - } - }); + + @Override + public ListenableFuture> reload(final String key, Optional oldValue) { + return executorService.submit(new Callable>() { + @Override + public Optional call() { + return load(key); + } + }); + } + }); + } + + public LoadingCache> getCachedAuths() { + return cachedAuths; } @Override @@ -137,6 +153,13 @@ public class CacheDataSource implements DataSource { @Override public synchronized void close() { source.close(); + cachedAuths.invalidateAll(); + executorService.shutdown(); + try { + executorService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + ConsoleLogger.writeStackTrace(e); + } } @Override @@ -160,8 +183,8 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized List getAllAuthsByEmail(final String email) { - return source.getAllAuthsByEmail(email); + public synchronized int countAuthsByEmail(final String email) { + return source.countAuthsByEmail(email); } @Override @@ -201,12 +224,6 @@ public class CacheDataSource implements DataSource { return source.getAccountsRegistered(); } - @Override - public void updateName(final String oldOne, final String newOne) { // unused method - source.updateName(oldOne, newOne); - cachedAuths.invalidate(oldOne); - } - @Override public boolean updateRealName(String user, String realName) { boolean result = source.updateRealName(user, realName); diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 4009586c2..6e3bc09b4 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -101,12 +101,12 @@ public interface DataSource { List getAllAuthsByIp(String ip); /** - * Return all usernames associated with the given email address. + * Return the number of accounts associated with the given email address. * * @param email The email address to look up - * @return Users using the given email address + * @return Number of accounts using the given email address */ - List getAllAuthsByEmail(String email); + int countAuthsByEmail(String email); /** * Update the email of the PlayerAuth in the data source. @@ -169,14 +169,6 @@ public interface DataSource { */ int getAccountsRegistered(); - /** - * Method updateName. - * - * @param oldOne String - * @param newOne String - */ - void updateName(String oldOne, String newOne); - boolean updateRealName(String user, String realName); boolean updateIp(String user, String ip); diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index 9bab76e14..c52d8e738 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -487,25 +487,21 @@ public class FlatFile implements DataSource { } @Override - public List getAllAuthsByEmail(String email) { + public int countAuthsByEmail(String email) { BufferedReader br = null; - List countEmail = new ArrayList<>(); + int countEmail = 0; try { br = new BufferedReader(new FileReader(source)); String line; while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args.length > 8 && args[8].equals(email)) { - countEmail.add(args[0]); + ++countEmail; } } return countEmail; - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return new ArrayList<>(); } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); - return new ArrayList<>(); } finally { if (br != null) { try { @@ -514,6 +510,7 @@ public class FlatFile implements DataSource { } } } + return 0; } @Override @@ -602,14 +599,6 @@ public class FlatFile implements DataSource { return result; } - @Override - public void updateName(String oldOne, String newOne) { - PlayerAuth auth = this.getAuth(oldOne); - auth.setNickname(newOne); - this.saveAuth(auth); - this.removeAuth(oldOne); - } - @Override public boolean updateRealName(String user, String realName) { return false; diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index a429d1814..1ab37d5ec 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import fr.xephi.authme.AuthMe; @@ -22,7 +23,6 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.List; @@ -82,6 +82,20 @@ public class MySQL implements DataSource { } } + @VisibleForTesting + MySQL(NewSetting settings, HikariDataSource hikariDataSource) { + this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST); + this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT); + this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME); + this.password = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD); + this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); + this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); + this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS); + this.col = new Columns(settings); + this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); + ds = hikariDataSource; + } + private synchronized void setConnectionArguments() throws RuntimeException { ds = new HikariDataSource(); ds.setPoolName("AuthMeMYSQLPool"); @@ -131,7 +145,7 @@ public class MySQL implements DataSource { + col.REAL_NAME + " VARCHAR(255) NOT NULL," + col.PASSWORD + " VARCHAR(255) NOT NULL," + col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1'," - + col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp," + + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," @@ -182,9 +196,9 @@ public class MySQL implements DataSource { rs = md.getColumns(null, null, tableName, col.LAST_LOGIN); if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName - + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp;"); + + " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;"); } else { - migrateLastLoginColumnToTimestamp(con, rs); + migrateLastLoginColumnToBigInt(con, rs); } rs.close(); @@ -234,66 +248,72 @@ public class MySQL implements DataSource { @Override public synchronized boolean isAuthAvailable(String user) { - try (Connection con = getConnection()) { - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; + ResultSet rs = null; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, user.toLowerCase()); - ResultSet rs = pst.executeQuery(); + rs = pst.executeQuery(); return rs.next(); } catch (SQLException ex) { logSqlException(ex); + } finally { + close(rs); } return false; } @Override public HashedPassword getPassword(String user) { - try (Connection con = getConnection()) { - String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName - + " WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName + + " WHERE " + col.NAME + "=?;"; + ResultSet rs = null; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, user.toLowerCase()); - ResultSet rs = pst.executeQuery(); + rs = pst.executeQuery(); if (rs.next()) { return new HashedPassword(rs.getString(col.PASSWORD), !col.SALT.isEmpty() ? rs.getString(col.SALT) : null); } } catch (SQLException ex) { logSqlException(ex); + } finally { + close(rs); } return null; } @Override public synchronized PlayerAuth getAuth(String user) { - PlayerAuth pAuth; - try (Connection con = getConnection()) { - String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; + PlayerAuth auth; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, user.toLowerCase()); - ResultSet rs = pst.executeQuery(); - if (!rs.next()) { - return null; + int id; + try (ResultSet rs = pst.executeQuery()) { + if (!rs.next()) { + return null; + } + id = rs.getInt(col.ID); + auth = buildAuthFromResultSet(rs); } - int id = rs.getInt(col.ID); - pAuth = buildAuthFromResultSet(rs); - rs.close(); - pst.close(); if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { - pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;"); - pst.setInt(1, id); - rs = pst.executeQuery(); - if (rs.next()) { - Blob blob = rs.getBlob("data"); - byte[] bytes = blob.getBytes(1, (int) blob.length()); - pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + try (PreparedStatement pst2 = con.prepareStatement( + "SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) { + pst2.setInt(1, id); + try (ResultSet rs = pst2.executeQuery()) { + if (rs.next()) { + Blob blob = rs.getBlob("data"); + byte[] bytes = blob.getBytes(1, (int) blob.length()); + auth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + } + } } } + return auth; } catch (SQLException ex) { logSqlException(ex); - return null; } - return pAuth; + return null; } @Override @@ -314,7 +334,7 @@ public class MySQL implements DataSource { pst.setString(1, auth.getNickname()); pst.setString(2, auth.getPassword().getHash()); pst.setString(3, auth.getIp()); - pst.setTimestamp(4, new Timestamp(auth.getLastLogin())); + pst.setLong(4, auth.getLastLogin()); pst.setString(5, auth.getRealName()); pst.setString(6, auth.getEmail()); if (useSalt) { @@ -566,7 +586,7 @@ public class MySQL implements DataSource { + col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, auth.getIp()); - pst.setTimestamp(2, new Timestamp(auth.getLastLogin())); + pst.setLong(2, auth.getLastLogin()); pst.setString(3, auth.getRealName()); pst.setString(4, auth.getNickname()); pst.executeUpdate(); @@ -580,20 +600,19 @@ public class MySQL implements DataSource { @Override public synchronized List autoPurgeDatabase(long until) { List list = new ArrayList<>(); - try (Connection con = getConnection()) { - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + " getAllAuthsByIp(String ip) { List result = new ArrayList<>(); - try (Connection con = getConnection()) { - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, ip); - ResultSet rs = pst.executeQuery(); - while (rs.next()) { - result.add(rs.getString(col.NAME)); + try (ResultSet rs = pst.executeQuery()) { + while (rs.next()) { + result.add(rs.getString(col.NAME)); + } } - rs.close(); - pst.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -707,33 +720,29 @@ public class MySQL implements DataSource { } @Override - public synchronized List getAllAuthsByEmail(String email) { - List countEmail = new ArrayList<>(); - try (Connection con = getConnection()) { - String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.EMAIL + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + public synchronized int countAuthsByEmail(String email) { + String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, email); - ResultSet rs = pst.executeQuery(); - while (rs.next()) { - countEmail.add(rs.getString(col.NAME)); + try (ResultSet rs = pst.executeQuery()) { + if (rs.next()) { + return rs.getInt(1); + } } - rs.close(); - pst.close(); } catch (SQLException ex) { logSqlException(ex); } - return countEmail; + return 0; } @Override public synchronized void purgeBanned(List banned) { - try (Connection con = getConnection()) { - PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); + String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { for (String name : banned) { pst.setString(1, name); pst.executeUpdate(); } - pst.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -746,28 +755,25 @@ public class MySQL implements DataSource { @Override public boolean isLogged(String user) { - boolean isLogged = false; - try (Connection con = getConnection()) { - String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, user); - ResultSet rs = pst.executeQuery(); - isLogged = rs.next() && (rs.getInt(col.IS_LOGGED) == 1); + try (ResultSet rs = pst.executeQuery()) { + return rs.next() && (rs.getInt(col.IS_LOGGED) == 1); + } } catch (SQLException ex) { logSqlException(ex); } - return isLogged; + return false; } @Override public void setLogged(String user) { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setInt(1, 1); pst.setString(2, user.toLowerCase()); pst.executeUpdate(); - pst.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -775,13 +781,11 @@ public class MySQL implements DataSource { @Override public void setUnlogged(String user) { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setInt(1, 0); pst.setString(2, user.toLowerCase()); pst.executeUpdate(); - pst.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -789,13 +793,11 @@ public class MySQL implements DataSource { @Override public void purgeLogged() { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setInt(1, 0); pst.setInt(2, 1); pst.executeUpdate(); - pst.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -804,38 +806,23 @@ public class MySQL implements DataSource { @Override public int getAccountsRegistered() { int result = 0; - try (Connection con = getConnection()) { - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + tableName); + String sql = "SELECT COUNT(*) FROM " + tableName; + try (Connection con = getConnection(); + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery(sql)) { if (rs.next()) { result = rs.getInt(1); } - rs.close(); - st.close(); } catch (SQLException ex) { logSqlException(ex); } return result; } - @Override - public void updateName(String oldOne, String newOne) { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); - pst.setString(1, newOne); - pst.setString(2, oldOne); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } - } - @Override public boolean updateRealName(String user, String realName) { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, realName); pst.setString(2, user); pst.executeUpdate(); @@ -848,9 +835,8 @@ public class MySQL implements DataSource { @Override public boolean updateIp(String user, String ip) { - try (Connection con = getConnection()) { - String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; - PreparedStatement pst = con.prepareStatement(sql); + String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, ip); pst.setString(2, user); pst.executeUpdate(); @@ -867,23 +853,23 @@ public class MySQL implements DataSource { try (Connection con = getConnection()) { Statement st = con.createStatement(); ResultSet rs = st.executeQuery("SELECT * FROM " + tableName); - PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;"); while (rs.next()) { PlayerAuth pAuth = buildAuthFromResultSet(rs); if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { - int id = rs.getInt(col.ID); - pst.setInt(1, id); - ResultSet rs2 = pst.executeQuery(); - if (rs2.next()) { - Blob blob = rs2.getBlob("data"); - byte[] bytes = blob.getBytes(1, (int) blob.length()); - pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + try (PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) { + int id = rs.getInt(col.ID); + pst.setInt(1, id); + ResultSet rs2 = pst.executeQuery(); + if (rs2.next()) { + Blob blob = rs2.getBlob("data"); + byte[] bytes = blob.getBytes(1, (int) blob.length()); + pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + } + rs2.close(); } - rs2.close(); } auths.add(pAuth); } - pst.close(); rs.close(); st.close(); } catch (SQLException ex) { @@ -922,12 +908,12 @@ public class MySQL implements DataSource { @Override public synchronized boolean isEmailStored(String email) { - String sql = "SELECT 1 FROM " + tableName + " WHERE " + col.EMAIL + " = ?"; - try (Connection con = ds.getConnection()) { - PreparedStatement pst = con.prepareStatement(sql); + String sql = "SELECT 1 FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)"; + try (Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, email); - ResultSet rs = pst.executeQuery(); - return rs.next(); + try (ResultSet rs = pst.executeQuery()) { + return rs.next(); + } } catch (SQLException e) { logSqlException(e); } @@ -941,7 +927,7 @@ public class MySQL implements DataSource { .name(row.getString(col.NAME)) .realName(row.getString(col.REAL_NAME)) .password(row.getString(col.PASSWORD), salt) - .lastLogin(safeGetTimestamp(row)) + .lastLogin(row.getLong(col.LAST_LOGIN)) .ip(row.getString(col.IP)) .locWorld(row.getString(col.LASTLOC_WORLD)) .locX(row.getDouble(col.LASTLOC_X)) @@ -953,24 +939,15 @@ public class MySQL implements DataSource { } /** - * Retrieve the last login timestamp in a safe way. + * Check if the lastlogin column is of type timestamp and, if so, revert it to the bigint format. * - * @param row The ResultSet to read - * @return The timestamp (as number of milliseconds since 1970-01-01 00:00:00 GMT) + * @param con Connection to the database + * @param rs ResultSet containing meta data for the lastlogin column */ - private long safeGetTimestamp(ResultSet row) { - try { - return row.getTimestamp(col.LAST_LOGIN).getTime(); - } catch (SQLException e) { - ConsoleLogger.logException("Could not get timestamp from resultSet. Defaulting to current time", e); - } - return System.currentTimeMillis(); - } - - private void migrateLastLoginColumnToTimestamp(Connection con, ResultSet rs) throws SQLException { + private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException { final int columnType = rs.getInt("DATA_TYPE"); - if (columnType == Types.BIGINT) { - ConsoleLogger.info("Migrating lastlogin column from bigint to timestamp"); + if (columnType == Types.TIMESTAMP) { + ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint"); final String lastLoginOld = col.LAST_LOGIN + "_old"; // Rename lastlogin to lastlogin_old @@ -981,12 +958,12 @@ public class MySQL implements DataSource { // Create lastlogin column sql = String.format("ALTER TABLE %s ADD COLUMN %s " - + "TIMESTAMP NOT NULL DEFAULT current_timestamp AFTER %s", + + "BIGINT NOT NULL DEFAULT 0 AFTER %s", tableName, col.LAST_LOGIN, col.IP); con.prepareStatement(sql).execute(); // Set values of lastlogin based on lastlogin_old - sql = String.format("UPDATE %s SET %s = FROM_UNIXTIME(%s)", + sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s)", tableName, col.LAST_LOGIN, lastLoginOld); con.prepareStatement(sql).execute(); @@ -994,7 +971,7 @@ public class MySQL implements DataSource { sql = String.format("ALTER TABLE %s DROP COLUMN %s", tableName, lastLoginOld); con.prepareStatement(sql).execute(); - ConsoleLogger.info("Finished migration of lastlogin (bigint to timestamp)"); + ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)"); } } @@ -1002,4 +979,14 @@ public class MySQL implements DataSource { ConsoleLogger.logException("Error during SQL operation:", e); } + private static void close(ResultSet rs) { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + ConsoleLogger.logException("Could not close ResultSet", e); + } + } + } + } diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 081e386cf..d0c39e154 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; @@ -46,6 +47,14 @@ public class SQLite implements DataSource { } } + @VisibleForTesting + SQLite(NewSetting settings, Connection connection) { + this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); + this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); + this.col = new Columns(settings); + this.con = connection; + } + private synchronized void connect() throws ClassNotFoundException, SQLException { Class.forName("org.sqlite.JDBC"); ConsoleLogger.info("SQLite driver loaded"); @@ -341,7 +350,8 @@ public class SQLite implements DataSource { @Override public synchronized void close() { try { - con.close(); + if (con != null && !con.isClosed()) + con.close(); } catch (SQLException ex) { logSqlException(ex); } @@ -394,25 +404,19 @@ public class SQLite implements DataSource { } @Override - public List getAllAuthsByEmail(String email) { - PreparedStatement pst = null; - ResultSet rs = null; - List countEmail = new ArrayList<>(); - try { - pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.EMAIL + "=?;"); + public int countAuthsByEmail(String email) { + String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;"; + try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, email); - rs = pst.executeQuery(); - while (rs.next()) { - countEmail.add(rs.getString(col.NAME)); + try (ResultSet rs = pst.executeQuery()) { + if (rs.next()) { + return rs.getInt(1); + } } - return countEmail; } catch (SQLException ex) { logSqlException(ex); - } finally { - close(rs); - close(pst); } - return new ArrayList<>(); + return 0; } @Override @@ -519,21 +523,6 @@ public class SQLite implements DataSource { return 0; } - @Override - public void updateName(String oldOne, String newOne) { - PreparedStatement pst = null; - try { - pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;"); - pst.setString(1, newOne); - pst.setString(2, oldOne); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } finally { - close(pst); - } - } - @Override public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index 56918026c..d563f33c0 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -243,9 +242,7 @@ public class AuthMePlayerListener implements Listener { String realName = auth.getRealName(); if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) { event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); - // TODO: Add a message like : MessageKey.INVALID_NAME_CASE - event.setKickMessage("You should join using username: " + ChatColor.AQUA + realName + - ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName()); + event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CASE, realName, event.getName())); return; } if (realName.isEmpty() || realName.equals("Player")) { diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java index 5d080ef65..54cce57a9 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java @@ -81,6 +81,8 @@ public class AuthMeServerListener implements Listener { } if (pluginName.equalsIgnoreCase("ProtocolLib")) { plugin.inventoryProtector = null; + plugin.tablistHider = null; + plugin.tabComplete = null; ConsoleLogger.showError("ProtocolLib has been disabled, unhook packet inventory protection!"); } } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java index 3bb083592..ad3402d6e 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeTabCompletePacketAdapter.java @@ -6,32 +6,27 @@ import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.reflect.FieldAccessException; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; public class AuthMeTabCompletePacketAdapter extends PacketAdapter { - public AuthMeTabCompletePacketAdapter(AuthMe plugin) { - super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE); - } + public AuthMeTabCompletePacketAdapter(AuthMe plugin) { + super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE); + } - @Override - public void onPacketReceiving(PacketEvent event) - { - if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) { - try - { - if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { - event.setCancelled(true); - } + @Override + public void onPacketReceiving(PacketEvent event) { + if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) { + try { + if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { + event.setCancelled(true); + } + } catch (FieldAccessException e) { + ConsoleLogger.showError("Couldn't access field."); + } } - catch (FieldAccessException e) - { - ConsoleLogger.showError("Couldn't access field."); - } - } } public void register() { diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java index ea8effd00..d85a5c5e9 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java @@ -6,32 +6,27 @@ import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.reflect.FieldAccessException; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; public class AuthMeTablistPacketAdapter extends PacketAdapter { - public AuthMeTablistPacketAdapter(AuthMe plugin) { - super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); - } + public AuthMeTablistPacketAdapter(AuthMe plugin) { + super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); + } - @Override - public void onPacketSending(PacketEvent event) - { - if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) { - try - { - if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { - event.setCancelled(true); - } + @Override + public void onPacketSending(PacketEvent event) { + if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) { + try { + if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { + event.setCancelled(true); + } + } catch (FieldAccessException e) { + ConsoleLogger.showError("Couldn't access field."); + } } - catch (FieldAccessException e) - { - ConsoleLogger.showError("Couldn't access field."); - } - } } public void register() { diff --git a/src/main/java/fr/xephi/authme/output/MessageKey.java b/src/main/java/fr/xephi/authme/output/MessageKey.java index d5e601207..45cdc0bef 100644 --- a/src/main/java/fr/xephi/authme/output/MessageKey.java +++ b/src/main/java/fr/xephi/authme/output/MessageKey.java @@ -127,7 +127,9 @@ public enum MessageKey { TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"), - NOT_OWNER_ERROR("not_owner_error"); + NOT_OWNER_ERROR("not_owner_error"), + + INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid"); private String key; private String[] tags; diff --git a/src/main/java/fr/xephi/authme/output/Messages.java b/src/main/java/fr/xephi/authme/output/Messages.java index 5afba4726..9ea87f448 100644 --- a/src/main/java/fr/xephi/authme/output/Messages.java +++ b/src/main/java/fr/xephi/authme/output/Messages.java @@ -55,17 +55,7 @@ public class Messages { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(key); - String[] tags = key.getTags(); - if (replacements.length == tags.length) { - for (int i = 0; i < tags.length; ++i) { - message = message.replace(tags[i], replacements[i]); - } - } else { - ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'"); - send(sender, key); - } - + String message = retrieveSingle(key, replacements); for (String line : message.split("\n")) { sender.sendMessage(line); } @@ -99,6 +89,27 @@ public class Messages { return StringUtils.join("\n", retrieve(key)); } + /** + * Retrieve the given message code with the given tag replacements. Note that this method + * logs an error if the number of supplied replacements doesn't correspond to the number of tags + * the message key contains. + * + * @param key The key of the message to send + * @param replacements The replacements to apply for the tags + */ + public String retrieveSingle(MessageKey key, String... replacements) { + String message = retrieveSingle(key); + String[] tags = key.getTags(); + if (replacements.length == tags.length) { + for (int i = 0; i < tags.length; ++i) { + message = message.replace(tags[i], replacements[i]); + } + } else { + ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'"); + } + return message; + } + /** * Reload the messages manager. * diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index f738268ee..e1b6096bb 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -206,20 +206,20 @@ public class AsynchronousJoin { int msgInterval = Settings.getWarnMessageInterval; if (timeOut > 0) { - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); + BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); } - String[] msg; + MessageKey msg; if (isAuthAvailable) { - msg = m.retrieve(MessageKey.LOGIN_MESSAGE); + msg = MessageKey.LOGIN_MESSAGE; } else { msg = Settings.emailRegistration - ? m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE) - : m.retrieve(MessageKey.REGISTER_MESSAGE); + ? MessageKey.REGISTER_EMAIL_MESSAGE + : MessageKey.REGISTER_MESSAGE; } if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) { - BukkitTask msgTask = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval)); + BukkitTask msgTask = sched.runTask(plugin, new MessageTask(plugin, name, msg, msgInterval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask); } } diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 09647dfff..fcc64ab0e 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -105,7 +105,7 @@ public class AsynchronousLogin { } else { msg = m.retrieve(MessageKey.REGISTER_MESSAGE); } - BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, + BukkitTask msgT = Bukkit.getScheduler().runTask(plugin, new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); } diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java index 5a8133b79..64c300ef0 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java @@ -74,21 +74,24 @@ public class ProcessSyncronousPlayerLogout implements Runnable { int interval = Settings.getWarnMessageInterval; BukkitScheduler sched = player.getServer().getScheduler(); if (timeOut != 0) { - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); + BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); } - BukkitTask msgT = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), interval)); + BukkitTask msgT = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); - if (player.isInsideVehicle() && player.getVehicle() != null) + if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); - if (Settings.applyBlindEffect) - player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2)); + } + if (Settings.applyBlindEffect) { + player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); + } player.setOp(false); restoreSpeedEffect(); // Player is now logout... Time to fire event ! Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player)); - if (Settings.bungee) + if (Settings.bungee) { sendBungeeMessage(); + } m.send(player, MessageKey.LOGOUT_SUCCESS); ConsoleLogger.info(player.getName() + " logged out"); } diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java index 00b477440..7f716e7e6 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -5,34 +5,26 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; +import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; -/** - */ public class AsynchronousQuit { - protected final AuthMe plugin; - protected final DataSource database; - protected final Player player; + private final AuthMe plugin; + private final DataSource database; + private final Player player; private final String name; private boolean isOp = false; private boolean needToChange = false; private boolean isKick = false; - /** - * Constructor for AsynchronousQuit. - * - * @param p Player - * @param plugin AuthMe - * @param database DataSource - * @param isKick boolean - */ public AsynchronousQuit(Player p, AuthMe plugin, DataSource database, boolean isKick) { this.player = p; @@ -43,9 +35,7 @@ public class AsynchronousQuit { } public void process() { - if (player == null) - return; - if (Utils.isUnrestricted(player)) { + if (player == null || Utils.isUnrestricted(player)) { return; } @@ -54,7 +44,9 @@ public class AsynchronousQuit { if (PlayerCache.getInstance().isAuthenticated(name)) { if (Settings.isSaveQuitLocationEnabled) { Location loc = player.getLocation(); - PlayerAuth auth = new PlayerAuth(name, loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName(), player.getName()); + PlayerAuth auth = PlayerAuth.builder() + .name(name).location(loc) + .realName(player.getName()).build(); database.updateQuitLoc(auth); } PlayerAuth auth = new PlayerAuth(name, ip, System.currentTimeMillis(), player.getName()); @@ -63,14 +55,11 @@ public class AsynchronousQuit { LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); if (limbo != null) { - if (limbo.getGroup() != null && !limbo.getGroup().isEmpty()) + if (!StringUtils.isEmpty(limbo.getGroup())) { Utils.addNormal(player, limbo.getGroup()); + } needToChange = true; isOp = limbo.getOperator(); - if (limbo.getTimeoutTaskId() != null) - limbo.getTimeoutTaskId().cancel(); - if (limbo.getMessageTaskId() != null) - limbo.getMessageTaskId().cancel(); LimboCache.getInstance().deleteLimboPlayer(name); } if (Settings.isSessionsEnabled && !isKick) { @@ -100,12 +89,15 @@ public class AsynchronousQuit { if (plugin.isEnabled()) { Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); } + // remove player from cache + if (database instanceof CacheDataSource) { + ((CacheDataSource) database).getCachedAuths().invalidate(name); + } } private void postLogout() { PlayerCache.getInstance().removePlayer(name); - if (database.isLogged(name)) - database.setUnlogged(name); + database.setUnlogged(name); plugin.sessions.remove(name); } } diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java index ac9215105..e17c511da 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -98,7 +98,7 @@ public class AsyncRegister { private void emailRegister() { if (Settings.getmaxRegPerEmail > 0 && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - && database.getAllAuthsByEmail(email).size() >= Settings.getmaxRegPerEmail) { + && database.countAuthsByEmail(email) >= Settings.getmaxRegPerEmail) { m.send(player, MessageKey.MAX_REGISTER_EXCEEDED); return; } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java index 4d8dcf9e9..47264c0f1 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -52,14 +52,13 @@ public class ProcessSyncEmailRegister implements Runnable { int msgInterval = Settings.getWarnMessageInterval; BukkitScheduler sched = plugin.getServer().getScheduler(); - if (time != 0 && limbo != null) { - limbo.getTimeoutTaskId().cancel(); - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), time); - limbo.setTimeoutTaskId(id); - } + if (limbo != null) { - limbo.getMessageTaskId().cancel(); - BukkitTask nwMsg = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); + if (time != 0) { + BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), time); + limbo.setTimeoutTaskId(id); + } + BukkitTask nwMsg = sched.runTask(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); limbo.setMessageTaskId(nwMsg); } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 60f5bc187..9f9f8919a 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -1,16 +1,7 @@ package fr.xephi.authme.process.register; -import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.properties.HooksSettings; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; - import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.limbo.LimboCache; @@ -19,10 +10,17 @@ import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; /** */ @@ -37,8 +35,8 @@ public class ProcessSyncPasswordRegister implements Runnable { /** * Constructor for ProcessSyncPasswordRegister. * - * @param player Player - * @param plugin AuthMe + * @param player Player + * @param plugin AuthMe * @param settings The plugin settings */ public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) { @@ -77,11 +75,10 @@ public class ProcessSyncPasswordRegister implements Runnable { BukkitScheduler sched = plugin.getServer().getScheduler(); BukkitTask task; if (delay != 0) { - task = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), delay); + task = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), delay); cache.getLimboPlayer(name).setTimeoutTaskId(task); } - task = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, - m.retrieve(MessageKey.LOGIN_MESSAGE), interval)); + task = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); cache.getLimboPlayer(name).setMessageTaskId(task); if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); @@ -158,7 +155,7 @@ public class ProcessSyncPasswordRegister implements Runnable { // Register is now finished; we can force all commands forceCommands(); - + sendTo(); } diff --git a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java index 58c176941..7da80c390 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -73,12 +73,11 @@ public class AsynchronousUnregister { int interval = Settings.getWarnMessageInterval; BukkitScheduler scheduler = plugin.getServer().getScheduler(); if (timeOut != 0) { - BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, - new TimeoutTask(plugin, name, player), timeOut); + BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); limboPlayer.setTimeoutTaskId(id); } - limboPlayer.setMessageTaskId(scheduler.runTaskAsynchronously(plugin, - new MessageTask(plugin, name, m.retrieve(MessageKey.REGISTER_MESSAGE), interval))); + limboPlayer.setMessageTaskId(scheduler.runTask(plugin, + new MessageTask(plugin, name, MessageKey.REGISTER_MESSAGE, interval))); m.send(player, MessageKey.UNREGISTERED_SUCCESS); ConsoleLogger.info(player.getDisplayName() + " unregistered himself"); return; diff --git a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java index ee1645257..3defffe44 100644 --- a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java +++ b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java @@ -82,4 +82,13 @@ public abstract class CustomConfiguration extends YamlConfiguration { } return false; } + + public boolean containsAll(String... paths) { + for (String path : paths) { + if (!contains(path)) { + return false; + } + } + return true; + } } diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 7ba3b3b18..53a8560cf 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -5,6 +5,7 @@ import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -144,8 +145,6 @@ public final class Settings { denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN); - plugin.checkProtocolLib(); - passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH); backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true); @@ -176,7 +175,7 @@ public final class Settings { emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false); saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8); getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1); - multiverse = configFile.getBoolean("Hooks.multiverse", true); + multiverse = load(HooksSettings.MULTIVERSE); bungee = configFile.getBoolean("Hooks.bungeecord", false); getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds"); banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false); @@ -213,7 +212,7 @@ public final class Settings { broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false); forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false); forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false); - spawnPriority = configFile.getString("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default"); + spawnPriority = load(RestrictionSettings.SPAWN_PRIORITY); getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); diff --git a/src/main/java/fr/xephi/authme/settings/Spawn.java b/src/main/java/fr/xephi/authme/settings/Spawn.java index 4c20b2960..e891e47fb 100644 --- a/src/main/java/fr/xephi/authme/settings/Spawn.java +++ b/src/main/java/fr/xephi/authme/settings/Spawn.java @@ -1,7 +1,13 @@ package fr.xephi.authme.settings; +import com.onarandombox.MultiverseCore.api.MVWorldManager; +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.util.StringUtils; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; import java.io.File; @@ -12,13 +18,17 @@ import java.io.File; public class Spawn extends CustomConfiguration { private static Spawn spawn; + private static String[] spawnPriority; - public Spawn() { - super(new File("." + File.separator + "plugins" + File.separator + "AuthMe" + File.separator + "spawn.yml")); - spawn = this; + private Spawn() { + super(new File(Settings.PLUGIN_FOLDER, "spawn.yml")); load(); save(); - saveDefault(); + spawnPriority = Settings.spawnPriority.split(","); + } + + public static void reload() { + spawn = new Spawn(); } /** @@ -33,111 +43,106 @@ public class Spawn extends CustomConfiguration { return spawn; } - private void saveDefault() { - if (!contains("spawn")) { - set("spawn.world", ""); - set("spawn.x", ""); - set("spawn.y", ""); - set("spawn.z", ""); - set("spawn.yaw", ""); - set("spawn.pitch", ""); - save(); - } - if (!contains("firstspawn")) { - set("firstspawn.world", ""); - set("firstspawn.x", ""); - set("firstspawn.y", ""); - set("firstspawn.z", ""); - set("firstspawn.yaw", ""); - set("firstspawn.pitch", ""); - save(); - } - } - - /** - * Method setSpawn. - * - * @param location Location - * - * @return boolean - */ public boolean setSpawn(Location location) { - try { - set("spawn.world", location.getWorld().getName()); - set("spawn.x", location.getX()); - set("spawn.y", location.getY()); - set("spawn.z", location.getZ()); - set("spawn.yaw", location.getYaw()); - set("spawn.pitch", location.getPitch()); - save(); - return true; - } catch (NullPointerException npe) { + if (location == null || location.getWorld() == null) { return false; } + set("spawn.world", location.getWorld().getName()); + set("spawn.x", location.getX()); + set("spawn.y", location.getY()); + set("spawn.z", location.getZ()); + set("spawn.yaw", location.getYaw()); + set("spawn.pitch", location.getPitch()); + save(); + return true; } - /** - * Method setFirstSpawn. - * - * @param location Location - * - * @return boolean - */ public boolean setFirstSpawn(Location location) { - try { - set("firstspawn.world", location.getWorld().getName()); - set("firstspawn.x", location.getX()); - set("firstspawn.y", location.getY()); - set("firstspawn.z", location.getZ()); - set("firstspawn.yaw", location.getYaw()); - set("firstspawn.pitch", location.getPitch()); - save(); - return true; - } catch (NullPointerException npe) { + if (location == null || location.getWorld() == null) { return false; } + set("firstspawn.world", location.getWorld().getName()); + set("firstspawn.x", location.getX()); + set("firstspawn.y", location.getY()); + set("firstspawn.z", location.getZ()); + set("firstspawn.yaw", location.getYaw()); + set("firstspawn.pitch", location.getPitch()); + save(); + return true; } - /** - * Method getLocation. - * - * @return Location - */ - @Deprecated - public Location getLocation() { - return getSpawn(); - } - - /** - * Method getSpawn. - * - * @return Location - */ public Location getSpawn() { - try { - if (this.getString("spawn.world").isEmpty() || this.getString("spawn.world").equals("")) - return null; - Location location = new Location(Bukkit.getWorld(this.getString("spawn.world")), this.getDouble("spawn.x"), this.getDouble("spawn.y"), this.getDouble("spawn.z"), Float.parseFloat(this.getString("spawn.yaw")), Float.parseFloat(this.getString("spawn.pitch"))); - return location; - } catch (NullPointerException | NumberFormatException npe) { - return null; + if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) { + String worldName = getString("spawn.world"); + World world = Bukkit.getWorld(worldName); + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location( + world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"), + Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch")) + ); + } } + return null; } - /** - * Method getFirstSpawn. - * - * @return Location - */ public Location getFirstSpawn() { - try { - if (this.getString("firstspawn.world").isEmpty() || this.getString("firstspawn.world").equals("")) - return null; - Location location = new Location(Bukkit.getWorld(this.getString("firstspawn.world")), this.getDouble("firstspawn.x"), this.getDouble("firstspawn.y"), this.getDouble("firstspawn.z"), Float.parseFloat(this.getString("firstspawn.yaw")), Float.parseFloat(this.getString("firstspawn.pitch"))); - return location; - } catch (NullPointerException | NumberFormatException npe) { - return null; + if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y", + "firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) { + String worldName = getString("firstspawn.world"); + World world = Bukkit.getWorld(worldName); + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location( + world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"), + Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch")) + ); + } } + return null; } + // Return the spawn location of a player + public Location getSpawnLocation(Player player) { + AuthMe plugin = AuthMe.getInstance(); + if (plugin == null || player == null || player.getWorld() == null) { + return null; + } + + World world = player.getWorld(); + Location spawnLoc = null; + for (String priority : spawnPriority) { + switch (priority.toLowerCase()) { + case "default": + if (world.getSpawnLocation() != null) { + spawnLoc = world.getSpawnLocation(); + } + break; + case "multiverse": + if (Settings.multiverse && plugin.multiverse != null) { + MVWorldManager manager = plugin.multiverse.getMVWorldManager(); + if (manager.isMVWorld(world)) { + spawnLoc = manager.getMVWorld(world).getSpawnLocation(); + } + } + break; + case "essentials": + spawnLoc = plugin.essentialsSpawn; + break; + case "authme": + String playerNameLower = player.getName().toLowerCase(); + if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) { + spawnLoc = getSpawn(); + } else if ((getFirstSpawn() != null) && (!player.hasPlayedBefore() || + (!plugin.getDataSource().isAuthAvailable(playerNameLower)))) { + spawnLoc = getFirstSpawn(); + } else { + spawnLoc = getSpawn(); + } + break; + } + if (spawnLoc != null) { + return spawnLoc; + } + } + return world.getSpawnLocation(); // return default location + } } diff --git a/src/main/java/fr/xephi/authme/task/MessageTask.java b/src/main/java/fr/xephi/authme/task/MessageTask.java index cfaa7d4a4..92310f376 100644 --- a/src/main/java/fr/xephi/authme/task/MessageTask.java +++ b/src/main/java/fr/xephi/authme/task/MessageTask.java @@ -3,6 +3,7 @@ package fr.xephi.authme.task; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; +import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; @@ -24,32 +25,31 @@ public class MessageTask implements Runnable { * @param strings String[] * @param interval int */ - public MessageTask(AuthMe plugin, String name, String[] strings, - int interval) { + public MessageTask(AuthMe plugin, String name, String[] strings, int interval) { this.plugin = plugin; this.name = name; this.msg = strings; this.interval = interval; } - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ + public MessageTask(AuthMe plugin, String name, MessageKey messageKey, int interval) { + this(plugin, name, plugin.getMessages().retrieve(messageKey), interval); + } + @Override public void run() { - if (PlayerCache.getInstance().isAuthenticated(name)) + if (PlayerCache.getInstance().isAuthenticated(name)) { return; + } for (Player player : Utils.getOnlinePlayers()) { - if (player.getName().toLowerCase().equals(name)) { + if (player.getName().equalsIgnoreCase(name)) { for (String ms : msg) { player.sendMessage(ms); } - BukkitTask late = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, this, interval * 20); + BukkitTask nextTask = plugin.getServer().getScheduler().runTaskLater(plugin, this, interval * 20); if (LimboCache.getInstance().hasLimboPlayer(name)) { - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(late); + LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nextTask); } return; } diff --git a/src/main/java/fr/xephi/authme/task/TimeoutTask.java b/src/main/java/fr/xephi/authme/task/TimeoutTask.java index eedf08755..b304632d5 100644 --- a/src/main/java/fr/xephi/authme/task/TimeoutTask.java +++ b/src/main/java/fr/xephi/authme/task/TimeoutTask.java @@ -4,14 +4,10 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; -/** - */ public class TimeoutTask implements Runnable { - private final AuthMe plugin; private final String name; private final Messages m; private final Player player; @@ -25,38 +21,14 @@ public class TimeoutTask implements Runnable { */ public TimeoutTask(AuthMe plugin, String name, Player player) { this.m = plugin.getMessages(); - this.plugin = plugin; this.name = name; this.player = player; } - /** - * Method getName. - * - * @return String - */ - public String getName() { - return name; - } - - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { - if (PlayerCache.getInstance().isAuthenticated(name)) { - return; + if (!PlayerCache.getInstance().isAuthenticated(name)) { + player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR)); } - - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - @Override - public void run() { - if (player.isOnline()) { - player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR)); - } - } - }); } } diff --git a/src/main/resources/messages/messages_bg.yml b/src/main/resources/messages/messages_bg.yml index 4370c00b7..94e32f38f 100644 --- a/src/main/resources/messages/messages_bg.yml +++ b/src/main/resources/messages/messages_bg.yml @@ -58,6 +58,7 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично изключ # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index dfaab23dd..246627773 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -44,7 +44,7 @@ usage_captcha: '&3Para logar você deve resolver o captcha, por favor use o coma wrong_captcha: '&cCaptcha inválido, por favor escreva "/captcha THE_CAPTCHA"' valid_captcha: '&2Código do captcha correto!' kick_forvip: '&3Um vip entrou no servidor!' -kick_fullserver: '&4Servidor esta cheio! Para entrar mesmo cheio compre vip no site www.site.com.br' +kick_fullserver: '&4Servidor esta cheio, tente outra vez mais tarde' usage_email_add: '&cUse: /email add ' usage_email_change: '&cUse: /email change ' usage_email_recovery: '&cUse: /email recovery ' @@ -56,10 +56,10 @@ email_confirm: '&cPor favor confirme o email!' email_changed: '&2Email mudado com sucesso!' email_send: '&2Email de recuperação enviado com sucesso! !' email_exists: '&cUm email de recuperação já foi enviado! Você pode reenviar outro usando o comando:' -country_banned: '&4Seu país foi banido do servidor! Your country is banned from this server!' +country_banned: '&4Seu país foi banido do servidor!' antibot_auto_enabled: '&4[AntiBotService] AntiBot ativado devido ao grande número de conexões!' antibot_auto_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!' -# TODO two_factor_create: Missing tag %url -two_factor_create: '&2Seu código secreto é %code' -# TODO email_already_used: '&4The email address is already being used' -# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file +two_factor_create: '&2Seu código secreto é %code. Você pode escanear ele daqui %url' +email_already_used: '&4Este endereço de email já está em uso' +not_owner_error: 'Você não é o dono desta conta. Por favor, tente outro nome!' +invalid_name_case: 'Você deve entrar usando %valid, não %invalid.' diff --git a/src/main/resources/messages/messages_cz.yml b/src/main/resources/messages/messages_cz.yml index d56a6d423..580a12bc8 100644 --- a/src/main/resources/messages/messages_cz.yml +++ b/src/main/resources/messages/messages_cz.yml @@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod automaticky ukoncen po %m minutach, # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index eb62ce309..942460ac0 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -60,4 +60,5 @@ kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du di # TODO two_factor_create: Missing tag %url two_factor_create: '&2Dein geheimer Code ist %code' # TODO email_already_used: '&4The email address is already being used' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 0b7c96bfb..64aa90993 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -60,3 +60,4 @@ antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m mi email_already_used: '&4The email address is already being used' two_factor_create: '&2Your secret code is %code. You can scan it from here %url' not_owner_error: 'You are not the owner of this account. Please try another name!' +invalid_name_case: 'You should join using username %valid, not %invalid.' diff --git a/src/main/resources/messages/messages_es.yml b/src/main/resources/messages/messages_es.yml index 6fd0dbc19..df415952a 100644 --- a/src/main/resources/messages/messages_es.yml +++ b/src/main/resources/messages/messages_es.yml @@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automáticamente luego d # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_eu.yml b/src/main/resources/messages/messages_eu.yml index bddf751d7..b490aeebf 100644 --- a/src/main/resources/messages/messages_eu.yml +++ b/src/main/resources/messages/messages_eu.yml @@ -52,6 +52,7 @@ country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' diff --git a/src/main/resources/messages/messages_fi.yml b/src/main/resources/messages/messages_fi.yml index 2e26e512d..80e6cd4fd 100644 --- a/src/main/resources/messages/messages_fi.yml +++ b/src/main/resources/messages/messages_fi.yml @@ -55,6 +55,7 @@ email_send: '[AuthMe] Palautus sähköposti lähetetty!' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO country_banned: '&4Your country is banned from this server!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 31a944714..153cceb47 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -61,4 +61,5 @@ email_exists: '&cUn email de restauration a déjà été envoyé ! Vous pouvez l # TODO two_factor_create: Missing tag %url two_factor_create: '&2Votre code secret est %code' # TODO email_already_used: '&4The email address is already being used' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_gl.yml b/src/main/resources/messages/messages_gl.yml index 3dfeb88d3..1562e3a31 100644 --- a/src/main/resources/messages/messages_gl.yml +++ b/src/main/resources/messages/messages_gl.yml @@ -60,5 +60,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despo # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 504364546..b3f228825 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -58,5 +58,6 @@ antibot_auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt a nagy számú antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!' kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.' # TODO email_already_used: '&4The email address is already being used' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_id.yml b/src/main/resources/messages/messages_id.yml index 528357560..24e6a35b7 100644 --- a/src/main/resources/messages/messages_id.yml +++ b/src/main/resources/messages/messages_id.yml @@ -55,6 +55,7 @@ antibot_auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' # TODO email_already_used: '&4The email address is already being used' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO country_banned: '&4Your country is banned from this server!' # TODO usage_unreg: '&cUsage: /unregister ' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index abc943fc4..e7c77c898 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -60,4 +60,5 @@ antibot_auto_disabled: "Il servizio di AntiBot è stato automaticamente disabili kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.' two_factor_create: '&2Il tuo codice segreto è: &f%code&n&2Puoi anche scannerizzare il codice QR da qui: &f%url' # TODO email_already_used: '&4The email address is already being used' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_ko.yml b/src/main/resources/messages/messages_ko.yml index 32ad61663..ab1223095 100644 --- a/src/main/resources/messages/messages_ko.yml +++ b/src/main/resources/messages/messages_ko.yml @@ -61,5 +61,6 @@ antibot_auto_disabled: '[AuthMe] 봇차단모드가 %m 분 후에 자동적으 # TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_lt.yml b/src/main/resources/messages/messages_lt.yml index a432b62a9..fce26d1be 100644 --- a/src/main/resources/messages/messages_lt.yml +++ b/src/main/resources/messages/messages_lt.yml @@ -46,6 +46,7 @@ kick_fullserver: '&cServeris yra pilnas, Atsiprasome.' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO new_email_invalid: '&cInvalid new email, try again!' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' # TODO usage_email_recovery: '&cUsage: /email recovery ' # TODO email_confirm: '&cPlease confirm your email address!' diff --git a/src/main/resources/messages/messages_nl.yml b/src/main/resources/messages/messages_nl.yml index bf662ce7c..a3159e488 100644 --- a/src/main/resources/messages/messages_nl.yml +++ b/src/main/resources/messages/messages_nl.yml @@ -59,5 +59,6 @@ kick_antibot: 'AntiBot is aangezet! Wacht alsjeblieft enkele minuten voor je met two_factor_create: '&2Je geheime code is %code' # TODO email_already_used: '&4The email address is already being used' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO reg_email_msg: '&3Please, register to the server with the command "/register "' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_pl.yml b/src/main/resources/messages/messages_pl.yml index 506995bd5..c6cba56f4 100644 --- a/src/main/resources/messages/messages_pl.yml +++ b/src/main/resources/messages/messages_pl.yml @@ -55,6 +55,7 @@ email_send: '[AuthMe] Email z odzyskaniem wyslany!' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO country_banned: '&4Your country is banned from this server!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' diff --git a/src/main/resources/messages/messages_pt.yml b/src/main/resources/messages/messages_pt.yml index 5b11aea6b..64428a525 100644 --- a/src/main/resources/messages/messages_pt.yml +++ b/src/main/resources/messages/messages_pt.yml @@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automaticamente após %m # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml index 3c34794e9..2b9dd640b 100644 --- a/src/main/resources/messages/messages_ru.yml +++ b/src/main/resources/messages/messages_ru.yml @@ -58,5 +58,6 @@ antibot_auto_disabled: '&a[AuthMe] AntiBot-режим автоматичски # TODO email_already_used: '&4The email address is already being used' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_sk.yml b/src/main/resources/messages/messages_sk.yml index 1366e2a95..77433f62c 100644 --- a/src/main/resources/messages/messages_sk.yml +++ b/src/main/resources/messages/messages_sk.yml @@ -38,28 +38,29 @@ name_len: '&cTvoje meno je velmi krátke alebo dlhé' regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX' add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"' recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery ' -# TODO email_already_used: '&4The email address is already being used' -# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' # TODO usage_email_change: '&cUsage: /email change ' -# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' +# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' +# TODO email_already_used: '&4The email address is already being used' # TODO new_email_invalid: '&cInvalid new email, try again!' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' -# TODO email_confirm: '&cPlease confirm your email address!' -# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha "' -# TODO usage_email_recovery: '&cUsage: /email recovery ' -# TODO email_changed: '&2Email address changed correctly!' # TODO old_email_invalid: '&cInvalid old email, try again!' +# TODO email_changed: '&2Email address changed correctly!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' -# TODO kick_fullserver: '&4The server is full, try again later!' -# TODO email_added: '&2Email address successfully added to your account!' -# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO country_banned: '&4Your country is banned from this server!' -# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' -# TODO email_invalid: '&cInvalid email address, try again!' -# TODO kick_forvip: '&3A VIP player has joined the server when it was full!' # TODO usage_email_add: '&cUsage: /email add ' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO valid_captcha: '&2Captcha code solved correctly!' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' +# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' +# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' +# TODO usage_email_recovery: '&cUsage: /email recovery ' +# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha "' +# TODO email_confirm: '&cPlease confirm your email address!' +# TODO kick_fullserver: '&4The server is full, try again later!' +# TODO email_added: '&2Email address successfully added to your account!' +# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO kick_forvip: '&3A VIP player has joined the server when it was full!' +# TODO email_invalid: '&cInvalid email address, try again!' +# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_tr.yml b/src/main/resources/messages/messages_tr.yml index b3603929a..0e118de04 100644 --- a/src/main/resources/messages/messages_tr.yml +++ b/src/main/resources/messages/messages_tr.yml @@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMode %m dakika sonra otomatik olarak isg # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_uk.yml b/src/main/resources/messages/messages_uk.yml index 6c5db173f..df5710f93 100644 --- a/src/main/resources/messages/messages_uk.yml +++ b/src/main/resources/messages/messages_uk.yml @@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично вимкну # TODO email_already_used: '&4The email address is already being used' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_vn.yml b/src/main/resources/messages/messages_vn.yml index 2df8588af..262b7b73b 100644 --- a/src/main/resources/messages/messages_vn.yml +++ b/src/main/resources/messages/messages_vn.yml @@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBot tự huỷ kích hoạt sau %m phút, h # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhcn.yml b/src/main/resources/messages/messages_zhcn.yml index d1f71209b..3c7fa1a23 100644 --- a/src/main/resources/messages/messages_zhcn.yml +++ b/src/main/resources/messages/messages_zhcn.yml @@ -63,5 +63,6 @@ antibot_auto_disabled: '&8[&6用戶系統&8] 防止機械人程序檢查到不 # TODO email_already_used: '&4The email address is already being used' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhhk.yml b/src/main/resources/messages/messages_zhhk.yml index e18f4109c..2eacb1f62 100644 --- a/src/main/resources/messages/messages_zhhk.yml +++ b/src/main/resources/messages/messages_zhhk.yml @@ -63,4 +63,5 @@ email_already_used: '&4邮箱已被使用' kick_antibot: '[AuthMe] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器' email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:' two_factor_create: '&2你的代码是 %code,你可以使用 %url 来扫描' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_zhtw.yml b/src/main/resources/messages/messages_zhtw.yml index 61ed5d88a..d31431f4b 100644 --- a/src/main/resources/messages/messages_zhtw.yml +++ b/src/main/resources/messages/messages_zhtw.yml @@ -63,5 +63,6 @@ antibot_auto_enabled: '&b【AuthMe】&6AntiBotMod已自動啟用!' antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉' # TODO email_already_used: '&4The email address is already being used' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' +# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file diff --git a/src/test/java/fr/xephi/authme/TestHelper.java b/src/test/java/fr/xephi/authme/TestHelper.java index a7a60865f..06ff06a4b 100644 --- a/src/test/java/fr/xephi/authme/TestHelper.java +++ b/src/test/java/fr/xephi/authme/TestHelper.java @@ -2,6 +2,8 @@ package fr.xephi.authme; import java.io.File; import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; /** * AuthMe test utilities. @@ -18,11 +20,31 @@ public final class TestHelper { * @return The project file */ public static File getJarFile(String path) { + URL url = getUrlOrThrow(path); + return new File(url.getFile()); + } + + /** + * Return a {@link Path} to a file in the JAR's resources (main or test). + * + * @param path The absolute path to the file + * @return The Path object to the file + */ + public static Path getJarPath(String path) { + String sqlFilePath = getUrlOrThrow(path).getPath(); + // Windows preprends the path with a '/' or '\', which Paths cannot handle + String appropriatePath = System.getProperty("os.name").contains("indow") + ? sqlFilePath.substring(1) + : sqlFilePath; + return Paths.get(appropriatePath); + } + + private static URL getUrlOrThrow(String path) { URL url = TestHelper.class.getResource(path); if (url == null) { throw new IllegalStateException("File '" + path + "' could not be loaded"); } - return new File(url.getFile()); + return url; } } diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java new file mode 100644 index 000000000..abe1d5212 --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -0,0 +1,295 @@ +package fr.xephi.authme.datasource; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.security.crypts.HashedPassword; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static fr.xephi.authme.datasource.AuthMeMatchers.equalToHash; +import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthBasicData; +import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthLocation; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; + +/** + * Abstract class for data source integration tests. + */ +public abstract class AbstractDataSourceIntegrationTest { + + protected abstract DataSource getDataSource(); + + @Test + public void shouldReturnIfAuthIsAvailableOrNot() { + // given + DataSource dataSource = getDataSource(); + + // when + boolean isBobbyAvailable = dataSource.isAuthAvailable("bobby"); + boolean isChrisAvailable = dataSource.isAuthAvailable("chris"); + boolean isUserAvailable = dataSource.isAuthAvailable("USER"); + + // then + assertThat(isBobbyAvailable, equalTo(true)); + assertThat(isChrisAvailable, equalTo(false)); + assertThat(isUserAvailable, equalTo(true)); + } + + @Test + public void shouldReturnPassword() { + // given + DataSource dataSource = getDataSource(); + + // when + HashedPassword bobbyPassword = dataSource.getPassword("bobby"); + HashedPassword invalidPassword = dataSource.getPassword("doesNotExist"); + HashedPassword userPassword = dataSource.getPassword("user"); + + // then + assertThat(bobbyPassword, equalToHash("$SHA$11aa0706173d7272$dbba966")); + assertThat(invalidPassword, nullValue()); + assertThat(userPassword, equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32")); + } + + @Test + public void shouldGetAuth() { + // given + DataSource dataSource = getDataSource(); + + // when + PlayerAuth invalidAuth = dataSource.getAuth("notInDB"); + PlayerAuth bobbyAuth = dataSource.getAuth("Bobby"); + PlayerAuth userAuth = dataSource.getAuth("user"); + + // then + assertThat(invalidAuth, nullValue()); + + assertThat(bobbyAuth, hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")); + assertThat(bobbyAuth, hasAuthLocation(1.05, 2.1, 4.2, "world")); + assertThat(bobbyAuth.getLastLogin(), equalTo(1449136800L)); + assertThat(bobbyAuth.getPassword(), equalToHash("$SHA$11aa0706173d7272$dbba966")); + + assertThat(userAuth, hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90")); + assertThat(userAuth, hasAuthLocation(124.1, 76.3, -127.8, "nether")); + assertThat(userAuth.getLastLogin(), equalTo(1453242857L)); + assertThat(userAuth.getPassword(), equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32")); + } + + @Test + public void shouldFindIfEmailExists() { + // given + DataSource dataSource = getDataSource(); + + // when + boolean isUserMailPresent = dataSource.isEmailStored("user@example.org"); + boolean isUserMailPresentCaseInsensitive = dataSource.isEmailStored("user@example.ORG"); + boolean isInvalidMailPresent = dataSource.isEmailStored("not-in-database@example.com"); + + // then + assertThat(isUserMailPresent, equalTo(true)); + assertThat(isUserMailPresentCaseInsensitive, equalTo(true)); + assertThat(isInvalidMailPresent, equalTo(false)); + } + + @Test + public void shouldCountAuthsByEmail() { + // given + DataSource dataSource = getDataSource(); + + // when + int userMailCount = dataSource.countAuthsByEmail("user@example.ORG"); + int invalidMailCount = dataSource.countAuthsByEmail("not.in.db@example.com"); + boolean response = dataSource.saveAuth( + PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build()); + int newUserCount = dataSource.countAuthsByEmail("user@Example.org"); + + // then + assertThat(userMailCount, equalTo(1)); + assertThat(invalidMailCount, equalTo(0)); + assertThat(response, equalTo(true)); + assertThat(newUserCount, equalTo(2)); + } + + @Test + public void shouldReturnAllAuths() { + // given + DataSource dataSource = getDataSource(); + + // when + List authList = dataSource.getAllAuths(); + boolean response = dataSource.saveAuth( + PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build()); + List newAuthList = dataSource.getAllAuths(); + + // then + assertThat(response, equalTo(true)); + assertThat(authList, hasSize(2)); + assertThat(newAuthList, hasSize(3)); + boolean hasBobby = false; + for (PlayerAuth auth : authList) { + if (auth.getNickname().equals("bobby")) { + hasBobby = true; + break; + } + } + assertThat(hasBobby, equalTo(true)); + } + + @Test + public void shouldUpdatePassword() { + // given + DataSource dataSource = getDataSource(); + HashedPassword newHash = new HashedPassword("new_hash"); + + // when + boolean response1 = dataSource.updatePassword("user", newHash); + boolean response2 = dataSource.updatePassword("non-existent-name", new HashedPassword("sd")); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getPassword("user"), equalToHash(newHash)); + } + + @Test + public void shouldRemovePlayerAuth() { + // given + DataSource dataSource = getDataSource(); + + // when + boolean response1 = dataSource.removeAuth("bobby"); + boolean response2 = dataSource.removeAuth("does-not-exist"); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAuth("bobby"), nullValue()); + assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false)); + } + + @Test + public void shouldUpdateSession() { + // given + DataSource dataSource = getDataSource(); + PlayerAuth bobby = PlayerAuth.builder() + .name("bobby").realName("BOBBY").lastLogin(123L) + .ip("12.12.12.12").build(); + + // when + boolean response = dataSource.updateSession(bobby); + + // then + assertThat(response, equalTo(true)); + PlayerAuth result = dataSource.getAuth("bobby"); + assertThat(result, hasAuthBasicData("bobby", "BOBBY", "your@email.com", "12.12.12.12")); + assertThat(result.getLastLogin(), equalTo(123L)); + } + + @Test + public void shouldUpdateLastLoc() { + // given + DataSource dataSource = getDataSource(); + PlayerAuth user = PlayerAuth.builder() + .name("user").locX(143).locY(-42.12).locZ(29.47) + .locWorld("the_end").build(); + + // when + boolean response = dataSource.updateQuitLoc(user); + + // then + assertThat(response, equalTo(true)); + assertThat(dataSource.getAuth("user"), hasAuthLocation(143, -42.12, 29.47, "the_end")); + } + + @Test + public void shouldDeletePlayers() { + // given + DataSource dataSource = getDataSource(); + List playersToDelete = Arrays.asList("bobby", "doesNotExist"); + assumeThat(dataSource.getAccountsRegistered(), equalTo(2)); + + // when + dataSource.purgeBanned(playersToDelete); + + // then + assertThat(dataSource.getAccountsRegistered(), equalTo(1)); + assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false)); + assertThat(dataSource.isAuthAvailable("user"), equalTo(true)); + } + + @Test + public void shouldUpdateEmail() { + // given + DataSource dataSource = getDataSource(); + String email = "new-user@mail.tld"; + PlayerAuth userAuth = PlayerAuth.builder().name("user").email(email).build(); + PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").email("addr@example.com").build(); + + // when + boolean response1 = dataSource.updateEmail(userAuth); + boolean response2 = dataSource.updateEmail(invalidAuth); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("user", "user", email, "34.56.78.90"))); + } + + @Test + public void shouldUpdateIp() { + // given + DataSource dataSource = getDataSource(); + String ip = "250.230.67.73"; + + // when + boolean response1 = dataSource.updateIp("bobby", ip); + boolean response2 = dataSource.updateIp("bogus", "123.123.123.123"); + + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", ip))); + } + + @Test + public void shouldCountAuths() { + // given + DataSource dataSource = getDataSource(); + + // when + int initialCount = dataSource.getAccountsRegistered(); + for (int i = 0; i < 4; ++i) { + dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).build()); + } + int endCount = dataSource.getAccountsRegistered(); + + // then + assertThat(initialCount, equalTo(2)); + assertThat(endCount, equalTo(6)); + } + + @Test + public void shouldGetAllUsersByIp() { + // given + DataSource dataSource = getDataSource(); + + // when + List initialList = dataSource.getAllAuthsByIp("123.45.67.89"); + List emptyList = dataSource.getAllAuthsByIp("8.8.8.8"); + for (int i = 0; i < 3; ++i) { + dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).ip("123.45.67.89").build()); + } + List updatedList = dataSource.getAllAuthsByIp("123.45.67.89"); + + // then + assertThat(initialList, hasSize(1)); + assertThat(initialList.get(0), equalTo("bobby")); + assertThat(emptyList, hasSize(0)); + assertThat(updatedList, hasSize(4)); + assertThat(updatedList, hasItem(equalTo("bobby"))); + assertThat(updatedList, hasItem(equalTo("test-1"))); + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java b/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java new file mode 100644 index 000000000..798d5315b --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java @@ -0,0 +1,84 @@ +package fr.xephi.authme.datasource; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.security.crypts.HashedPassword; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.util.Objects; + +/** + * Custom matchers for AuthMe entities. + */ +public final class AuthMeMatchers { + + private AuthMeMatchers() { + } + + public static Matcher equalToHash(final String hash) { + return equalToHash(new HashedPassword(hash)); + } + + public static Matcher equalToHash(final String hash, final String salt) { + return equalToHash(new HashedPassword(hash, salt)); + } + + public static Matcher equalToHash(final HashedPassword hash) { + return new TypeSafeMatcher() { + @Override + public boolean matchesSafely(HashedPassword item) { + return Objects.equals(hash.getHash(), item.getHash()) + && Objects.equals(hash.getSalt(), item.getSalt()); + } + + @Override + public void describeTo(Description description) { + String representation = "'" + hash.getHash() + "'"; + if (hash.getSalt() != null) { + representation += ", '" + hash.getSalt() + "'"; + } + description.appendValue("HashedPassword(" + representation + ")"); + } + }; + } + + public static Matcher hasAuthBasicData(final String name, final String realName, + final String email, final String ip) { + return new TypeSafeMatcher() { + @Override + public boolean matchesSafely(PlayerAuth item) { + return Objects.equals(name, item.getNickname()) + && Objects.equals(realName, item.getRealName()) + && Objects.equals(email, item.getEmail()) + && Objects.equals(ip, item.getIp()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(String.format("PlayerAuth with name %s, realname %s, email %s, ip %s", + name, realName, email, ip)); + } + }; + } + + public static Matcher hasAuthLocation(final double x, final double y, final double z, + final String world) { + return new TypeSafeMatcher() { + @Override + public boolean matchesSafely(PlayerAuth item) { + return Objects.equals(x, item.getQuitLocX()) + && Objects.equals(y, item.getQuitLocY()) + && Objects.equals(z, item.getQuitLocZ()) + && Objects.equals(world, item.getWorld()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(String.format("PlayerAuth with quit location (x: %f, y: %f, z: %f, world: %s)", + x, y, z, world)); + } + }; + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java new file mode 100644 index 000000000..586ca0ad1 --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -0,0 +1,96 @@ +package fr.xephi.authme.datasource; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import org.junit.Before; +import org.junit.BeforeClass; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Integration test for {@link MySQL}. + */ +public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { + + /** Mock of a settings instance. */ + private static NewSetting settings; + /** SQL statement to execute before running a test. */ + private static String sqlInitialize; + /** Connection to the H2 test database. */ + private HikariDataSource hikariSource; + + /** + * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. + */ + @BeforeClass + public static void initializeSettings() throws IOException, ClassNotFoundException { + // Check that we have an H2 driver + Class.forName("org.h2.jdbcx.JdbcDataSource"); + + settings = mock(NewSetting.class); + when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return ((Property) invocation.getArguments()[0]).getDefaultValue(); + } + }); + set(DatabaseSettings.MYSQL_DATABASE, "h2_test"); + set(DatabaseSettings.MYSQL_TABLE, "authme"); + set(DatabaseSettings.MYSQL_COL_SALT, "salt"); + ConsoleLoggerTestInitializer.setupLogger(); + + Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql"); + sqlInitialize = new String(Files.readAllBytes(sqlInitFile)); + } + + @Before + public void initializeConnectionAndTable() throws SQLException { + silentClose(hikariSource); + HikariConfig config = new HikariConfig(); + config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); + config.setConnectionTestQuery("VALUES 1"); + config.addDataSourceProperty("URL", "jdbc:h2:mem:test"); + config.addDataSourceProperty("user", "sa"); + config.addDataSourceProperty("password", "sa"); + HikariDataSource ds = new HikariDataSource(config); + Connection connection = ds.getConnection(); + + try (Statement st = connection.createStatement()) { + st.execute("DROP TABLE IF EXISTS authme"); + st.execute(sqlInitialize); + } + hikariSource = ds; + } + + @Override + protected DataSource getDataSource() { + return new MySQL(settings, hikariSource); + } + + private static void set(Property property, T value) { + when(settings.getProperty(property)).thenReturn(value); + } + + private static void silentClose(HikariDataSource con) { + if (con != null && !con.isClosed()) { + con.close(); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java new file mode 100644 index 000000000..715d2ce9b --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java @@ -0,0 +1,96 @@ +package fr.xephi.authme.datasource; + +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import org.junit.Before; +import org.junit.BeforeClass; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Integration test for {@link SQLite}. + */ +public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest { + + /** Mock of a settings instance. */ + private static NewSetting settings; + /** Collection of SQL statements to execute for initialization of a test. */ + private static String[] sqlInitialize; + /** Connection to the SQLite test database. */ + private Connection con; + + /** + * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. + */ + @BeforeClass + public static void initializeSettings() throws IOException, ClassNotFoundException { + // Check that we have an implementation for SQLite + Class.forName("org.sqlite.JDBC"); + + settings = mock(NewSetting.class); + when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return ((Property) invocation.getArguments()[0]).getDefaultValue(); + } + }); + set(DatabaseSettings.MYSQL_DATABASE, "sqlite-test"); + set(DatabaseSettings.MYSQL_TABLE, "authme"); + set(DatabaseSettings.MYSQL_COL_SALT, "salt"); + ConsoleLoggerTestInitializer.setupLogger(); + + Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql"); + // Note ljacqu 20160221: It appears that we can only run one statement per Statement.execute() so we split + // the SQL file by ";\n" as to get the individual statements + sqlInitialize = new String(Files.readAllBytes(sqlInitFile)).split(";(\\r?)\\n"); + } + + @Before + public void initializeConnectionAndTable() throws SQLException { + silentClose(con); + Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:"); + try (Statement st = connection.createStatement()) { + st.execute("DROP TABLE IF EXISTS authme"); + for (String statement : sqlInitialize) { + st.execute(statement); + } + } + con = connection; + } + + @Override + protected DataSource getDataSource() { + return new SQLite(settings, con); + } + + private static void set(Property property, T value) { + when(settings.getProperty(property)).thenReturn(value); + } + + private static void silentClose(Connection con) { + if (con != null) { + try { + if (!con.isClosed()) { + con.close(); + } + } catch (SQLException e) { + // silent + } + } + } +} diff --git a/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java index 5ff79b9dd..399553657 100644 --- a/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java @@ -266,4 +266,16 @@ public class MessagesIntegrationTest { assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE), equalTo("Message from default file")); } + + @Test + public void shouldRetrieveMessageWithReplacements() { + // given + MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; + + // when + String result = messages.retrieveSingle(key, "24680"); + + // then + assertThat(result, equalTo("Use /captcha 24680 to solve the captcha")); + } } diff --git a/src/test/resources/datasource-integration/sql-initialize.sql b/src/test/resources/datasource-integration/sql-initialize.sql new file mode 100644 index 000000000..5dea47d51 --- /dev/null +++ b/src/test/resources/datasource-integration/sql-initialize.sql @@ -0,0 +1,22 @@ +-- Important: separate SQL statements by ; followed directly by a newline. We split the file contents by ";\n" + +CREATE TABLE authme ( + id INTEGER AUTO_INCREMENT, + username VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + ip VARCHAR(40) NOT NULL, + lastlogin BIGINT, + x DOUBLE NOT NULL DEFAULT '0.0', + y DOUBLE NOT NULL DEFAULT '0.0', + z DOUBLE NOT NULL DEFAULT '0.0', + world VARCHAR(255) NOT NULL DEFAULT 'world', + email VARCHAR(255) DEFAULT 'your@email.com', + isLogged INT DEFAULT '0', realname VARCHAR(255) NOT NULL DEFAULT 'Player', + salt varchar(255), + CONSTRAINT table_const_prim PRIMARY KEY (id) +); + +INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt) +VALUES (1,'bobby','$SHA$11aa0706173d7272$dbba966','123.45.67.89',1449136800,1.05,2.1,4.2,'world','your@email.com',0,'Bobby',NULL); +INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt) +VALUES (NULL,'user','b28c32f624a4eb161d6adc9acb5bfc5b','34.56.78.90',1453242857,124.1,76.3,-127.8,'nether','user@example.org',0,'user','f750ba32');