From 2638007adae2138ec37fad53919af17675c7fb62 Mon Sep 17 00:00:00 2001 From: Xephi Date: Fri, 13 Jun 2014 05:56:59 +0200 Subject: [PATCH] AuthMe 3.4 --- lib/bungeecord-api.jar | Bin 68293 -> 0 bytes pom.xml | 11 +- src/main/java/fr/xephi/authme/AuthMe.java | 3 +- src/main/java/fr/xephi/authme/api/API.java | 2 +- .../xephi/authme/cache/auth/PlayerAuth.java | 16 +- .../xephi/authme/cache/backup/FileCache.java | 178 ++++++++++-------- .../xephi/authme/commands/AdminCommand.java | 76 +++----- .../xephi/authme/commands/LogoutCommand.java | 2 +- .../authme/commands/UnregisterCommand.java | 4 +- .../fr/xephi/authme/converter/Converter.java | 6 + .../fr/xephi/authme/converter/FlatToSql.java | 6 +- .../xephi/authme/converter/FlatToSqlite.java | 24 ++- .../authme/converter/RakamakConverter.java | 56 +++--- .../authme/converter/RoyalAuthConverter.java | 40 ++-- .../authme/converter/xAuthConverter.java | 35 ++++ .../authme/datasource/MySQLDataSource.java | 2 +- .../authme/listener/AuthMePlayerListener.java | 40 ++-- .../plugin/manager/CombatTagComunicator.java | 81 +++----- .../login/ProcessSyncronousPlayerLogin.java | 44 ++--- .../ProcessSyncronousEmailRegister.java | 2 +- .../ProcessSyncronousPasswordRegister.java | 4 +- .../xephi/authme/security/HashAlgorithm.java | 2 + .../authme/security/PasswordSecurity.java | 10 +- .../xephi/authme/security/crypts/BCRYPT.java | 34 +++- .../authme/security/crypts/CRAZYCRYPT1.java | 44 +++++ .../authme/security/crypts/CryptPBKDF2.java | 2 +- .../authme/security/crypts/DOUBLEMD5.java | 4 +- .../security/crypts/EncryptionMethod.java | 2 +- .../fr/xephi/authme/security/crypts/IPB3.java | 4 +- .../xephi/authme/security/crypts/JOOMLA.java | 2 +- .../fr/xephi/authme/security/crypts/MD5.java | 4 +- .../xephi/authme/security/crypts/MD5VB.java | 4 +- .../fr/xephi/authme/security/crypts/MYBB.java | 4 +- .../xephi/authme/security/crypts/PHPBB.java | 2 +- .../authme/security/crypts/PHPFUSION.java | 4 +- .../authme/security/crypts/PLAINTEXT.java | 2 +- .../authme/security/crypts/ROYALAUTH.java | 28 +-- .../authme/security/crypts/SALTED2MD5.java | 2 +- .../fr/xephi/authme/security/crypts/SHA1.java | 4 +- .../xephi/authme/security/crypts/SHA256.java | 4 +- .../xephi/authme/security/crypts/SHA512.java | 4 +- .../fr/xephi/authme/security/crypts/SMF.java | 6 +- .../fr/xephi/authme/security/crypts/WBB3.java | 4 +- .../fr/xephi/authme/security/crypts/WBB4.java | 19 ++ .../authme/security/crypts/WHIRLPOOL.java | 4 +- .../authme/security/crypts/WORDPRESS.java | 2 +- .../xephi/authme/security/crypts/XAUTH.java | 4 +- .../fr/xephi/authme/security/crypts/XF.java | 2 +- .../fr/xephi/authme/settings/Settings.java | 35 ++-- .../fr/xephi/authme/threads/MySQLThread.java | 2 +- src/main/resources/config.yml | 9 +- src/main/resources/plugin.yml | 2 +- 52 files changed, 504 insertions(+), 383 deletions(-) delete mode 100644 lib/bungeecord-api.jar create mode 100644 src/main/java/fr/xephi/authme/converter/Converter.java create mode 100644 src/main/java/fr/xephi/authme/converter/xAuthConverter.java create mode 100644 src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java create mode 100644 src/main/java/fr/xephi/authme/security/crypts/WBB4.java diff --git a/lib/bungeecord-api.jar b/lib/bungeecord-api.jar deleted file mode 100644 index 73c5e094b6b29ca589df43476d0b792794714080..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68293 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 - 3.3.6 + 3.4 org.bukkit craftbukkit - 1.7.5-R0.1-SNAPSHOT + 1.7.9-R0.2 net.milkbowl.vault @@ -128,13 +128,6 @@ Pre2.9.4.1 system ${project.basedir}/lib/Essentials.jar - - - net.md-5 - bungeecord-api - 1.7.2-SNAPSHOT - system - ${project.basedir}/lib/bungeecord-api.jar \ No newline at end of file diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index fe253e49a..8c7cfc860 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -537,7 +537,8 @@ public class AuthMe extends JavaPlugin { player.getInventory().setArmorContents(limbo.getArmour()); player.getInventory().setContents(limbo.getInventory()); } - player.teleport(limbo.getLoc()); + if (!Settings.noTeleport) + player.teleport(limbo.getLoc()); this.utils.addNormal(player, limbo.getGroup()); player.setOp(limbo.getOperator()); this.plugin.getServer().getScheduler().cancelTask(limbo.getTimeoutTaskId()); diff --git a/src/main/java/fr/xephi/authme/api/API.java b/src/main/java/fr/xephi/authme/api/API.java index e34eebcb0..f37b4d90e 100644 --- a/src/main/java/fr/xephi/authme/api/API.java +++ b/src/main/java/fr/xephi/authme/api/API.java @@ -32,7 +32,7 @@ public class API { */ public static AuthMe hookAuthMe() { Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("AuthMe"); - if (plugin == null && !(plugin instanceof AuthMe)) { + if (plugin == null || !(plugin instanceof AuthMe)) { return null; } return (AuthMe) plugin; 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 4f7cb6d84..e9292750c 100644 --- a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java @@ -26,6 +26,7 @@ public class PlayerAuth { this.lastLogin = lastLogin; this.email = email; this.realName = realName; + } public PlayerAuth(String nickname, double x, double y, double z, String world) { @@ -34,6 +35,8 @@ public class PlayerAuth { this.y = y; this.z = z; this.world = world; + this.lastLogin = System.currentTimeMillis(); + } public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) { @@ -47,6 +50,7 @@ public class PlayerAuth { this.world = world; this.email = email; this.realName = realName; + } public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) { @@ -62,6 +66,7 @@ public class PlayerAuth { this.groupId = groupId; this.email = email; this.realName = realName; + } public PlayerAuth(String nickname, String hash, String salt, int groupId , String ip, long lastLogin, String realName) { @@ -72,6 +77,7 @@ public class PlayerAuth { this.salt = salt; this.groupId = groupId; this.realName = realName; + } public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, String realName) { @@ -81,6 +87,7 @@ public class PlayerAuth { this.lastLogin = lastLogin; this.salt = salt; this.realName = realName; + } public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) { @@ -101,6 +108,14 @@ public class PlayerAuth { this.nickname = nickname; this.ip = ip; this.lastLogin = lastLogin; + + } + + public PlayerAuth(String nickname, String hash, String ip, long lastLogin) { + this.nickname = nickname; + this.ip = ip; + this.lastLogin = lastLogin; + this.hash = hash; } public String getIp() { @@ -218,5 +233,4 @@ public class PlayerAuth { public String getRealname() { return realName; } - } diff --git a/src/main/java/fr/xephi/authme/cache/backup/FileCache.java b/src/main/java/fr/xephi/authme/cache/backup/FileCache.java index a61770359..75b77805b 100644 --- a/src/main/java/fr/xephi/authme/cache/backup/FileCache.java +++ b/src/main/java/fr/xephi/authme/cache/backup/FileCache.java @@ -10,11 +10,14 @@ import java.util.Scanner; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import fr.xephi.authme.AuthMe; import fr.xephi.authme.api.API; public class FileCache { + private AuthMe plugin = AuthMe.getInstance(); public FileCache() { final File folder = new File("cache"); if (!folder.exists()) { @@ -127,25 +130,25 @@ public class FileCache { final File file = new File("cache/" + playername + ".cache"); - ItemStack[] stacki = new ItemStack[36]; - ItemStack[] stacka = new ItemStack[4]; - String group = null; - boolean op = false; - boolean flying = false; - if (!file.exists()) { - return new DataFileCache(stacki, stacka); - } + ItemStack[] stacki = new ItemStack[36]; + ItemStack[] stacka = new ItemStack[4]; + String group = null; + boolean op = false; + boolean flying = false; + if (!file.exists()) { + return new DataFileCache(stacki, stacka); + } - Scanner reader = null; - try { - reader = new Scanner(file); + Scanner reader = null; + try { + reader = new Scanner(file); - int i = 0; - int a = 0; - while (reader.hasNextLine()) { - String line = reader.nextLine(); + int i = 0; + int a = 0; + while (reader.hasNextLine()) { + String line = reader.nextLine(); - if (!line.contains(":")) { + if (!line.contains(":")) { // the fist line represent the player group, operator status and flying status final String[] playerInfo = line.split(";"); group = playerInfo[0]; @@ -154,77 +157,88 @@ public class FileCache { op = true; } else op = false; if (playerInfo.length > 2) { - if (Integer.parseInt(playerInfo[2]) == 1) - flying = true; - else flying = false; + if (Integer.parseInt(playerInfo[2]) == 1) + flying = true; + else flying = false; } continue; - } + } - if (!line.startsWith("i") && !line.startsWith("w")) { - continue; - } - String lores = ""; - String name = ""; - if (line.split("\\*").length > 1) { - lores = line.split("\\*")[1]; - line = line.split("\\*")[0]; - } - if (line.split(";").length > 1) { - name = line.split(";")[1]; - line = line.split(";")[0]; - } - final String[] in = line.split(":"); + if (!line.startsWith("i") && !line.startsWith("w")) { + continue; + } + String lores = ""; + String name = ""; + if (line.split("\\*").length > 1) { + lores = line.split("\\*")[1]; + line = line.split("\\*")[0]; + } + if (line.split(";").length > 1) { + name = line.split(";")[1]; + line = line.split(";")[0]; + } + final String[] in = line.split(":"); // can enchant item? size ofstring in file - 4 all / 2 = number of enchant - if (in[0].equals("i")) { - stacki[i] = new ItemStack(Material.getMaterial(in[1]), - Integer.parseInt(in[2]), Short.parseShort((in[3]))); - if(in.length > 4 && !in[4].isEmpty()) { - for(int k=4;k loreList = new ArrayList(); - for (String s : lores.split("%newline%")) { - loreList.add(s); - } - stacki[i].getItemMeta().setLore(loreList); - } - i++; - } else { - stacka[a] = new ItemStack(Material.getMaterial(in[1]), - Integer.parseInt(in[2]), Short.parseShort((in[3]))); - if(in.length > 4 && !in[4].isEmpty()) { - for(int k=4;k loreList = new ArrayList(); - for (String s : lores.split("%newline%")) { - loreList.add(s); - } - stacka[a].getItemMeta().setLore(loreList); - } - a++; - } - } - } catch (final Exception e) { - e.printStackTrace(); - } finally { - if (reader != null) { - reader.close(); - } - } - return new DataFileCache(stacki, stacka, group, op, flying); + if (in[0].equals("i")) { + stacki[i] = new ItemStack(Material.getMaterial(in[1]), + Integer.parseInt(in[2]), Short.parseShort((in[3]))); + if(in.length > 4 && !in[4].isEmpty()) { + for(int k=4;k loreList = new ArrayList(); + for (String s : lores.split("%newline%")) { + loreList.add(s); + } + meta.setLore(loreList); + } + if (meta != null) + stacki[i].setItemMeta(meta); + } catch (Exception e) {} + i++; + } else { + stacka[a] = new ItemStack(Material.getMaterial(in[1]), + Integer.parseInt(in[2]), Short.parseShort((in[3]))); + if(in.length > 4 && !in[4].isEmpty()) { + for(int k=4;k loreList = new ArrayList(); + for (String s : lores.split("%newline%")) { + loreList.add(s); + } + meta.setLore(loreList); + } + if (meta != null) + stacki[i].setItemMeta(meta); + } catch (Exception e) {} + a++; + } + } + } catch (final Exception e) { + e.printStackTrace(); + } finally { + if (reader != null) { + reader.close(); + } + } + return new DataFileCache(stacki, stacka, group, op, flying); } public void removeCache(String playername) { diff --git a/src/main/java/fr/xephi/authme/commands/AdminCommand.java b/src/main/java/fr/xephi/authme/commands/AdminCommand.java index 1b36c3a2d..154a20185 100644 --- a/src/main/java/fr/xephi/authme/commands/AdminCommand.java +++ b/src/main/java/fr/xephi/authme/commands/AdminCommand.java @@ -2,7 +2,6 @@ package fr.xephi.authme.commands; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -33,12 +32,12 @@ import fr.xephi.authme.api.API; 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.converter.Converter; import fr.xephi.authme.converter.FlatToSql; import fr.xephi.authme.converter.FlatToSqlite; import fr.xephi.authme.converter.RakamakConverter; import fr.xephi.authme.converter.RoyalAuthConverter; -import fr.xephi.authme.converter.newxAuthToFlat; -import fr.xephi.authme.converter.oldxAuthToFlat; +import fr.xephi.authme.converter.xAuthConverter; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.SpawnTeleportEvent; import fr.xephi.authme.security.PasswordSecurity; @@ -301,44 +300,26 @@ public class AdminCommand implements CommandExecutor { } return true; } else if (args[0].equalsIgnoreCase("convertflattosql")) { - try { - FlatToSql converter = new FlatToSql(); - if (sender instanceof Player) { - if (converter.convert()) - sender.sendMessage("[AuthMe] FlatFile converted to authme.sql file"); - else sender.sendMessage("[AuthMe] Error while converting to authme.sql"); - } - - } catch (IOException e) { - e.printStackTrace(); - } catch (NullPointerException ex) { - System.out.println(ex.getMessage()); - } + FlatToSql converter = new FlatToSql(); + try { + converter.convert(); + } catch (Exception e) { + sender.sendMessage("[AuthMe] Error while converting to authme.sql"); + } } else if (args[0].equalsIgnoreCase("flattosqlite")) { - try { - String s = FlatToSqlite.convert(); - if (sender instanceof Player) - sender.sendMessage(s); - } catch (IOException e) { - e.printStackTrace(); - } catch (NullPointerException ex) { - System.out.println(ex.getMessage()); - } + FlatToSqlite converter = new FlatToSqlite(sender); + try { + converter.convert(); + } catch (Exception e) { + } return true; } else if (args[0].equalsIgnoreCase("xauthimport")) { - try { - Class.forName("com.cypherx.xauth.xAuth"); - oldxAuthToFlat converter = new oldxAuthToFlat(plugin, database, sender); - converter.run(); - } catch (ClassNotFoundException e) { - try { - Class.forName("de.luricos.bukkit.xAuth.xAuth"); - newxAuthToFlat converter = new newxAuthToFlat(plugin, database, sender); - converter.run(); - } catch (ClassNotFoundException ce) { - sender.sendMessage("[AuthMe] No version of xAuth found or xAuth isn't enable! "); - } - } + Converter converter = new xAuthConverter(plugin, database, sender); + try { + converter.convert(); + } catch (Exception e) { + sender.sendMessage("Error while importing xAuth data, check your logs"); + } return true; } else if (args[0].equalsIgnoreCase("getemail")) { if (args.length != 2) { @@ -373,15 +354,12 @@ public class AdminCommand implements CommandExecutor { PlayerCache.getInstance().updatePlayer(getAuth); return true; } else if (args[0].equalsIgnoreCase("convertfromrakamak")) { - try { - RakamakConverter.RakamakConvert(); - if (sender instanceof Player) - sender.sendMessage("[AuthMe] Rakamak database converted to auths.db"); - } catch (IOException e) { - e.printStackTrace(); - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); - } + Converter converter = new RakamakConverter(plugin, database, sender); + try { + converter.convert(); + } catch (Exception e) { + sender.sendMessage("Error while importing Rakamak data, check your logs"); + } return true; } else if (args[0].equalsIgnoreCase("setspawn")) { try { @@ -509,7 +487,7 @@ public class AdminCommand implements CommandExecutor { Utils.getInstance().setGroup(name, groupType.UNREGISTERED); if (target != null) { if (target.isOnline()) { - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawn = plugin.getSpawnLocation(target); SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(target, target.getLocation(), spawn, false); plugin.getServer().getPluginManager().callEvent(tpEvent); @@ -587,7 +565,7 @@ public class AdminCommand implements CommandExecutor { sender.sendMessage("Usage : /authme getip onlinePlayerName"); return true; } - if (Bukkit.getOfflinePlayer(args[1]).isOnline()) { + if (Bukkit.getPlayer(args[1]) != null) { Player player = Bukkit.getPlayer(args[1]); sender.sendMessage(player.getName() + " actual ip is : " + player.getAddress().getAddress().getHostAddress() + ":" + player.getAddress().getPort()); sender.sendMessage(player.getName() + " real ip is : " + plugin.getIP(player)); diff --git a/src/main/java/fr/xephi/authme/commands/LogoutCommand.java b/src/main/java/fr/xephi/authme/commands/LogoutCommand.java index 28aaa88e0..cccc289d5 100644 --- a/src/main/java/fr/xephi/authme/commands/LogoutCommand.java +++ b/src/main/java/fr/xephi/authme/commands/LogoutCommand.java @@ -69,7 +69,7 @@ public class LogoutCommand implements CommandExecutor { PlayerCache.getInstance().removePlayer(name); database.setUnlogged(name); - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawnLoc = plugin.getSpawnLocation(player); AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, spawnLoc); plugin.getServer().getPluginManager().callEvent(tpEvent); diff --git a/src/main/java/fr/xephi/authme/commands/UnregisterCommand.java b/src/main/java/fr/xephi/authme/commands/UnregisterCommand.java index 1998645fb..bf8e831f5 100644 --- a/src/main/java/fr/xephi/authme/commands/UnregisterCommand.java +++ b/src/main/java/fr/xephi/authme/commands/UnregisterCommand.java @@ -70,7 +70,7 @@ public class UnregisterCommand implements CommandExecutor { return true; } if(Settings.isForcedRegistrationEnabled) { - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawn = plugin.getSpawnLocation(player); SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawn, false); plugin.getServer().getPluginManager().callEvent(tpEvent); @@ -113,7 +113,7 @@ public class UnregisterCommand implements CommandExecutor { if(plugin.notifications != null) { plugin.notifications.showNotification(new Notification("[AuthMe] " + player.getName() + " unregistered himself!")); } - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawn = plugin.getSpawnLocation(player); SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawn, false); plugin.getServer().getPluginManager().callEvent(tpEvent); diff --git a/src/main/java/fr/xephi/authme/converter/Converter.java b/src/main/java/fr/xephi/authme/converter/Converter.java new file mode 100644 index 000000000..e9ccd6c9b --- /dev/null +++ b/src/main/java/fr/xephi/authme/converter/Converter.java @@ -0,0 +1,6 @@ +package fr.xephi.authme.converter; + +public interface Converter { + + void convert() throws Exception; +} diff --git a/src/main/java/fr/xephi/authme/converter/FlatToSql.java b/src/main/java/fr/xephi/authme/converter/FlatToSql.java index 389bc8b7d..359ca1e0c 100644 --- a/src/main/java/fr/xephi/authme/converter/FlatToSql.java +++ b/src/main/java/fr/xephi/authme/converter/FlatToSql.java @@ -17,7 +17,7 @@ import fr.xephi.authme.settings.Settings; * * @author Xephi59 */ -public class FlatToSql { +public class FlatToSql implements Converter { private static String tableName; private static String columnName; @@ -49,7 +49,7 @@ public class FlatToSql { columnID = Settings.getMySQLColumnId; } - public boolean convert() throws IOException { + public void convert() throws IOException { try { source = new File(AuthMe.getInstance().getDataFolder() + File.separator + "auths.db"); source.createNewFile(); @@ -93,12 +93,10 @@ public class FlatToSql { sql.close(); br.close(); ConsoleLogger.info("The FlatFile has been converted to authme.sql file"); - return true; } catch (FileNotFoundException ex) { ConsoleLogger.showError(ex.getMessage()); } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); } - return false; } } diff --git a/src/main/java/fr/xephi/authme/converter/FlatToSqlite.java b/src/main/java/fr/xephi/authme/converter/FlatToSqlite.java index 550f16939..bf6721ca1 100644 --- a/src/main/java/fr/xephi/authme/converter/FlatToSqlite.java +++ b/src/main/java/fr/xephi/authme/converter/FlatToSqlite.java @@ -12,12 +12,20 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import org.bukkit.command.CommandSender; + import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.Settings; -public class FlatToSqlite { +public class FlatToSqlite implements Converter { + + public CommandSender sender; + + public FlatToSqlite(CommandSender sender) { + this.sender = sender; + } private static String tableName; private static String columnName; @@ -34,7 +42,7 @@ public class FlatToSqlite { private static String columnID; private static Connection con; - public static String convert() throws IOException { + public void convert() throws Exception { database = Settings.getMySQLDatabase; tableName = Settings.getMySQLTablename; columnName = Settings.getMySQLColumnName; @@ -49,14 +57,16 @@ public class FlatToSqlite { columnID = Settings.getMySQLColumnId; ConsoleLogger.info("Converting FlatFile to SQLite ..."); if (new File(AuthMe.getInstance().getDataFolder() + File.separator + database + ".db").exists()) { - return "The Database " + database + ".db can't be created cause the file already exist"; + sender.sendMessage("The Database " + database + ".db can't be created cause the file already exist"); + return; } try { connect(); setup(); } catch (Exception e) { ConsoleLogger.showError("Problem while trying to convert to sqlite !"); - return "Problem while trying to convert to sqlite !"; + sender.sendMessage("Problem while trying to convert to sqlite !"); + return; } try { source = new File(AuthMe.getInstance().getDataFolder() + File.separator + "auths.db"); @@ -84,13 +94,15 @@ public class FlatToSqlite { br.close(); ConsoleLogger.info("The FlatFile has been converted to " + database + ".db file"); close(); - return ("The FlatFile has been converted to " + database + ".db file"); + sender.sendMessage("The FlatFile has been converted to " + database + ".db file"); + return; } catch (FileNotFoundException ex) { ConsoleLogger.showError(ex.getMessage()); } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); } - return "Errors appears while trying to convert to SQLite"; + sender.sendMessage("Errors appears while trying to convert to SQLite"); + return; } private synchronized static void connect() throws ClassNotFoundException, SQLException { diff --git a/src/main/java/fr/xephi/authme/converter/RakamakConverter.java b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java index aa9348e72..9386f44b1 100644 --- a/src/main/java/fr/xephi/authme/converter/RakamakConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java @@ -1,50 +1,52 @@ package fr.xephi.authme.converter; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map.Entry; +import org.bukkit.command.CommandSender; + import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.Settings; - /** * * @author Xephi59 */ -public class RakamakConverter { +public class RakamakConverter implements Converter { public AuthMe instance; + public DataSource database; + public CommandSender sender; - public RakamakConverter (AuthMe instance) { + public RakamakConverter (AuthMe instance, DataSource database, CommandSender sender) { this.instance = instance; + this.database = database; + this.sender = sender; } public RakamakConverter getInstance() { return this; } - private static HashAlgorithm hash; private static Boolean useIP; private static String fileName; private static String ipFileName; private static File source; - private static File output; private static File ipfiles; - private static boolean alreadyExist = false; - public static void RakamakConvert() throws IOException { - hash = Settings.rakamakHash; + public void convert() throws Exception { + HashAlgorithm hash = Settings.getPasswordHash; useIP = Settings.rakamakUseIp; fileName = Settings.rakamakUsers; ipFileName = Settings.rakamakUsersIp; @@ -53,19 +55,12 @@ public class RakamakConverter { try { source = new File(AuthMe.getInstance().getDataFolder() + File.separator + fileName); ipfiles = new File(AuthMe.getInstance().getDataFolder() + File.separator + ipFileName); - output = new File(AuthMe.getInstance().getDataFolder() + File.separator + "auths.db"); source.createNewFile(); ipfiles.createNewFile(); - if (new File(AuthMe.getInstance().getDataFolder() + File.separator + "auths.db").exists()) { - alreadyExist = true; - } - output.createNewFile(); BufferedReader users = null; - BufferedWriter outputDB = null; BufferedReader ipFile = null; ipFile = new BufferedReader(new FileReader(ipfiles)); String line; - String newLine = null; if (useIP) { String tempLine; while ((tempLine = ipFile.readLine()) != null) { @@ -81,33 +76,28 @@ public class RakamakConverter { if (line.contains("=")) { String[] arguments = line.split("="); try { - playerPSW.put(arguments[0],PasswordSecurity.getHash(hash, arguments[1], arguments[0].toLowerCase())); + playerPSW.put(arguments[0],PasswordSecurity.getHash(hash, arguments[1], arguments[0])); } catch (NoSuchAlgorithmException e) { ConsoleLogger.showError(e.getMessage()); } } } users.close(); - outputDB = new BufferedWriter(new FileWriter(output)); for (Entry m : playerPSW.entrySet()) { + String player = m.getKey(); + String psw = playerPSW.get(player); + String ip; if (useIP) { - String player = m.getKey(); - String psw = playerPSW.get(player); - String ip = playerIP.get(player); - newLine = player + ":" + psw + ":" + ip + ":1325376060:0:0:0:world:your@email.com"; + ip = playerIP.get(player); } else { - String player = m.getKey(); - String psw = playerPSW.get(player); - String ip = "127.0.0.1"; - newLine = player + ":" + psw + ":" + ip + ":1325376060:0:0:0:world:your@email.com"; + ip = "127.0.0.1"; } - if (alreadyExist) outputDB.newLine(); - outputDB.write(newLine); - System.out.println("Write line"); - outputDB.newLine(); + PlayerAuth auth = new PlayerAuth(player, psw, ip, System.currentTimeMillis()); + if (PasswordSecurity.userSalt.containsKey(player)) + auth.setSalt(PasswordSecurity.userSalt.get(player)); + database.saveAuth(auth); } - outputDB.close(); - ConsoleLogger.info("Rakamak database has been converted to auths.db"); + ConsoleLogger.info("Rakamak database has been imported correctly"); } catch (FileNotFoundException ex) { ConsoleLogger.showError(ex.getMessage()); } catch (IOException ex) { diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java index f5ad9d85b..b51e99267 100644 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java @@ -9,7 +9,7 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; -public class RoyalAuthConverter extends Thread { +public class RoyalAuthConverter extends Thread implements Converter { public AuthMe plugin; private DataSource data; @@ -21,23 +21,27 @@ public class RoyalAuthConverter extends Thread { } public void run() { - for (OfflinePlayer o : plugin.getServer().getOfflinePlayers()) { - try { - String name = o.getName().toLowerCase(); - String separator = File.separator; - File file = new File("." + separator + "plugins" + separator + "RoyalAuth" + separator + "userdata" + separator + name + ".yml"); - if (data.isAuthAvailable(name)) - continue; - if (!file.exists()) - continue; - RoyalAuthYamlReader ra = new RoyalAuthYamlReader(file); - PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); - data.saveAuth(auth); - } catch (Exception e) { - ConsoleLogger.showError("Error while trying to import "+ o.getName() + " RoyalAuth datas"); - } - } - this.interrupt(); } + @Override + public void convert() throws Exception { + for (OfflinePlayer o : plugin.getServer().getOfflinePlayers()) { + try { + String name = o.getName().toLowerCase(); + String separator = File.separator; + File file = new File("." + separator + "plugins" + separator + "RoyalAuth" + separator + "userdata" + separator + name + ".yml"); + if (data.isAuthAvailable(name)) + continue; + if (!file.exists()) + continue; + RoyalAuthYamlReader ra = new RoyalAuthYamlReader(file); + PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); + data.saveAuth(auth); + } catch (Exception e) { + ConsoleLogger.showError("Error while trying to import "+ o.getName() + " RoyalAuth datas"); + } + } + this.interrupt(); + } + } diff --git a/src/main/java/fr/xephi/authme/converter/xAuthConverter.java b/src/main/java/fr/xephi/authme/converter/xAuthConverter.java new file mode 100644 index 000000000..52d63d6e3 --- /dev/null +++ b/src/main/java/fr/xephi/authme/converter/xAuthConverter.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.converter; + +import org.bukkit.command.CommandSender; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.datasource.DataSource; + +public class xAuthConverter implements Converter { + + public AuthMe plugin; + public DataSource database; + public CommandSender sender; + + public xAuthConverter(AuthMe plugin, DataSource database, CommandSender sender) { + this.plugin = plugin; + this.database = database; + this.sender = sender; + } + @Override + public void convert() throws Exception { + try { + Class.forName("com.cypherx.xauth.xAuth"); + oldxAuthToFlat converter = new oldxAuthToFlat(plugin, database, sender); + converter.run(); + } catch (ClassNotFoundException e) { + try { + Class.forName("de.luricos.bukkit.xAuth.xAuth"); + newxAuthToFlat converter = new newxAuthToFlat(plugin, database, sender); + converter.run(); + } catch (ClassNotFoundException ce) { + } + } + } + +} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQLDataSource.java b/src/main/java/fr/xephi/authme/datasource/MySQLDataSource.java index 3e7a8a26f..6cbe7875a 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQLDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQLDataSource.java @@ -235,7 +235,7 @@ public class MySQLDataSource implements DataSource { PreparedStatement pst = null; try { con = makeSureConnectionIsReady(); - if ((columnSalt == null || columnSalt.isEmpty()) && (auth.getSalt() == null || auth.getSalt().isEmpty())) { + if ((columnSalt == null || columnSalt.isEmpty()) || (auth.getSalt() == null || auth.getSalt().isEmpty())) { pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + "," + columnIp + "," + columnLastLogin + ") VALUES (?,?,?,?);"); pst.setString(1, auth.getNickname()); pst.setString(2, auth.getHash()); diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index 13767bff0..a477a2f3e 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -580,7 +580,6 @@ public class AuthMePlayerListener implements Listener { long cur = new Date().getTime(); if((cur - lastLogin < timeout || timeout == 0) && !auth.getIp().equals("198.18.0.1") ) { if (auth.getNickname().equalsIgnoreCase(name) && auth.getIp().equals(ip) ) { - plugin.getServer().getPluginManager().callEvent(new SessionEvent(auth, true)); if(PlayerCache.getInstance().getAuth(name) != null) { PlayerCache.getInstance().updatePlayer(auth); } else { @@ -590,6 +589,7 @@ public class AuthMePlayerListener implements Listener { m._(player, "valid_session"); // Restore Permission Group utils.setGroup(player, Utils.groupType.LOGGEDIN); + plugin.getServer().getPluginManager().callEvent(new SessionEvent(auth, true)); return; } else if (!Settings.sessionExpireOnIpChange){ GameMode gM = gameMode.get(name); @@ -627,15 +627,16 @@ public class AuthMePlayerListener implements Listener { Utils.forceGM(player); this.causeByAuthMe = false; } - if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); - plugin.getServer().getPluginManager().callEvent(tpEvent); - if(!tpEvent.isCancelled()) { - if (player != null && player.isOnline() && tpEvent.getTo() != null) { - player.teleport(tpEvent.getTo()); - } + if (!Settings.noTeleport) + if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { + SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); + plugin.getServer().getPluginManager().callEvent(tpEvent); + if(!tpEvent.isCancelled()) { + if (player != null && player.isOnline() && tpEvent.getTo() != null) { + player.teleport(tpEvent.getTo()); + } + } } - } placePlayerSafely(player, spawnLoc); LimboCache.getInstance().updateLimboPlayer(player); DataFileCache dataFile = new DataFileCache(LimboCache.getInstance().getLimboPlayer(name).getInventory(),LimboCache.getInstance().getLimboPlayer(name).getArmour()); @@ -649,15 +650,16 @@ public class AuthMePlayerListener implements Listener { if(!Settings.unRegisteredGroup.isEmpty()){ utils.setGroup(player, Utils.groupType.UNREGISTERED); } - if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); - plugin.getServer().getPluginManager().callEvent(tpEvent); - if(!tpEvent.isCancelled()) { - if (player != null && player.isOnline() && tpEvent.getTo() != null) { - player.teleport(tpEvent.getTo()); - } + if (!Settings.noTeleport) + if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { + SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); + plugin.getServer().getPluginManager().callEvent(tpEvent); + if(!tpEvent.isCancelled()) { + if (player != null && player.isOnline() && tpEvent.getTo() != null) { + player.teleport(tpEvent.getTo()); + } + } } - } if (!Settings.isForcedRegistrationEnabled) { return; } @@ -711,13 +713,14 @@ public class AuthMePlayerListener implements Listener { player.performCommand("motd"); // Remove the join message while the player isn't logging in - if (Settings.enableProtection) { + if (Settings.enableProtection || Settings.delayJoinMessage) { joinMessage.put(name, event.getJoinMessage()); event.setJoinMessage(null); } } private void placePlayerSafely(Player player, Location spawnLoc) { + if (!Settings.noTeleport) return; if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) return; Block b = player.getLocation().getBlock(); @@ -843,6 +846,7 @@ public class AuthMePlayerListener implements Listener { ConsoleLogger.showError("Problem while restore " + name + " inventory after a kick"); } } + if (!Settings.noTeleport) try { AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, limbo.getLoc()); plugin.getServer().getPluginManager().callEvent(tpEvent); diff --git a/src/main/java/fr/xephi/authme/plugin/manager/CombatTagComunicator.java b/src/main/java/fr/xephi/authme/plugin/manager/CombatTagComunicator.java index a5c372eb8..f3c57651f 100644 --- a/src/main/java/fr/xephi/authme/plugin/manager/CombatTagComunicator.java +++ b/src/main/java/fr/xephi/authme/plugin/manager/CombatTagComunicator.java @@ -1,64 +1,39 @@ package fr.xephi.authme.plugin.manager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; + import com.trc202.CombatTag.CombatTag; import com.trc202.CombatTagApi.CombatTagApi; -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; - public abstract class CombatTagComunicator { - static CombatTagApi combatApi; + public static CombatTagApi combatApi; - public CombatTagComunicator() { - if(Bukkit.getServer().getPluginManager().getPlugin("CombatTag") != null){ - combatApi = new CombatTagApi((CombatTag)Bukkit.getServer().getPluginManager().getPlugin("CombatTag")); - } + /** + * Returns if the entity is an NPC + * @param player + * @return true if the player is an NPC + */ + public static boolean isNPC(Entity player) { + try { + if(Bukkit.getServer().getPluginManager().getPlugin("CombatTag") != null){ + combatApi = new CombatTagApi((CombatTag) Bukkit.getServer().getPluginManager().getPlugin("CombatTag")); + try { + combatApi.getClass().getMethod("isNPC"); + } catch (Exception e) { + return false; + } + return combatApi.isNPC(player); + } + } catch (ClassCastException ex) { + return false; + } catch (NullPointerException npe) { + return false; + } catch (NoClassDefFoundError ncdfe) { + return false; + } + return false; } - /** - * Checks to see if the player is in combat. The combat time can be configured by the server owner - * If the player has died while in combat the player is no longer considered in combat and as such will return false - * @param playerName - * @return true if player is in combat - */ - public abstract boolean isInCombat(String player); - - /** - * Checks to see if the player is in combat. The combat time can be configured by the server owner - * If the player has died while in combat the player is no longer considered in combat and as such will return false - * @param player - * @return true if player is in combat - */ - public abstract boolean isInCombat(Player player); - /** - * Returns the time before the tag is over - * -1 if the tag has expired - * -2 if the player is not in combat - * @param player - * @return - */ - public abstract long getRemainingTagTime(String player); - - /** - * Returns if the entity is an NPC - * @param player - * @return true if the player is an NPC - */ - public static boolean isNPC(Entity player) { - try { - if(Bukkit.getServer().getPluginManager().getPlugin("CombatTag") != null){ - combatApi = new CombatTagApi((CombatTag) Bukkit.getServer().getPluginManager().getPlugin("CombatTag")); - return combatApi.isNPC(player); - } - } catch (ClassCastException ex) { - return false; - } catch (NullPointerException npe) { - return false; - } catch (NoClassDefFoundError ncdfe) { - return false; - } - return false; - } } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java index 9da361747..49ee068a5 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncronousPlayerLogin.java @@ -132,36 +132,24 @@ public class ProcessSyncronousPlayerLogin implements Runnable { player.setGameMode(GameMode.SURVIVAL); } - // Teleport the player - if(Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { - // If we have force the spawn location on join - teleportToSpawn(); - } else { - if (Settings.isTeleportToSpawnEnabled) { - // If and only if teleport unauthed to spawn is activate - teleportBackFromSpawn(); - } else { - if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { - // Teleport the player on the saved location - packQuitLocation(); - } else { - // Do not move the player from his position - } - } - } - // Teleport - if (Settings.isTeleportToSpawnEnabled && !Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { - if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { - packQuitLocation(); + if (!Settings.noTeleport) { + // Teleport the player + if(Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { + // If we have force the spawn location on join + teleportToSpawn(); } else { - teleportBackFromSpawn(); + if (Settings.isTeleportToSpawnEnabled) { + // If and only if teleport unauthed to spawn is activate + teleportBackFromSpawn(); + } else { + if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { + // Teleport the player on the saved location + packQuitLocation(); + } else { + // Do not move the player from his position + } + } } - } else if (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { - teleportToSpawn(); - } else if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { - packQuitLocation(); - } else { - teleportBackFromSpawn(); } // Re-Force Survival GameMode if we need due to world change specification diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousEmailRegister.java index eabf68dfc..fa42713cc 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousEmailRegister.java @@ -46,7 +46,7 @@ public class ProcessSyncronousEmailRegister implements Runnable { int nwMsg = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new MessageTask(plugin, name, m._("login_msg"), msgInterval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nwMsg); - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location loca = plugin.getSpawnLocation(player); RegisterTeleportEvent tpEvent = new RegisterTeleportEvent(player, loca); plugin.getServer().getPluginManager().callEvent(tpEvent); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousPasswordRegister.java index 8faac8154..5730c28f7 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncronousPasswordRegister.java @@ -42,7 +42,7 @@ public class ProcessSyncronousPasswordRegister implements Runnable { } protected void forceLogin(Player player) { - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawnLoc = plugin.getSpawnLocation(player); AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, spawnLoc); plugin.getServer().getPluginManager().callEvent(tpEvent); @@ -78,7 +78,7 @@ public class ProcessSyncronousPasswordRegister implements Runnable { LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); if (limbo != null) { player.setGameMode(limbo.getGameMode()); - if (Settings.isTeleportToSpawnEnabled) { + if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location loca = plugin.getSpawnLocation(player); RegisterTeleportEvent tpEvent = new RegisterTeleportEvent(player, loca); plugin.getServer().getPluginManager().callEvent(tpEvent); diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java index f75780b10..b0795136e 100644 --- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java +++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java @@ -21,11 +21,13 @@ public enum HashAlgorithm { JOOMLA(fr.xephi.authme.security.crypts.JOOMLA.class), BCRYPT(fr.xephi.authme.security.crypts.BCRYPT.class), WBB3(fr.xephi.authme.security.crypts.WBB3.class), + WBB4(fr.xephi.authme.security.crypts.WBB4.class), SHA512(fr.xephi.authme.security.crypts.SHA512.class), DOUBLEMD5(fr.xephi.authme.security.crypts.DOUBLEMD5.class), PBKDF2(fr.xephi.authme.security.crypts.CryptPBKDF2.class), WORDPRESS(fr.xephi.authme.security.crypts.WORDPRESS.class), ROYALAUTH(fr.xephi.authme.security.crypts.ROYALAUTH.class), + CRAZYCRYPT1(fr.xephi.authme.security.crypts.CRAZYCRYPT1.class), CUSTOM(Null.class); Class classe; diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index fb6726996..13826c014 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -80,12 +80,16 @@ public class PasswordSecurity { salt = createSalt(40); userSalt.put(playerName, salt); break; + case WBB4: + salt = BCRYPT.gensalt(8); + userSalt.put(playerName, salt); + break; case PBKDF2: salt = createSalt(12); userSalt.put(playerName, salt); break; case SMF: - return method.getHash(password, playerName.toLowerCase()); + return method.getHash(password, null, playerName); case PHPBB: salt = createSalt(16); userSalt.put(playerName, salt); @@ -96,6 +100,8 @@ public class PasswordSecurity { case PLAINTEXT: case XENFORO: case SHA512: + case ROYALAUTH: + case CRAZYCRYPT1: case DOUBLEMD5: case WORDPRESS: case CUSTOM: @@ -108,7 +114,7 @@ public class PasswordSecurity { method = event.getMethod(); if (method == null) throw new NoSuchAlgorithmException("Unknown hash algorithm"); - return method.getHash(password, salt); + return method.getHash(password, salt, playerName); } public static boolean comparePasswordWithHash(String password, String hash, String playerName) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java index da64eb131..ab4702443 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java @@ -750,8 +750,35 @@ public class BCRYPT implements EncryptionMethod { return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); } + /** + * Check that a text password matches a previously hashed + * one with the specified number of rounds using recursion + * + * @param text plaintext or hashed text + * @param hashed the previously-hashed password + * @param rounds number of rounds to hash the password + * @return + */ + public static boolean checkpw(String text, String hashed, int rounds) { + boolean matched = false; + + if (rounds > 0) { + String hash = hashpw(text, hashed); + + if (rounds > 1) { + matched = checkpw(hash, hashed, rounds - 1); + } else { + matched = hash.compareTo(hashed) == 0; + } + } else { + matched = text.compareTo(hashed) == 0; + } + + return matched; + } + @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return hashpw(password, salt); } @@ -761,4 +788,9 @@ public class BCRYPT implements EncryptionMethod { String playerName) throws NoSuchAlgorithmException { return checkpw(password, hash); } + + public static String getDoubleHash(String text, String salt) { + String hash = hashpw(text, salt); + return hashpw(text, hash); + } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java new file mode 100644 index 000000000..8bd4d83bd --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java @@ -0,0 +1,44 @@ +package fr.xephi.authme.security.crypts; + +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class CRAZYCRYPT1 implements EncryptionMethod { + + protected final Charset charset = Charset.forName("UTF-8"); + private static final char[] CRYPTCHARS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + @Override + public String getHash(String password, String salt, String name) + throws NoSuchAlgorithmException { + final String text = "ÜÄaeut//&/=I " + password + "7421€547" + name + "__+IÄIH§%NK " + password; + try + { + final MessageDigest md = MessageDigest.getInstance("SHA-512"); + md.update(text.getBytes(charset), 0, text.length()); + return byteArrayToHexString(md.digest()); + } + catch (final NoSuchAlgorithmException e) + { + return null; + } + } + + @Override + public boolean comparePassword(String hash, String password, + String playerName) throws NoSuchAlgorithmException { + return hash.equals(getHash(password, null, playerName)); + } + + public static String byteArrayToHexString(final byte... args) + { + final char[] chars = new char[args.length * 2]; + for (int i = 0; i < args.length; i++) { + chars[i * 2] = CRYPTCHARS[(args[i] >> 4) & 0xF]; + chars[i * 2 + 1] = CRYPTCHARS[(args[i]) & 0xF]; + } + return new String(chars); + } + +} diff --git a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java b/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java index 78f09c3d4..f2673f737 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java +++ b/src/main/java/fr/xephi/authme/security/crypts/CryptPBKDF2.java @@ -9,7 +9,7 @@ import fr.xephi.authme.security.pbkdf2.PBKDF2Parameters; public class CryptPBKDF2 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { String result = "pbkdf2_sha256$10000$"+salt+"$"; PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "ASCII", salt.getBytes(), 10000); diff --git a/src/main/java/fr/xephi/authme/security/crypts/DOUBLEMD5.java b/src/main/java/fr/xephi/authme/security/crypts/DOUBLEMD5.java index 7ac3c0fc0..e4c3caaf5 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/DOUBLEMD5.java +++ b/src/main/java/fr/xephi/authme/security/crypts/DOUBLEMD5.java @@ -7,7 +7,7 @@ import java.security.NoSuchAlgorithmException; public class DOUBLEMD5 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(getMD5(password)); } @@ -15,7 +15,7 @@ public class DOUBLEMD5 implements EncryptionMethod { @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, "")); + return hash.equals(getHash(password, "", "")); } private static String getMD5(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java b/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java index d320eaa82..d5cd9562e 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java +++ b/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java @@ -16,7 +16,7 @@ public interface EncryptionMethod { * @return Hashing password * @throws NoSuchAlgorithmException */ - String getHash(String password, String salt) throws NoSuchAlgorithmException; + String getHash(String password, String salt, String name) throws NoSuchAlgorithmException; /** * @param hash diff --git a/src/main/java/fr/xephi/authme/security/crypts/IPB3.java b/src/main/java/fr/xephi/authme/security/crypts/IPB3.java index f350e0815..1f9443367 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/IPB3.java +++ b/src/main/java/fr/xephi/authme/security/crypts/IPB3.java @@ -10,7 +10,7 @@ import fr.xephi.authme.AuthMe; public class IPB3 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(getMD5(salt) + getMD5(password)); } @@ -19,7 +19,7 @@ public class IPB3 implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt(); - return hash.equals(getHash(password, salt)); + return hash.equals(getHash(password, salt, playerName)); } private static String getMD5(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java b/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java index 0fdb95dd9..9b20f35c1 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java +++ b/src/main/java/fr/xephi/authme/security/crypts/JOOMLA.java @@ -7,7 +7,7 @@ import java.security.NoSuchAlgorithmException; public class JOOMLA implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(password + salt) + ":" + salt; } diff --git a/src/main/java/fr/xephi/authme/security/crypts/MD5.java b/src/main/java/fr/xephi/authme/security/crypts/MD5.java index 33e4cf5e8..9fd2d786d 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/MD5.java +++ b/src/main/java/fr/xephi/authme/security/crypts/MD5.java @@ -7,13 +7,13 @@ import java.security.NoSuchAlgorithmException; public class MD5 implements EncryptionMethod { @Override - public String getHash(String password, String salt) throws NoSuchAlgorithmException { + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(password); } @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, "")); + return hash.equals(getHash(password, "", "")); } private static String getMD5(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/MD5VB.java b/src/main/java/fr/xephi/authme/security/crypts/MD5VB.java index 1ec310758..8b6df78a0 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/MD5VB.java +++ b/src/main/java/fr/xephi/authme/security/crypts/MD5VB.java @@ -7,7 +7,7 @@ import java.security.NoSuchAlgorithmException; public class MD5VB implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return "$MD5vb$" + salt + "$" + getMD5(getMD5(password) + salt); } @@ -16,7 +16,7 @@ public class MD5VB implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String[] line = hash.split("\\$"); - return hash.equals(getHash(password, line[2])); + return hash.equals(getHash(password, line[2], "")); } private static String getMD5(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/MYBB.java b/src/main/java/fr/xephi/authme/security/crypts/MYBB.java index 1ea28b464..9bd50bf7d 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/MYBB.java +++ b/src/main/java/fr/xephi/authme/security/crypts/MYBB.java @@ -10,7 +10,7 @@ import fr.xephi.authme.AuthMe; public class MYBB implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(getMD5(salt)+ getMD5(password)); } @@ -19,7 +19,7 @@ public class MYBB implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt(); - return hash.equals(getHash(password, salt)); + return hash.equals(getHash(password, salt, playerName)); } private static String getMD5(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/PHPBB.java b/src/main/java/fr/xephi/authme/security/crypts/PHPBB.java index f85ff0215..b965a7b42 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/PHPBB.java +++ b/src/main/java/fr/xephi/authme/security/crypts/PHPBB.java @@ -151,7 +151,7 @@ private String _hash_gensalt_private( } @Override -public String getHash(String password, String salt) +public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return phpbb_hash(password, salt); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/PHPFUSION.java b/src/main/java/fr/xephi/authme/security/crypts/PHPFUSION.java index b88a911e8..e74b45fdf 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/PHPFUSION.java +++ b/src/main/java/fr/xephi/authme/security/crypts/PHPFUSION.java @@ -15,7 +15,7 @@ import fr.xephi.authme.AuthMe; public class PHPFUSION implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { String digest = null; String algo = "HmacSHA256"; @@ -45,7 +45,7 @@ public class PHPFUSION implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt(); - return hash.equals(getHash(password, salt)); + return hash.equals(getHash(password, salt, "")); } private static String getSHA1(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java b/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java index c69cada41..4562d7d54 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/PLAINTEXT.java @@ -5,7 +5,7 @@ import java.security.NoSuchAlgorithmException; public class PLAINTEXT implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return password; } diff --git a/src/main/java/fr/xephi/authme/security/crypts/ROYALAUTH.java b/src/main/java/fr/xephi/authme/security/crypts/ROYALAUTH.java index 675cbd99c..368514bbf 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/ROYALAUTH.java +++ b/src/main/java/fr/xephi/authme/security/crypts/ROYALAUTH.java @@ -6,20 +6,26 @@ import java.security.NoSuchAlgorithmException; public class ROYALAUTH implements EncryptionMethod { @Override - public String getHash(String password, String salt) - throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("SHA-512"); - md.update(password.getBytes()); - byte byteData[] = md.digest(); - StringBuilder sb = new StringBuilder(); - for (byte aByteData : byteData) sb.append(Integer.toString((aByteData & 0xff) + 0x100, 16).substring(1)); - return sb.toString(); + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { + String data = ""; + for (int i = 0; i < 25; i++) + data = hash(data, salt); + return data; + } + + public String hash(String password, String salt) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-512"); + md.update(password.getBytes()); + byte byteData[] = md.digest(); + StringBuilder sb = new StringBuilder(); + for (byte aByteData : byteData) + sb.append(Integer.toString((aByteData & 0xff) + 0x100, 16).substring(1)); + return sb.toString(); } @Override - public boolean comparePassword(String hash, String password, - String playerName) throws NoSuchAlgorithmException { - return hash.equalsIgnoreCase(getHash(password, "")); + public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { + return hash.equalsIgnoreCase(getHash(password, "", "")); } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java b/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java index bc1266d24..326f1f74e 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SALTED2MD5.java @@ -10,7 +10,7 @@ import fr.xephi.authme.AuthMe; public class SALTED2MD5 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getMD5(getMD5(password) + salt); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/SHA1.java b/src/main/java/fr/xephi/authme/security/crypts/SHA1.java index 056f9aa12..79d8b6f2a 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SHA1.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SHA1.java @@ -7,14 +7,14 @@ import java.security.NoSuchAlgorithmException; public class SHA1 implements EncryptionMethod { @Override - public String getHash(String password, String salt) throws NoSuchAlgorithmException { + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getSHA1(password); } @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, "")); + return hash.equals(getHash(password, "", "")); } private static String getSHA1(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/SHA256.java b/src/main/java/fr/xephi/authme/security/crypts/SHA256.java index e1808fbc7..6b5d08b1b 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SHA256.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SHA256.java @@ -7,7 +7,7 @@ import java.security.NoSuchAlgorithmException; public class SHA256 implements EncryptionMethod { @Override - public String getHash(String password, String salt) throws NoSuchAlgorithmException { + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return "$SHA$" + salt + "$" + getSHA256(getSHA256(password) + salt); } @@ -15,7 +15,7 @@ public class SHA256 implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String[] line = hash.split("\\$"); - return hash.equals(getHash(password, line[2])); + return hash.equals(getHash(password, line[2], "")); } private static String getSHA256(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/SHA512.java b/src/main/java/fr/xephi/authme/security/crypts/SHA512.java index eb69a060a..738ce5c22 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SHA512.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SHA512.java @@ -7,7 +7,7 @@ import java.security.NoSuchAlgorithmException; public class SHA512 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getSHA512(password); } @@ -15,7 +15,7 @@ public class SHA512 implements EncryptionMethod { @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, "")); + return hash.equals(getHash(password, "", "")); } private static String getSHA512(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/SMF.java b/src/main/java/fr/xephi/authme/security/crypts/SMF.java index 16d726fc4..6941b79d6 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/SMF.java +++ b/src/main/java/fr/xephi/authme/security/crypts/SMF.java @@ -7,15 +7,15 @@ import java.security.NoSuchAlgorithmException; public class SMF implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { - return getSHA1(salt.toLowerCase() + password); + return getSHA1(name.toLowerCase() + password); } @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, playerName.toLowerCase())); + return hash.equals(getHash(password, null, playerName)); } private static String getSHA1(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/WBB3.java b/src/main/java/fr/xephi/authme/security/crypts/WBB3.java index 8a0a0e4d6..7969df0b7 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/WBB3.java +++ b/src/main/java/fr/xephi/authme/security/crypts/WBB3.java @@ -10,7 +10,7 @@ import fr.xephi.authme.AuthMe; public class WBB3 implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getSHA1(salt.concat(getSHA1(salt.concat(getSHA1(password))))); } @@ -19,7 +19,7 @@ public class WBB3 implements EncryptionMethod { public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { String salt = AuthMe.getInstance().database.getAuth(playerName).getSalt(); - return hash.equals(getHash(password, salt)); + return hash.equals(getHash(password, salt, "")); } private static String getSHA1(String message) throws NoSuchAlgorithmException { diff --git a/src/main/java/fr/xephi/authme/security/crypts/WBB4.java b/src/main/java/fr/xephi/authme/security/crypts/WBB4.java new file mode 100644 index 000000000..895e52f40 --- /dev/null +++ b/src/main/java/fr/xephi/authme/security/crypts/WBB4.java @@ -0,0 +1,19 @@ +package fr.xephi.authme.security.crypts; + +import java.security.NoSuchAlgorithmException; + +public class WBB4 implements EncryptionMethod { + + @Override + public String getHash(String password, String salt, String name) + throws NoSuchAlgorithmException { + return BCRYPT.getDoubleHash(password, salt); + } + + @Override + public boolean comparePassword(String hash, String password, + String playerName) throws NoSuchAlgorithmException { + return BCRYPT.checkpw(password, hash, 2); + } + +} diff --git a/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java b/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java index d82b48406..8a58498f4 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java +++ b/src/main/java/fr/xephi/authme/security/crypts/WHIRLPOOL.java @@ -408,7 +408,7 @@ public class WHIRLPOOL implements EncryptionMethod { } @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { byte[] digest = new byte[DIGESTBYTES]; NESSIEinit(); @@ -420,6 +420,6 @@ public class WHIRLPOOL implements EncryptionMethod { @Override public boolean comparePassword(String hash, String password, String playerName) throws NoSuchAlgorithmException { - return hash.equals(getHash(password, "")); + return hash.equals(getHash(password, "", "")); } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/WORDPRESS.java b/src/main/java/fr/xephi/authme/security/crypts/WORDPRESS.java index ebd9772af..3a525c8ba 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/WORDPRESS.java +++ b/src/main/java/fr/xephi/authme/security/crypts/WORDPRESS.java @@ -99,7 +99,7 @@ public class WORDPRESS implements EncryptionMethod { } @Override - public String getHash(String password, String salt) throws NoSuchAlgorithmException { + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { byte random[] = new byte[6]; this.randomGen.nextBytes(random); return crypt(password, gensaltPrivate(stringToUtf8(new String(random)))); diff --git a/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java b/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java index 59af0eb9d..2e4e7ba07 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XAUTH.java @@ -5,7 +5,7 @@ import java.security.NoSuchAlgorithmException; public class XAUTH implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { String hash = getWhirlpool(salt + password).toLowerCase(); int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length()); @@ -17,7 +17,7 @@ public class XAUTH implements EncryptionMethod { String playerName) throws NoSuchAlgorithmException { int saltPos = (password.length() >= hash.length() ? hash.length() - 1 : password.length()); String salt = hash.substring(saltPos, saltPos + 12); - return hash.equals(getHash(password, salt)); + return hash.equals(getHash(password, salt, "")); } public static String getWhirlpool(String message) { diff --git a/src/main/java/fr/xephi/authme/security/crypts/XF.java b/src/main/java/fr/xephi/authme/security/crypts/XF.java index d522a413b..9d954a369 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XF.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XF.java @@ -13,7 +13,7 @@ import fr.xephi.authme.AuthMe; public class XF implements EncryptionMethod { @Override - public String getHash(String password, String salt) + public String getHash(String password, String salt, String name) throws NoSuchAlgorithmException { return getSHA256(getSHA256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt)); } diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 5eb5b57b2..6065777ad 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -43,7 +43,6 @@ public final class Settings extends YamlConfiguration { private final File file; public static DataSourceType getDataSource; public static HashAlgorithm getPasswordHash; - public static HashAlgorithm rakamakHash; public static Boolean useLogging = false; public static int purgeDelay = 60; public static List welcomeMsg = null; @@ -60,7 +59,8 @@ public final class Settings extends YamlConfiguration { disableSocialSpy, useMultiThreading, forceOnlyAfterLogin, useEssentialsMotd, usePurge, purgePlayerDat, purgeEssentialsFile, supportOldPassword, purgeLimitedCreative, purgeAntiXray, purgePermissions, enableProtection, enableAntiBot, recallEmail, useWelcomeMessage, - broadcastWelcomeMessage, forceRegKick, forceRegLogin, checkVeryGames; + broadcastWelcomeMessage, forceRegKick, forceRegLogin, checkVeryGames, delayJoinMessage, + noTeleport; public static String getNickRegex, getUnloggedinGroup, getMySQLHost, getMySQLPort, getMySQLUsername, getMySQLPassword, getMySQLDatabase, getMySQLTablename, @@ -178,7 +178,6 @@ public void loadConfigOptions() { rakamakUsers = configFile.getString("Converter.Rakamak.fileName", "users.rak"); rakamakUsersIp = configFile.getString("Converter.Rakamak.ipFileName", "UsersIp.rak"); rakamakUseIp = configFile.getBoolean("Converter.Rakamak.useIp", false); - rakamakHash = getRakamakHash(); noConsoleSpam = configFile.getBoolean("Security.console.noConsoleSpam", false); removePassword = configFile.getBoolean("Security.console.removePassword", true); getmailAccount = configFile.getString("Email.mailAccount", ""); @@ -244,6 +243,8 @@ public void loadConfigOptions() { getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); + delayJoinMessage = configFile.getBoolean("settings.delayJoinMessage", false); + noTeleport = configFile.getBoolean("settings.restrictions.noTeleport", false); // Load the welcome message getWelcomeMessage(plugin); @@ -337,7 +338,6 @@ public static void reloadConfigOptions(YamlConfiguration newConfig) { rakamakUsers = configFile.getString("Converter.Rakamak.fileName", "users.rak"); rakamakUsersIp = configFile.getString("Converter.Rakamak.ipFileName", "UsersIp.rak"); rakamakUseIp = configFile.getBoolean("Converter.Rakamak.useIp", false); - rakamakHash = getRakamakHash(); noConsoleSpam = configFile.getBoolean("Security.console.noConsoleSpam", false); removePassword = configFile.getBoolean("Security.console.removePassword", true); getmailAccount = configFile.getString("Email.mailAccount", ""); @@ -403,6 +403,8 @@ public static void reloadConfigOptions(YamlConfiguration newConfig) { getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); + delayJoinMessage = configFile.getBoolean("settings.delayJoinMessage", false); + noTeleport = configFile.getBoolean("settings.restrictions.noTeleport", false); // Reload the welcome message getWelcomeMessage(AuthMe.getInstance()); @@ -505,10 +507,20 @@ public static void reloadConfigOptions(YamlConfiguration newConfig) { } if(getString("settings.restrictions.allowedNicknameCharacters").equals("[a-zA-Z0-9_?]*")) set("settings.restrictions.allowedNicknameCharacters", "[a-zA-Z0-9_]*"); + if(!contains("settings.delayJoinMessage")) { + set("settings.delayJoinMessage", false); + changes = true; + } + if(!contains("settings.restrictions.noTeleport")) { + set("settings.restrictions.noTeleport", false); + changes = true; + } + if(contains("Converter.Rakamak.newPasswordHash")) + set("Converter.Rakamak.newPasswordHash", null); if (changes) { - plugin.getLogger().warning("Merge new Config Options if needed.."); - plugin.getLogger().warning("Please check your config.yml file!"); + plugin.getLogger().warning("Merge new Config Options - I'm not an error, please don't report me"); + plugin.getLogger().warning("Please check your config.yml file for new configs!"); } plugin.saveConfig(); @@ -525,17 +537,6 @@ public static void reloadConfigOptions(YamlConfiguration newConfig) { } } - private static HashAlgorithm getRakamakHash() { - String key = "Converter.Rakamak.newPasswordHash"; - - try { - return HashAlgorithm.valueOf(configFile.getString(key,"SHA256").toUpperCase()); - } catch (IllegalArgumentException ex) { - ConsoleLogger.showError("Unknown Hash Algorithm; defaulting to SHA256"); - return HashAlgorithm.SHA256; - } - } - private static DataSourceType getDataSource() { String key = "DataSource.backend"; try { diff --git a/src/main/java/fr/xephi/authme/threads/MySQLThread.java b/src/main/java/fr/xephi/authme/threads/MySQLThread.java index 53f498d5a..59a130624 100644 --- a/src/main/java/fr/xephi/authme/threads/MySQLThread.java +++ b/src/main/java/fr/xephi/authme/threads/MySQLThread.java @@ -264,7 +264,7 @@ public class MySQLThread extends Thread implements DataSource { PreparedStatement pst = null; try { con = makeSureConnectionIsReady(); - if ((columnSalt == null || columnSalt.isEmpty()) && (auth.getSalt() == null || auth.getSalt().isEmpty())) { + if ((columnSalt == null || columnSalt.isEmpty()) || (auth.getSalt() == null || auth.getSalt().isEmpty())) { pst = con.prepareStatement("INSERT INTO " + tableName + "(" + columnName + "," + columnPassword + "," + columnIp + "," + columnLastLogin + ") VALUES (?,?,?,?);"); pst.setString(1, auth.getNickname()); pst.setString(2, auth.getHash()); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 744878b91..70a991e66 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -162,6 +162,8 @@ settings: maxLoginPerIp: 0 # Maximum Join authorized by IP maxJoinPerIp: 0 + # AuthMe will NEVER teleport players ! + noTeleport: false GameMode: # ForceSurvivalMode to player when join ? ForceSurvivalMode: false @@ -254,6 +256,8 @@ settings: useWelcomeMessage: true # Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player broadcastWelcomeMessage: false + # Do we need to delay the X has joined the game after /login ? + delayJoinMessage: false ExternalBoardOptions: # MySQL column for the salt , needed for some forum/cms support mySQLColumnSalt: '' @@ -325,11 +329,6 @@ Converter: useIP: false # IP file name for rakamak ipFileName: UsersIp.rak - # possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, - # PLAINTEXT ( unhashed password), - # MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, - # DOUBLEMD5, PBKDF2, WORDPRESS, CUSTOM(for developpers only) - newPasswordHash: SHA256 Email: # Email SMTP server host mailSMTP: smtp.gmail.com diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e4092e345..13b85402b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ author: Xephi59 website: http://dev.bukkit.org/bukkit-plugins/authme-reloaded/ description: AuthMe prevents people, which aren't logged in, from doing stuff like placing blocks, moving, typing commands or seeing the inventory of the current player. main: fr.xephi.authme.AuthMe -version: 3.3.6 +version: 3.4 softdepend: [Vault, ChestShop, Spout, Multiverse-Core, Notifications, Citizens, CombatTag, Essentials, EssentialsSpawn] commands: register: