diff --git a/LaunchServer/source/LaunchServer.java b/LaunchServer/source/LaunchServer.java index 8ee9f90..fd7cb46 100644 --- a/LaunchServer/source/LaunchServer.java +++ b/LaunchServer/source/LaunchServer.java @@ -147,8 +147,7 @@ config.verify(); // Set launcher EXE binary - launcherEXEBinary = config.launch4J ? - new EXEL4JLauncherBinary(this) : new EXELauncherBinary(this); + launcherEXEBinary = config.launch4J ? new EXEL4JLauncherBinary(this) : new EXELauncherBinary(this); syncLauncherBinaries(); // Sync updates dir @@ -397,6 +396,7 @@ try (BufferedReader reader = IOHelper.newReader(file)) { profile = new ClientProfile(TextConfigReader.read(reader, true)); } + profile.verify(); // Add SIGNED profile to result list result.add(new SignedObjectHolder<>(profile, privateKey)); @@ -426,13 +426,22 @@ private Config(BlockConfigEntry block) { super(block); address = block.getEntry("address", StringConfigEntry.class); - port = block.getEntryValue("port", IntegerConfigEntry.class); + port = VerifyHelper.verifyInt(block.getEntryValue("port", IntegerConfigEntry.class), + VerifyHelper.range(0, 65535), "Illegal LaunchServer port"); bindAddress = block.hasEntry("bindAddress") ? block.getEntryValue("bindAddress", StringConfigEntry.class) : getAddress(); // Skin system skinsURL = block.getEntryValue("skinsURL", StringConfigEntry.class); + String skinURL = getSkinURL("skinUsername", ZERO_UUID); + if (skinURL != null) { + IOHelper.verifyURL(skinURL); + } cloaksURL = block.getEntryValue("cloaksURL", StringConfigEntry.class); + String cloakURL = getCloakURL("cloakUsername", ZERO_UUID); + if (cloakURL != null) { + IOHelper.verifyURL(cloakURL); + } // Set auth handler and provider String authHandlerName = block.getEntryValue("authHandler", StringConfigEntry.class); @@ -444,25 +453,6 @@ launch4J = block.getEntryValue("launch4J", BooleanConfigEntry.class); } - @Override - public void verify() { - VerifyHelper.verifyInt(port, VerifyHelper.range(0, 65535), "Illegal LaunchServer port: " + port); - - // Verify textures info - String skinURL = getSkinURL("skinUsername", ZERO_UUID); - if (skinURL != null) { - IOHelper.verifyURL(skinURL); - } - String cloakURL = getCloakURL("cloakUsername", ZERO_UUID); - if (cloakURL != null) { - IOHelper.verifyURL(cloakURL); - } - - // Verify auth handler and provider - authHandler.verify(); - authProvider.verify(); - } - @LauncherAPI public String getAddress() { return address.getValue(); @@ -494,6 +484,11 @@ } @LauncherAPI + public void verify() { + VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty"); + } + + @LauncherAPI public static String getTextureURL(String url, String username, UUID uuid) { if (url.isEmpty()) { return null; diff --git a/LaunchServer/source/auth/MySQLSourceConfig.java b/LaunchServer/source/auth/MySQLSourceConfig.java new file mode 100644 index 0000000..930bde3 --- /dev/null +++ b/LaunchServer/source/auth/MySQLSourceConfig.java @@ -0,0 +1,105 @@ +package launchserver.auth; + +import javax.sql.DataSource; +import java.io.Flushable; +import java.sql.Connection; +import java.sql.SQLException; + +import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; +import com.zaxxer.hikari.HikariDataSource; +import launcher.LauncherAPI; +import launcher.helper.IOHelper; +import launcher.helper.LogHelper; +import launcher.helper.VerifyHelper; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.IntegerConfigEntry; +import launcher.serialize.config.entry.StringConfigEntry; + +public final class MySQLSourceConfig extends ConfigObject implements Flushable { + private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( + Integer.parseUnsignedInt(System.getProperty("launcher.mysql.maxPoolSize", Integer.toString(10))), + VerifyHelper.POSITIVE, "launcher.mysql.maxPoolSize can't be <= 0"); + private static final int STMT_CACHE_SIZE = VerifyHelper.verifyInt( + Integer.parseUnsignedInt(System.getProperty("launcher.mysql.stmtCacheSize", Integer.toString(250))), + VerifyHelper.NOT_NEGATIVE, "launcher.mysql.stmtCacheSize can't be < 0"); + + // Instance + private final String poolName; + + // Config + private final String address; + private final int port; + private final String username; + private final String password; + private final String database; + + // Cache + private DataSource source; + private boolean hikari; + + @LauncherAPI + public MySQLSourceConfig(String poolName, BlockConfigEntry block) { + super(block); + this.poolName = poolName; + address = VerifyHelper.verify(block.getEntryValue("address", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL address can't be empty"); + port = VerifyHelper.verifyInt(block.getEntryValue("port", IntegerConfigEntry.class), + VerifyHelper.range(0, 65535), "Illegal MySQL port"); + username = VerifyHelper.verify(block.getEntryValue("username", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL username can't be empty"); + password = block.getEntryValue("password", StringConfigEntry.class); + database = VerifyHelper.verify(block.getEntryValue("database", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL database can't be empty"); + + // Password shouldn't be verified + } + + @Override + public synchronized void flush() { + if (hikari) { // Shutdown hikari pool + ((HikariDataSource) source).close(); + } + } + + @LauncherAPI + public synchronized Connection getConnection() throws SQLException { + if (source == null) { // New data source + MysqlDataSource mysqlSource = new MysqlDataSource(); + mysqlSource.setUseUnicode(true); + mysqlSource.setLoginTimeout(IOHelper.TIMEOUT); + mysqlSource.setCachePrepStmts(true); + mysqlSource.setPrepStmtCacheSize(STMT_CACHE_SIZE); + mysqlSource.setPrepStmtCacheSqlLimit(IOHelper.BUFFER_SIZE); + + // Set credentials + mysqlSource.setServerName(address); + mysqlSource.setPortNumber(port); + mysqlSource.setUser(username); + mysqlSource.setPassword(password); + mysqlSource.setDatabaseName(database); + + // Try using HikariCP + source = mysqlSource; + try { + Class.forName("com.zaxxer.hikari.HikariDataSource"); + hikari = true; // Used for shutdown. Not instanceof because of possible classpath error + + // Set HikariCP pool + HikariDataSource hikariSource = new HikariDataSource(); + hikariSource.setDataSource(source); + + // Set pool settings + hikariSource.setPoolName(poolName); + hikariSource.setMaximumPoolSize(MAX_POOL_SIZE); + + // Replace source with hds + source = hikariSource; + LogHelper.info("HikariCP pooling enabled for '%s'", poolName); + } catch (ClassNotFoundException ignored) { + LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName); + } + } + return source.getConnection(); + } +} diff --git a/LaunchServer/source/auth/handler/CachedAuthHandler.java b/LaunchServer/source/auth/handler/CachedAuthHandler.java index 2aa40d3..cd76a83 100644 --- a/LaunchServer/source/auth/handler/CachedAuthHandler.java +++ b/LaunchServer/source/auth/handler/CachedAuthHandler.java @@ -2,12 +2,12 @@ import java.io.IOException; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.UUID; import launcher.LauncherAPI; +import launcher.helper.CommonHelper; import launcher.helper.IOHelper; import launcher.helper.SecurityHelper; import launcher.request.auth.JoinServerRequest; @@ -45,8 +45,8 @@ @Override public final synchronized boolean joinServer(String username, String accessToken, String serverID) throws IOException { Entry entry = getEntry(username); - if (entry == null || !username.equals(entry.username) || - !accessToken.equals(entry.accessToken) || !updateServerID(entry.uuid, serverID)) { + if (entry == null || !username.equals(entry.username) || !accessToken.equals(entry.accessToken) || + !updateServerID(entry.uuid, serverID)) { return false; // Account doesn't exist or invalid access token } @@ -70,7 +70,7 @@ @LauncherAPI protected void addEntry(Entry entry) { entryCache.put(entry.uuid, entry); - usernamesCache.put(low(entry.username), entry.uuid); + usernamesCache.put(CommonHelper.low(entry.username), entry.uuid); } @LauncherAPI @@ -97,7 +97,7 @@ } private Entry getEntry(String username) throws IOException { - UUID uuid = usernamesCache.get(low(username)); + UUID uuid = usernamesCache.get(CommonHelper.low(username)); if (uuid != null) { return getEntry(uuid); } @@ -112,10 +112,6 @@ return entry; } - private static String low(String username) { - return username.toLowerCase(Locale.US); - } - public final class Entry { @LauncherAPI public final UUID uuid; private String username; @@ -126,10 +122,8 @@ public Entry(UUID uuid, String username, String accessToken, String serverID) { this.uuid = Objects.requireNonNull(uuid, "uuid"); this.username = Objects.requireNonNull(username, "username"); - this.accessToken = accessToken == null ? - null : SecurityHelper.verifyToken(accessToken); - this.serverID = serverID == null ? - null : JoinServerRequest.verifyServerID(serverID); + this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken); + this.serverID = serverID == null ? null : JoinServerRequest.verifyServerID(serverID); } } } diff --git a/LaunchServer/source/auth/handler/FileAuthHandler.java b/LaunchServer/source/auth/handler/FileAuthHandler.java index 337dac5..6b3ee84 100644 --- a/LaunchServer/source/auth/handler/FileAuthHandler.java +++ b/LaunchServer/source/auth/handler/FileAuthHandler.java @@ -5,7 +5,6 @@ import java.security.SecureRandom; import java.util.Collections; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -13,6 +12,7 @@ import launcher.LauncherAPI; import launcher.client.PlayerProfile; +import launcher.helper.CommonHelper; import launcher.helper.IOHelper; import launcher.helper.LogHelper; import launcher.helper.SecurityHelper; @@ -68,7 +68,7 @@ // Generate UUID uuid = genUUIDFor(username); authsMap.put(uuid, auth); - usernamesMap.put(low(username), uuid); + usernamesMap.put(CommonHelper.low(username), uuid); } // Authenticate @@ -119,7 +119,7 @@ public final UUID usernameToUUID(String username) { lock.readLock().lock(); try { - return usernamesMap.get(low(username)); + return usernamesMap.get(CommonHelper.low(username)); } finally { lock.readLock().unlock(); } @@ -136,11 +136,6 @@ } } - @Override - public final void verify() { - // Do nothing? - } - @LauncherAPI public final Set> entrySet() { return Collections.unmodifiableMap(authsMap).entrySet(); @@ -151,7 +146,7 @@ lock.writeLock().lock(); try { authsMap.put(uuid, entry); - usernamesMap.put(low(entry.username), uuid); + usernamesMap.put(CommonHelper.low(entry.username), uuid); } finally { lock.writeLock().unlock(); } @@ -180,10 +175,6 @@ return uuid; } - private static String low(String username) { - return username.toLowerCase(Locale.US); - } - public static final class Auth extends StreamObject { private String username; private String accessToken; @@ -202,10 +193,8 @@ } // Set and verify access token - this.accessToken = accessToken == null ? - null : SecurityHelper.verifyToken(accessToken); - this.serverID = serverID == null ? - null : JoinServerRequest.verifyServerID(serverID); + this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken); + this.serverID = serverID == null ? null : JoinServerRequest.verifyServerID(serverID); } @LauncherAPI diff --git a/LaunchServer/source/auth/handler/MySQLAuthHandler.java b/LaunchServer/source/auth/handler/MySQLAuthHandler.java index 7ec628a..36c4bcb 100644 --- a/LaunchServer/source/auth/handler/MySQLAuthHandler.java +++ b/LaunchServer/source/auth/handler/MySQLAuthHandler.java @@ -12,7 +12,7 @@ import launcher.serialize.config.entry.BlockConfigEntry; import launcher.serialize.config.entry.BooleanConfigEntry; import launcher.serialize.config.entry.StringConfigEntry; -import launchserver.helper.MySQLSourceConfig; +import launchserver.auth.MySQLSourceConfig; public final class MySQLAuthHandler extends CachedAuthHandler { private final MySQLSourceConfig mySQLHolder; @@ -31,11 +31,18 @@ public MySQLAuthHandler(BlockConfigEntry block) { super(block); mySQLHolder = new MySQLSourceConfig("authHandlerPool", block); - table = block.getEntryValue("table", StringConfigEntry.class); - uuidColumn = block.getEntryValue("uuidColumn", StringConfigEntry.class); - usernameColumn = block.getEntryValue("usernameColumn", StringConfigEntry.class); - accessTokenColumn = block.getEntryValue("accessTokenColumn", StringConfigEntry.class); - serverIDColumn = block.getEntryValue("serverIDColumn", StringConfigEntry.class); + + // Read query params + table = VerifyHelper.verifyIDName( + block.getEntryValue("table", StringConfigEntry.class)); + uuidColumn = VerifyHelper.verifyIDName( + block.getEntryValue("uuidColumn", StringConfigEntry.class)); + usernameColumn = VerifyHelper.verifyIDName( + block.getEntryValue("usernameColumn", StringConfigEntry.class)); + accessTokenColumn = VerifyHelper.verifyIDName( + block.getEntryValue("accessTokenColumn", StringConfigEntry.class)); + serverIDColumn = VerifyHelper.verifyIDName( + block.getEntryValue("serverIDColumn", StringConfigEntry.class)); // Prepare SQL queries queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s FROM %s WHERE %s=?", @@ -68,16 +75,6 @@ } @Override - public void verify() { - mySQLHolder.verify(); - VerifyHelper.verifyIDName(table); - VerifyHelper.verifyIDName(uuidColumn); - VerifyHelper.verifyIDName(usernameColumn); - VerifyHelper.verifyIDName(accessTokenColumn); - VerifyHelper.verifyIDName(serverIDColumn); - } - - @Override protected Entry fetchEntry(String username) throws IOException { return query(queryByUsernameSQL, username); } diff --git a/LaunchServer/source/auth/handler/NullAuthHandler.java b/LaunchServer/source/auth/handler/NullAuthHandler.java index c5d7d2f..23e7185 100644 --- a/LaunchServer/source/auth/handler/NullAuthHandler.java +++ b/LaunchServer/source/auth/handler/NullAuthHandler.java @@ -47,14 +47,6 @@ return getHandler().uuidToUsername(uuid); } - @Override - public void verify() { - AuthHandler handler = this.handler; - if (handler != null) { - handler.verify(); - } - } - @LauncherAPI public void setBackend(AuthHandler handler) { this.handler = handler; diff --git a/LaunchServer/source/auth/provider/AcceptAuthProvider.java b/LaunchServer/source/auth/provider/AcceptAuthProvider.java index 446ff02..2156849 100644 --- a/LaunchServer/source/auth/provider/AcceptAuthProvider.java +++ b/LaunchServer/source/auth/provider/AcceptAuthProvider.java @@ -16,9 +16,4 @@ public void flush() { // Do nothing } - - @Override - public void verify() { - // Do nothing - } -} \ No newline at end of file +} diff --git a/LaunchServer/source/auth/provider/DigestAuthProvider.java b/LaunchServer/source/auth/provider/DigestAuthProvider.java index fab94bf..0113f12 100644 --- a/LaunchServer/source/auth/provider/DigestAuthProvider.java +++ b/LaunchServer/source/auth/provider/DigestAuthProvider.java @@ -13,12 +13,7 @@ protected DigestAuthProvider(BlockConfigEntry block) { super(block); digest = block.getEntryValue("digest", StringConfigEntry.class); - } - - @Override - @SuppressWarnings("DesignForExtension") - public void verify() { - getDigest(); + getDigest(); // Verify that this digest exists } @LauncherAPI diff --git a/LaunchServer/source/auth/provider/FileAuthProvider.java b/LaunchServer/source/auth/provider/FileAuthProvider.java index a9e3feb..c0d1c81 100644 --- a/LaunchServer/source/auth/provider/FileAuthProvider.java +++ b/LaunchServer/source/auth/provider/FileAuthProvider.java @@ -6,36 +6,49 @@ import java.nio.file.attribute.FileTime; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import launcher.helper.CommonHelper; import launcher.helper.IOHelper; import launcher.helper.LogHelper; import launcher.helper.VerifyHelper; +import launcher.serialize.config.ConfigObject; +import launcher.serialize.config.TextConfigReader; import launcher.serialize.config.entry.BlockConfigEntry; +import launcher.serialize.config.entry.ConfigEntry; import launcher.serialize.config.entry.StringConfigEntry; -import launchserver.helper.LineReader; public final class FileAuthProvider extends DigestAuthProvider { private final Path file; // Cache - private final Map cache = new HashMap<>(8192); + private final Map auths = new HashMap<>(8192); private final Object cacheLock = new Object(); private FileTime cacheLastModified; public FileAuthProvider(BlockConfigEntry block) { super(block); file = IOHelper.toPath(block.getEntryValue("file", StringConfigEntry.class)); + + // Try to update cache + try { + updateCache(); + } catch (IOException e) { + LogHelper.error(e); + } } @Override public String auth(String login, String password) throws IOException { - String validDigest; + Auth auth; synchronized (cacheLock) { updateCache(); - validDigest = cache.get(login); + auth = auths.get(CommonHelper.low(login)); } - verifyDigest(validDigest, password); - return login; + + // Verify digest and return true username + verifyDigest(auth.password, password); + return auth.username; } @Override @@ -50,27 +63,39 @@ } // Read file - cache.clear(); - LogHelper.info("Recaching users file: '%s'", file); - try (BufferedReader reader = new LineReader(IOHelper.newReader(file))) { - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - // Get and verify split index - int splitIndex = VerifyHelper.verifyInt(line.indexOf(':'), VerifyHelper.NOT_NEGATIVE, - String.format("Illegal line in users file: '%s'", line)); + LogHelper.info("Recaching auth provider file: '%s'", file); + BlockConfigEntry authFile; + try (BufferedReader reader = IOHelper.newReader(file)) { + authFile = TextConfigReader.read(reader, false); + } - // Split and verify username and password - String username = VerifyHelper.verify(line.substring(0, splitIndex).trim(), - VerifyHelper.NOT_EMPTY, "Empty username"); - String password = VerifyHelper.verify(line.substring(splitIndex + 1).trim(), - VerifyHelper.NOT_EMPTY, "Empty pasword"); + // Read auths from config block + auths.clear(); + Set>> entrySet = authFile.getValue().entrySet(); + for (Map.Entry> entry : entrySet) { + String login = entry.getKey(); + ConfigEntry value = VerifyHelper.verify(entry.getValue(), v -> v.getType() == ConfigEntry.Type.BLOCK, + String.format("Illegal config entry type: '%s'", login)); - // Try put to cache - VerifyHelper.putIfAbsent(cache, username, password, - String.format("Duplicate username in users file: '%s'", username)); - } + // Add auth entry + Auth auth = new Auth((BlockConfigEntry) value); + VerifyHelper.putIfAbsent(auths, CommonHelper.low(login), auth, + String.format("Duplicate login: '%s'", login)); } // Update last modified time cacheLastModified = lastModified; } + + private static final class Auth extends ConfigObject { + private final String username; + private final String password; + + private Auth(BlockConfigEntry block) { + super(block); + this.username = VerifyHelper.verifyUsername(block.getEntryValue("username", StringConfigEntry.class)); + this.password = VerifyHelper.verify(block.getEntryValue("password", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, String.format("Password can't be empty: '%s'", username)); + } + } } diff --git a/LaunchServer/source/auth/provider/HTTPAuthProvider.java b/LaunchServer/source/auth/provider/HTTPAuthProvider.java index 8e5f978..60ac89c 100644 --- a/LaunchServer/source/auth/provider/HTTPAuthProvider.java +++ b/LaunchServer/source/auth/provider/HTTPAuthProvider.java @@ -18,6 +18,9 @@ super(block); url = block.getEntryValue("url", StringConfigEntry.class); response = Pattern.compile(block.getEntryValue("response", StringConfigEntry.class)); + + // Verify is valid URL + IOHelper.verifyURL(getFormattedURL("httpAuthLogin", "httpAuthPassword")); } @Override @@ -35,11 +38,6 @@ // Do nothing } - @Override - public void verify() { - IOHelper.verifyURL(getFormattedURL("httpAuthLogin", "httpAuthPassword")); - } - private String getFormattedURL(String login, String password) { return CommonHelper.replace(url, "login", login, "password", password); } diff --git a/LaunchServer/source/auth/provider/MySQLAuthProvider.java b/LaunchServer/source/auth/provider/MySQLAuthProvider.java index 236f89c..d39d51b 100644 --- a/LaunchServer/source/auth/provider/MySQLAuthProvider.java +++ b/LaunchServer/source/auth/provider/MySQLAuthProvider.java @@ -11,7 +11,7 @@ import launcher.serialize.config.entry.ListConfigEntry; import launcher.serialize.config.entry.StringConfigEntry; import launchserver.auth.AuthException; -import launchserver.helper.MySQLSourceConfig; +import launchserver.auth.MySQLSourceConfig; public final class MySQLAuthProvider extends AuthProvider { private final MySQLSourceConfig mySQLHolder; @@ -21,8 +21,12 @@ public MySQLAuthProvider(BlockConfigEntry block) { super(block); mySQLHolder = new MySQLSourceConfig("authProviderPool", block); - query = block.getEntryValue("query", StringConfigEntry.class); - queryParams = block.getEntry("queryParams", ListConfigEntry.class).stream(StringConfigEntry.class).toArray(String[]::new); + + // Read query + query = VerifyHelper.verify(block.getEntryValue("query", StringConfigEntry.class), + VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); + queryParams = block.getEntry("queryParams", ListConfigEntry.class). + stream(StringConfigEntry.class).toArray(String[]::new); } @Override @@ -36,8 +40,7 @@ // Execute SQL query try (ResultSet set = statement.executeQuery()) { - return set.next() ? set.getString(1) : - authError("Incorrect username or password"); + return set.next() ? set.getString(1) : authError("Incorrect username or password"); } } } @@ -47,12 +50,4 @@ public void flush() { // Do nothing } - - @Override - public void verify() { - mySQLHolder.verify(); - - // Verify auth provider-specific - VerifyHelper.verify(query, VerifyHelper.NOT_EMPTY, "MySQL query can't be empty"); - } } diff --git a/LaunchServer/source/auth/provider/NullAuthProvider.java b/LaunchServer/source/auth/provider/NullAuthProvider.java index 0d3a941..d63f909 100644 --- a/LaunchServer/source/auth/provider/NullAuthProvider.java +++ b/LaunchServer/source/auth/provider/NullAuthProvider.java @@ -26,14 +26,6 @@ } } - @Override - public void verify() { - AuthProvider provider = this.provider; - if (provider != null) { - provider.verify(); - } - } - @LauncherAPI public void setBackend(AuthProvider provider) { this.provider = provider; diff --git a/LaunchServer/source/auth/provider/RejectAuthProvider.java b/LaunchServer/source/auth/provider/RejectAuthProvider.java index 23ee57b..054d047 100644 --- a/LaunchServer/source/auth/provider/RejectAuthProvider.java +++ b/LaunchServer/source/auth/provider/RejectAuthProvider.java @@ -10,7 +10,8 @@ public RejectAuthProvider(BlockConfigEntry block) { super(block); - message = block.getEntryValue("message", StringConfigEntry.class); + message = VerifyHelper.verify(block.getEntryValue("message", StringConfigEntry.class), VerifyHelper.NOT_EMPTY, + "Auth error message can't be empty"); } @Override @@ -22,9 +23,4 @@ public void flush() { // Do nothing } - - @Override - public void verify() { - VerifyHelper.verify(message, VerifyHelper.NOT_EMPTY, "Auth error message can't be empty"); - } } diff --git a/LaunchServer/source/command/basic/EvalCommand.java b/LaunchServer/source/command/basic/EvalCommand.java deleted file mode 100644 index 07886d5..0000000 --- a/LaunchServer/source/command/basic/EvalCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package launchserver.command.basic; - -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.file.Path; - -import launcher.helper.IOHelper; -import launcher.helper.LogHelper; -import launchserver.LaunchServer; -import launchserver.command.Command; -import launchserver.command.CommandException; -import launchserver.helper.LineReader; - -public final class EvalCommand extends Command { - public EvalCommand(LaunchServer server) { - super(server); - } - - @Override - public String getArgsDescription() { - return ""; - } - - @Override - public String getUsageDescription() { - return "Evaluate input file (external flag enabled)"; - } - - @Override - public void invoke(String... args) throws IOException, CommandException { - verifyArgs(args, 1); - - // Evaluate input file - Path file = IOHelper.toPath(args[0]); - LogHelper.subInfo("Evaluating file: '%s'", file); - try (BufferedReader reader = new LineReader(IOHelper.newReader(file))) { - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - server.commandHandler.eval(line, false); - } - } - } -} diff --git a/LaunchServer/source/command/handler/CommandHandler.java b/LaunchServer/source/command/handler/CommandHandler.java index 105d856..ea97792 100644 --- a/LaunchServer/source/command/handler/CommandHandler.java +++ b/LaunchServer/source/command/handler/CommandHandler.java @@ -24,7 +24,6 @@ import launchserver.command.basic.BuildCommand; import launchserver.command.basic.ClearCommand; import launchserver.command.basic.DebugCommand; -import launchserver.command.basic.EvalCommand; import launchserver.command.basic.GCCommand; import launchserver.command.basic.HelpCommand; import launchserver.command.basic.RebindCommand; @@ -48,7 +47,6 @@ registerCommand("build", new BuildCommand(server)); registerCommand("stop", new StopCommand(server)); registerCommand("rebind", new RebindCommand(server)); - registerCommand("eval", new EvalCommand(server)); registerCommand("debug", new DebugCommand(server)); registerCommand("clear", new ClearCommand(server)); registerCommand("gc", new GCCommand(server)); diff --git a/LaunchServer/source/helper/LineReader.java b/LaunchServer/source/helper/LineReader.java deleted file mode 100644 index b5c9ed5..0000000 --- a/LaunchServer/source/helper/LineReader.java +++ /dev/null @@ -1,36 +0,0 @@ -package launchserver.helper; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -import launcher.LauncherAPI; -import launcher.helper.IOHelper; - -public final class LineReader extends BufferedReader { - @LauncherAPI - public LineReader(Reader in) { - super(in, IOHelper.BUFFER_SIZE); - } - - @Override - public String readLine() throws IOException { - String line; - do { - line = super.readLine(); - if (line == null) { - return null; - } - - // Trim comments - int commentIndex = line.indexOf('#'); - if (commentIndex >= 0) { - line = line.substring(0, commentIndex); - } - - // Trim - line = line.trim(); - } while (line.isEmpty()); - return line; - } -} diff --git a/LaunchServer/source/helper/MySQLSourceConfig.java b/LaunchServer/source/helper/MySQLSourceConfig.java deleted file mode 100644 index 6037296..0000000 --- a/LaunchServer/source/helper/MySQLSourceConfig.java +++ /dev/null @@ -1,110 +0,0 @@ -package launchserver.helper; - -import javax.sql.DataSource; -import java.io.Flushable; -import java.sql.Connection; -import java.sql.SQLException; - -import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; -import com.zaxxer.hikari.HikariDataSource; -import launcher.LauncherAPI; -import launcher.helper.IOHelper; -import launcher.helper.LogHelper; -import launcher.helper.VerifyHelper; -import launcher.serialize.config.ConfigObject; -import launcher.serialize.config.entry.BlockConfigEntry; -import launcher.serialize.config.entry.IntegerConfigEntry; -import launcher.serialize.config.entry.StringConfigEntry; - -public final class MySQLSourceConfig extends ConfigObject implements Flushable { - private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt( - Integer.parseUnsignedInt(System.getProperty("launcher.mysql.maxPoolSize", Integer.toString(10))), - VerifyHelper.POSITIVE, "launcher.mysql.maxPoolSize can't be <= 0"); - private static final int STMT_CACHE_SIZE = VerifyHelper.verifyInt( - Integer.parseUnsignedInt(System.getProperty("launcher.mysql.stmtCacheSize", Integer.toString(250))), - VerifyHelper.NOT_NEGATIVE, "launcher.mysql.stmtCacheSize can't be < 0"); - - // Instance - private final String poolName; - - // Config - private final String address; - private final int port; - private final String username; - private final String password; - private final String database; - - // Cache - private DataSource source; - private boolean hikari; - - @LauncherAPI - public MySQLSourceConfig(String poolName, BlockConfigEntry block) { - super(block); - this.poolName = poolName; - address = block.getEntryValue("address", StringConfigEntry.class); - port = block.getEntryValue("port", IntegerConfigEntry.class); - username = block.getEntryValue("username", StringConfigEntry.class); - password = block.getEntryValue("password", StringConfigEntry.class); - database = block.getEntryValue("database", StringConfigEntry.class); - } - - @Override - public synchronized void flush() { - if (hikari) { // Shutdown hikari pool - ((HikariDataSource) source).close(); - } - } - - @Override - public void verify() { - // Verify MySQL address - VerifyHelper.verify(address, VerifyHelper.NOT_EMPTY, "MySQL address can't be empty"); - VerifyHelper.verify(username, VerifyHelper.NOT_EMPTY, "MySQL username can't be empty"); - VerifyHelper.verify(database, VerifyHelper.NOT_EMPTY, "MySQL database can't be empty"); - VerifyHelper.verifyInt(port, VerifyHelper.range(0, 65535), "Illegal MySQL port: " + port); - - // Don't verify password, it can be empty - } - - @LauncherAPI - public synchronized Connection getConnection() throws SQLException { - if (source == null) { // New data source - MysqlDataSource mysqlSource = new MysqlDataSource(); - mysqlSource.setUseUnicode(true); - mysqlSource.setLoginTimeout(IOHelper.TIMEOUT); - mysqlSource.setCachePrepStmts(true); - mysqlSource.setPrepStmtCacheSize(STMT_CACHE_SIZE); - mysqlSource.setPrepStmtCacheSqlLimit(IOHelper.BUFFER_SIZE); - - // Set credentials - mysqlSource.setServerName(address); - mysqlSource.setPortNumber(port); - mysqlSource.setUser(username); - mysqlSource.setPassword(password); - mysqlSource.setDatabaseName(database); - - // Try using HikariCP - source = mysqlSource; - try { - Class.forName("com.zaxxer.hikari.HikariDataSource"); - hikari = true; // Used for shutdown. Not instanceof because of possible classpath error - - // Set HikariCP pool - HikariDataSource hikariSource = new HikariDataSource(); - hikariSource.setDataSource(source); - - // Set pool settings - hikariSource.setPoolName(poolName); - hikariSource.setMaximumPoolSize(MAX_POOL_SIZE); - - // Replace source with hds - source = hikariSource; - LogHelper.info("HikariCP pooling enabled for '%s'", poolName); - } catch (ClassNotFoundException ignored) { - LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName); - } - } - return source.getConnection(); - } -} diff --git a/LaunchServer/source/response/ResponseThread.java b/LaunchServer/source/response/ResponseThread.java index 877271a..077987f 100644 --- a/LaunchServer/source/response/ResponseThread.java +++ b/LaunchServer/source/response/ResponseThread.java @@ -62,7 +62,7 @@ try { respond(type, input, output); } catch (RequestException e) { - LogHelper.debug(String.format("#%d Request error: %s", id, e.getMessage())); + LogHelper.subDebug(String.format("#%d Request error: %s", id, e.getMessage())); output.writeString(e.getMessage(), 0); } } catch (Exception e) { diff --git a/LaunchServer/source/response/auth/AuthResponse.java b/LaunchServer/source/response/auth/AuthResponse.java index c01d39a..9f51728 100644 --- a/LaunchServer/source/response/auth/AuthResponse.java +++ b/LaunchServer/source/response/auth/AuthResponse.java @@ -30,8 +30,8 @@ // Decrypt password String password; try { - password = IOHelper.decode(SecurityHelper. - newRSADecryptCipher(server.privateKey).doFinal(encryptedPassword)); + password = IOHelper.decode(SecurityHelper.newRSADecryptCipher(server.privateKey). + doFinal(encryptedPassword)); } catch (IllegalBlockSizeException | BadPaddingException ignored) { throw new RequestException("Password decryption error"); } diff --git a/LaunchServer/source/response/update/LauncherResponse.java b/LaunchServer/source/response/update/LauncherResponse.java index 9ce3db8..fa73bc4 100644 --- a/LaunchServer/source/response/update/LauncherResponse.java +++ b/LaunchServer/source/response/update/LauncherResponse.java @@ -21,8 +21,7 @@ @Override public void reply() throws IOException { // Resolve launcher binary - SignedBytesHolder bytes = (input.readBoolean() ? - server.launcherEXEBinary : server.launcherBinary).getBytes(); + SignedBytesHolder bytes = (input.readBoolean() ? server.launcherEXEBinary : server.launcherBinary).getBytes(); if (bytes == null) { throw new RequestException("Missing launcher binary"); } diff --git a/Launcher/source/client/ClientLauncher.java b/Launcher/source/client/ClientLauncher.java index 885cfd4..22099e8 100644 --- a/Launcher/source/client/ClientLauncher.java +++ b/Launcher/source/client/ClientLauncher.java @@ -98,7 +98,8 @@ // Add classpath and main class Collections.addAll(args, profile.object.getJvmArgs()); - Collections.addAll(args, "-classpath", IOHelper.getCodeSource(ClientLauncher.class).toString(), ClientLauncher.class.getName()); + Collections.addAll(args, "-classpath", IOHelper.getCodeSource(ClientLauncher.class).toString(), + ClientLauncher.class.getName()); args.add(paramsFile.toString()); // Add params file path to args // Build client process diff --git a/Launcher/source/client/ClientProfile.java b/Launcher/source/client/ClientProfile.java index ad82597..6615e06 100644 --- a/Launcher/source/client/ClientProfile.java +++ b/Launcher/source/client/ClientProfile.java @@ -74,29 +74,6 @@ this(new BlockConfigEntry(input, ro)); } - @Override - public void verify() { - // Version - getVersion(); - IOHelper.verifyFileName(getAssetIndex()); - - // Client - VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Profile title can't be empty"); - VerifyHelper.verify(getServerAddress(), VerifyHelper.NOT_EMPTY, "Server address can't be empty"); - VerifyHelper.verifyInt(getServerPort(), VerifyHelper.range(0, 65535), "Illegal server port: " + getServerPort()); - - // Updater and client watch service - update.verifyOfType(ConfigEntry.Type.STRING); - updateVerify.verifyOfType(ConfigEntry.Type.STRING); - updateExclusions.verifyOfType(ConfigEntry.Type.STRING); - - // Client launcher - jvmArgs.verifyOfType(ConfigEntry.Type.STRING); - classPath.verifyOfType(ConfigEntry.Type.STRING); - clientArgs.verifyOfType(ConfigEntry.Type.STRING); - VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Main class can't be empty"); - } - @LauncherAPI public String getAssetIndex() { return assetIndex.getValue(); @@ -170,6 +147,29 @@ this.version.setValue(version.name); } + @LauncherAPI + public void verify() { + // Version + getVersion(); + IOHelper.verifyFileName(getAssetIndex()); + + // Client + VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Profile title can't be empty"); + VerifyHelper.verify(getServerAddress(), VerifyHelper.NOT_EMPTY, "Server address can't be empty"); + VerifyHelper.verifyInt(getServerPort(), VerifyHelper.range(0, 65535), "Illegal server port: " + getServerPort()); + + // Updater and client watch service + update.verifyOfType(ConfigEntry.Type.STRING); + updateVerify.verifyOfType(ConfigEntry.Type.STRING); + updateExclusions.verifyOfType(ConfigEntry.Type.STRING); + + // Client launcher + jvmArgs.verifyOfType(ConfigEntry.Type.STRING); + classPath.verifyOfType(ConfigEntry.Type.STRING); + clientArgs.verifyOfType(ConfigEntry.Type.STRING); + VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Main class can't be empty"); + } + @Override public int compareTo(ClientProfile o) { return Integer.compare(getSortIndex(), o.getSortIndex()); diff --git a/Launcher/source/helper/CommonHelper.java b/Launcher/source/helper/CommonHelper.java index a401268..866fdd6 100644 --- a/Launcher/source/helper/CommonHelper.java +++ b/Launcher/source/helper/CommonHelper.java @@ -1,6 +1,7 @@ package launcher.helper; import javax.script.ScriptEngine; +import java.util.Locale; import jdk.nashorn.api.scripting.NashornScriptEngineFactory; import launcher.LauncherAPI; @@ -13,6 +14,11 @@ } @LauncherAPI + public static String low(String s) { + return s.toLowerCase(Locale.US); + } + + @LauncherAPI public static ScriptEngine newScriptEngine() { return SCRIPT_ENGINE_FACTORY.getScriptEngine(SCRIPT_ENGINE_ARGS); } diff --git a/Launcher/source/serialize/config/ConfigObject.java b/Launcher/source/serialize/config/ConfigObject.java index aa6ce74..8ea4da3 100644 --- a/Launcher/source/serialize/config/ConfigObject.java +++ b/Launcher/source/serialize/config/ConfigObject.java @@ -21,9 +21,6 @@ block.write(output); } - @LauncherAPI - public abstract void verify(); - @FunctionalInterface public interface Adapter { @LauncherAPI diff --git a/LauncherAuthlib/source/yggdrasil/LegacyBridge.java b/LauncherAuthlib/source/yggdrasil/LegacyBridge.java index f021d57..e7558b0 100644 --- a/LauncherAuthlib/source/yggdrasil/LegacyBridge.java +++ b/LauncherAuthlib/source/yggdrasil/LegacyBridge.java @@ -12,7 +12,7 @@ @SuppressWarnings("unused") public static boolean checkServer(String username, String serverID) throws Exception { - LogHelper.debug("LegacyBridge.checkServer, Username: '%s', server ID: %s", username, serverID); + LogHelper.debug("LegacyBridge.checkServer, Username: '%s', Server ID: %s", username, serverID); return new CheckServerRequest(username, serverID).request() != null; } @@ -37,7 +37,8 @@ } // Join server - LogHelper.debug("LegacyBridge.joinServer, Username: '%s', access token: %s, server ID: %s", username, accessToken, serverID); + LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", + username, accessToken, serverID); try { return new JoinServerRequest(username, accessToken, serverID).request() ? "OK" : "Bad Login (Clientside)"; } catch (Exception e) { diff --git a/LauncherAuthlib/source/yggdrasil/YggdrasilGameProfileRepository.java b/LauncherAuthlib/source/yggdrasil/YggdrasilGameProfileRepository.java index 4377188..5811205 100644 --- a/LauncherAuthlib/source/yggdrasil/YggdrasilGameProfileRepository.java +++ b/LauncherAuthlib/source/yggdrasil/YggdrasilGameProfileRepository.java @@ -28,7 +28,8 @@ public void findProfilesByNames(String[] usernames, Agent agent, ProfileLookupCallback callback) { int offset = 0; while (offset < usernames.length) { - String[] sliceUsernames = Arrays.copyOfRange(usernames, offset, Math.min(offset + BatchProfileByUsernameRequest.MAX_BATCH_SIZE, usernames.length)); + String[] sliceUsernames = Arrays.copyOfRange(usernames, offset, + Math.min(offset + BatchProfileByUsernameRequest.MAX_BATCH_SIZE, usernames.length)); offset += BatchProfileByUsernameRequest.MAX_BATCH_SIZE; // Batch Username-To-UUID request @@ -52,7 +53,8 @@ if (pp == null) { String username = sliceUsernames[i]; LogHelper.debug("Couldn't find profile '%s'", username); - callback.onProfileLookupFailed(new GameProfile((UUID) null, username), new ProfileNotFoundException("Server did not find the requested profile")); + callback.onProfileLookupFailed(new GameProfile((UUID) null, username), + new ProfileNotFoundException("Server did not find the requested profile")); continue; } diff --git a/LauncherAuthlib/source/yggdrasil/YggdrasilMinecraftSessionService.java b/LauncherAuthlib/source/yggdrasil/YggdrasilMinecraftSessionService.java index 5264e72..c0f4366 100644 --- a/LauncherAuthlib/source/yggdrasil/YggdrasilMinecraftSessionService.java +++ b/LauncherAuthlib/source/yggdrasil/YggdrasilMinecraftSessionService.java @@ -69,7 +69,8 @@ @Override public Map getTextures(GameProfile profile, boolean requireSecure) { LogHelper.debug("getTextures, Username: '%s'", profile.getName()); - Map textures = new EnumMap<>(MinecraftProfileTexture.Type.class); + Map textures = + new EnumMap<>(MinecraftProfileTexture.Type.class); // Add skin URL to textures map Iterator skinURL = profile.getProperties().get(ClientLauncher.SKIN_URL_PROPERTY).iterator(); @@ -96,7 +97,7 @@ @Override public GameProfile hasJoinedServer(GameProfile profile, String serverID) throws AuthenticationUnavailableException { String username = profile.getName(); - LogHelper.debug("checkServer, Username: '%s', server ID: %s", username, serverID); + LogHelper.debug("checkServer, Username: '%s', Server ID: %s", username, serverID); // Make checkServer request PlayerProfile pp; @@ -119,7 +120,8 @@ // Join server String username = profile.getName(); - LogHelper.debug("joinServer, Username: '%s', access token: %s, server ID: %s", username, accessToken, serverID); + LogHelper.debug("joinServer, Username: '%s', Access token: %s, Server ID: %s", + username, accessToken, serverID); // Make joinServer request boolean success;