diff --git a/src/main/java/me/StevenLawson/TotalFreedomMod/manager/UUIDManager.java b/src/main/java/me/StevenLawson/TotalFreedomMod/manager/UUIDManager.java new file mode 100644 index 0000000..41f2d52 --- /dev/null +++ b/src/main/java/me/StevenLawson/TotalFreedomMod/manager/UUIDManager.java @@ -0,0 +1,288 @@ +package me.StevenLawson.TotalFreedomMod.manager; + +import com.google.common.collect.ImmutableList; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; +import me.StevenLawson.TotalFreedomMod.Log; +import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; +import me.StevenLawson.TotalFreedomMod.player.PlayerData; +import me.StevenLawson.TotalFreedomMod.sql.SQLiteDatabase; +import me.StevenLawson.TotalFreedomMod.sql.SQLiteDatabase.Statement; +import me.StevenLawson.TotalFreedomMod.util.SQLUtil; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +public class UUIDManager { + + public static final String TABLE_NAME = "uuids"; + private static final SQLiteDatabase SQL; + private static final Statement FIND; + private static final Statement UPDATE; + + private UUIDManager() { + throw new AssertionError(); + } + + static { + SQL = new SQLiteDatabase( + "uuids.db", + TABLE_NAME, + "username VARCHAR(" + TotalFreedomMod.MAX_USERNAME_LENGTH + + ") NOT NULL PRIMARY KEY, uuid CHAR(36) NOT NULL"); + + FIND = SQL.addPreparedStatement( + "SELECT * FROM " + TABLE_NAME + " WHERE lower(username) = ?;"); + UPDATE = SQL.addPreparedStatement( + "REPLACE INTO " + TABLE_NAME + " (username, uuid) VALUES (?, ?);"); + } + + public static void load() { + // Init DB + SQL.connect(); + } + + public static void close() { + SQL.close(); + } + + public static int purge() { + return SQL.purge(); + } + + public static UUID newPlayer(Player player, String ip) { + Log.info("Obtaining UUID for new player: " + player.getName()); + + final String username = player.getName().toLowerCase(); + + // Look in DB + final UUID dbUuid = find(username); + if (dbUuid != null) { + return dbUuid; + } + + // Find UUID and update in DB if not found + // Try API + UUID uuid = TFM_UuidResolver.getUUIDOf(username); + if (uuid == null) { + // Spoof + uuid = generateSpoofUuid(username); + } + + update(username, uuid); + return uuid; + } + + private static UUID generateOfflineUUID(String username) { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes()); + } + + public static UUID getUniqueId(OfflinePlayer offlinePlayer) { + // Online check first + if (offlinePlayer.isOnline() && PlayerData.hasPlayerData(offlinePlayer.getPlayer())) { + return PlayerData.getPlayerData(offlinePlayer.getPlayer()).getUniqueId(); + } + + // If the offline player's uuid isn't offline, just return that. + if (!offlinePlayer.getUniqueId().equals(generateOfflineUUID(offlinePlayer.getName()))) { + return offlinePlayer.getUniqueId(); + } + + // DB, API, Spoof + return getUniqueId(offlinePlayer.getName()); + } + + public static UUID getUniqueId(String username) { + // Use Bukkit + final Player player = Bukkit.getPlayerExact(username); + if (player != null) { + return player.getUniqueId(); + } + + // Look in DB + final UUID dbUuid = find(username); + if (dbUuid != null) { + return dbUuid; + } + + // Try API + final UUID apiUuid = TFM_UuidResolver.getUUIDOf(username); + if (apiUuid != null) { + return apiUuid; + } + + // Spoof + return generateSpoofUuid(username); + } + + public static void rawSetUUID(String name, UUID uuid) { + if (name == null || uuid == null || name.isEmpty()) { + Log.warning("Not setting raw UUID: name and uuid may not be null!"); + return; + } + + update(name.toLowerCase().trim(), uuid); + } + + private static UUID find(String searchName) { + if (!SQL.connect()) { + return null; + } + + final ResultSet result; + try { + final PreparedStatement statement = FIND.getStatement(); + statement.clearParameters(); + statement.setString(1, searchName.toLowerCase()); + result = statement.executeQuery(); + } catch (Exception ex) { + Log.severe("Could not execute find statement!"); + Log.severe(ex); + return null; + } + + if (!SQLUtil.hasData(result)) { + SQLUtil.close(result); + return null; + } + + try { + final String uuidString = result.getString("uuid"); + return UUID.fromString(uuidString); + } catch (Exception ex) { + Log.severe(ex); + return null; + } finally { + SQLUtil.close(result); + } + } + + private static boolean update(String username, UUID uuid) { + if (!SQL.connect()) { + return false; + } + + try { + final PreparedStatement statement = UPDATE.getStatement(); + statement.clearParameters(); + statement.setString(1, username.toLowerCase()); + statement.setString(2, uuid.toString()); + statement.executeUpdate(); + return true; + } catch (Exception ex) { + Log.severe("Could not execute update statement!"); + Log.severe(ex); + return false; + } + } + + private static UUID generateSpoofUuid(String name) { + name = name.toLowerCase(); + Log.info("Generating spoof UUID for " + name); + + try { + final MessageDigest digest = MessageDigest.getInstance("SHA256"); + final byte[] result = digest.digest(name.getBytes()); + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < result.length; i++) { + builder.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1)); + } + + return UUID.fromString( + "deadbeef" + + "-" + builder.substring(8, 12) + + "-" + builder.substring(12, 16) + + "-" + builder.substring(16, 20) + + "-" + builder.substring(20, 32)); + } catch (NoSuchAlgorithmException ex) { + Log.warning("Could not generate spoof UUID: SHA1 algorithm not found!"); + } + + return UUID.randomUUID(); + } + + public static class TFM_UuidResolver implements Callable> { + + private static final double PROFILES_PER_REQUEST = 100; + private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; + private final JSONParser jsonParser = new JSONParser(); + private final List names; + + public TFM_UuidResolver(List names) { + this.names = ImmutableList.copyOf(names); + } + + @Override + public Map call() { + final Map uuidMap = new HashMap(); + int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); + for (int i = 0; i < requests; i++) { + try { + final URL url = new URL(PROFILE_URL); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + final String body = JSONArray.toJSONString( + names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); + + final OutputStream stream = connection.getOutputStream(); + stream.write(body.getBytes()); + stream.flush(); + stream.close(); + + final JSONArray array = (JSONArray) jsonParser.parse( + new InputStreamReader(connection.getInputStream())); + + for (Object profile : array) { + final JSONObject jsonProfile = (JSONObject) profile; + final String id = (String) jsonProfile.get("id"); + final String name = (String) jsonProfile.get("name"); + final UUID uuid = UUID.fromString( + id.substring(0, 8) + + "-" + id.substring(8, 12) + + "-" + id.substring(12, 16) + + "-" + id.substring(16, 20) + + "-" + id.substring(20, 32)); + uuidMap.put(name, uuid); + } + + if (i != requests - 1) { + Thread.sleep(100L); + } + } catch (Exception ex) { + Log.severe("Could not resolve UUID(s) of " + + StringUtils.join( + names.subList(i * 100, Math.min((i + 1) * 100, names.size())), ", ")); + //Log.severe(ex); + } + } + return uuidMap; + } + + public static UUID getUUIDOf(String name) { + return new TFM_UuidResolver(Arrays.asList(name)).call().get(name); + } + } + +} diff --git a/src/main/java/me/StevenLawson/TotalFreedomMod/player/UUIDManager.java b/src/main/java/me/StevenLawson/TotalFreedomMod/player/UUIDManager.java deleted file mode 100644 index 36978ba..0000000 --- a/src/main/java/me/StevenLawson/TotalFreedomMod/player/UUIDManager.java +++ /dev/null @@ -1,262 +0,0 @@ -package me.StevenLawson.TotalFreedomMod.player; - -import com.google.common.collect.ImmutableList; -import me.StevenLawson.TotalFreedomMod.Log; -import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; -import me.StevenLawson.TotalFreedomMod.sql.SQLiteDatabase; -import me.StevenLawson.TotalFreedomMod.sql.SQLiteDatabase.Statement; -import me.StevenLawson.TotalFreedomMod.util.SQLUtil; -import org.apache.commons.lang3.StringUtils; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; - -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.*; -import java.util.concurrent.Callable; - -public class UUIDManager { - - public static final String TABLE_NAME = "uuids"; - private static final SQLiteDatabase SQL; - private static final Statement FIND; - private static final Statement UPDATE; - - private UUIDManager() { - throw new AssertionError(); - } - - static { - SQL = new SQLiteDatabase( - "uuids.db", - TABLE_NAME, - "username VARCHAR(" + TotalFreedomMod.MAX_USERNAME_LENGTH + ") NOT NULL PRIMARY KEY, uuid CHAR(36) NOT NULL"); - - FIND = SQL.addPreparedStatement("SELECT * FROM " + TABLE_NAME + " WHERE lower(username) = ?;"); - UPDATE = SQL.addPreparedStatement("REPLACE INTO " + TABLE_NAME + " (username, uuid) VALUES (?, ?);"); - } - - public static void load() { - // Init DB - SQL.connect(); - } - - public static void close() { - SQL.close(); - } - - public static int purge() { - return SQL.purge(); - } - - public static UUID newPlayer(Player player, String ip) { - Log.info("Obtaining UUID for new player: " + player.getName()); - - final String username = player.getName().toLowerCase(); - - // Look in DB - final UUID dbUuid = find(username); - if (dbUuid != null) { - return dbUuid; - } - - // Find UUID and update in DB if not found - // Try API - UUID uuid = TFM_UuidResolver.getUUIDOf(username); - if (uuid == null) { - // Spoof - uuid = generateSpoofUuid(username); - } - - update(username, uuid); - return uuid; - } - - public static UUID getUniqueId(OfflinePlayer offlinePlayer) { - // Online check first - if (offlinePlayer.isOnline() && PlayerData.hasPlayerData(offlinePlayer.getPlayer())) { - return PlayerData.getPlayerData(offlinePlayer.getPlayer()).getUniqueId(); - } - - // DB, API, Spoof - return getUniqueId(offlinePlayer.getName()); - } - - public static UUID getUniqueId(String username) { - // Look in DB - final UUID dbUuid = find(username); - if (dbUuid != null) { - return dbUuid; - } - - // Try API - final UUID apiUuid = TFM_UuidResolver.getUUIDOf(username); - if (apiUuid != null) { - return apiUuid; - } - - // Spoof - return generateSpoofUuid(username); - } - - public static void rawSetUUID(String name, UUID uuid) { - if (name == null || uuid == null || name.isEmpty()) { - Log.warning("Not setting raw UUID: name and uuid may not be null!"); - return; - } - - update(name.toLowerCase().trim(), uuid); - } - - private static UUID find(String searchName) { - if (!SQL.connect()) { - return null; - } - - final ResultSet result; - try { - final PreparedStatement statement = FIND.getStatement(); - statement.clearParameters(); - statement.setString(1, searchName.toLowerCase()); - result = statement.executeQuery(); - } catch (Exception ex) { - Log.severe("Could not execute find statement!"); - Log.severe(ex); - return null; - } - - if (!SQLUtil.hasData(result)) { - SQLUtil.close(result); - return null; - } - - try { - final String uuidString = result.getString("uuid"); - return UUID.fromString(uuidString); - } catch (Exception ex) { - Log.severe(ex); - return null; - } finally { - SQLUtil.close(result); - } - } - - private static boolean update(String username, UUID uuid) { - if (!SQL.connect()) { - return false; - } - - try { - final PreparedStatement statement = UPDATE.getStatement(); - statement.clearParameters(); - statement.setString(1, username.toLowerCase()); - statement.setString(2, uuid.toString()); - statement.executeUpdate(); - return true; - } catch (Exception ex) { - Log.severe("Could not execute update statement!"); - Log.severe(ex); - return false; - } - } - - private static UUID generateSpoofUuid(String name) { - name = name.toLowerCase(); - Log.info("Generating spoof UUID for " + name); - - try { - final MessageDigest digest = MessageDigest.getInstance("SHA256"); - final byte[] result = digest.digest(name.getBytes()); - final StringBuilder builder = new StringBuilder(); - for (int i = 0; i < result.length; i++) { - builder.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1)); - } - - return UUID.fromString( - "deadbeef" - + "-" + builder.substring(8, 12) - + "-" + builder.substring(12, 16) - + "-" + builder.substring(16, 20) - + "-" + builder.substring(20, 32)); - } catch (NoSuchAlgorithmException ex) { - Log.warning("Could not generate spoof UUID: SHA1 algorithm not found!"); - } - - return UUID.randomUUID(); - } - - public static class TFM_UuidResolver implements Callable> { - - private static final double PROFILES_PER_REQUEST = 100; - private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft"; - private final JSONParser jsonParser = new JSONParser(); - private final List names; - - public TFM_UuidResolver(List names) { - this.names = ImmutableList.copyOf(names); - } - - @Override - public Map call() { - final Map uuidMap = new HashMap(); - int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); - for (int i = 0; i < requests; i++) { - try { - final URL url = new URL(PROFILE_URL); - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - - final String body = JSONArray.toJSONString(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); - - final OutputStream stream = connection.getOutputStream(); - stream.write(body.getBytes()); - stream.flush(); - stream.close(); - - final JSONArray array = (JSONArray) jsonParser.parse(new InputStreamReader(connection.getInputStream())); - - for (Object profile : array) { - final JSONObject jsonProfile = (JSONObject) profile; - final String id = (String) jsonProfile.get("id"); - final String name = (String) jsonProfile.get("name"); - final UUID uuid = UUID.fromString( - id.substring(0, 8) - + "-" + id.substring(8, 12) - + "-" + id.substring(12, 16) - + "-" + id.substring(16, 20) - + "-" + id.substring(20, 32)); - uuidMap.put(name, uuid); - } - - if (i != requests - 1) { - Thread.sleep(100L); - } - } catch (Exception ex) { - Log.severe("Could not resolve UUID(s) of " - + StringUtils.join(names.subList(i * 100, Math.min((i + 1) * 100, names.size())), ", ")); - //Log.severe(ex); - } - } - return uuidMap; - } - - public static UUID getUUIDOf(String name) { - return new TFM_UuidResolver(Arrays.asList(name)).call().get(name); - } - } - -}