diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 4cd1ba58b..1239f9b2e 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -28,6 +28,7 @@ set(LEGACY_SRC legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java legacy/org/prismlauncher/legacy/fix/online/SkinFix.java legacy/org/prismlauncher/legacy/utils/Base64.java + legacy/org/prismlauncher/legacy/utils/api/ApiServers.java legacy/org/prismlauncher/legacy/utils/api/MojangApi.java legacy/org/prismlauncher/legacy/utils/api/Texture.java legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java index 1bab76d53..695732592 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java @@ -35,32 +35,80 @@ package org.prismlauncher.legacy.fix.online; +import org.prismlauncher.legacy.utils.api.ApiServers; +import org.prismlauncher.legacy.utils.api.MojangApi; +import org.prismlauncher.legacy.utils.url.ByteArrayUrlConnection; import org.prismlauncher.legacy.utils.url.UrlUtils; import java.io.IOException; -import java.net.MalformedURLException; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; +import java.util.HashMap; +import java.util.Map; public final class OnlineModeFix { public static URLConnection openConnection(URL address, Proxy proxy) throws IOException { // we start with "http://www.minecraft.net/game/joinserver.jsp?user=..." - if (!(address.getHost().equals("www.minecraft.net") && address.getPath().equals("/game/joinserver.jsp"))) + if (!(address.getHost().equals("www.minecraft.net") && address.getPath().equals("/game/joinserver.jsp"))) { return null; - - // change it to "https://session.minecraft.net/game/joinserver.jsp?user=..." - // this seems to be the modern version of the same endpoint... - // maybe Mojang planned to patch old versions of the game to use it - // if it ever disappears this should be changed to use sessionserver.mojang.com/session/minecraft/join - // which of course has a different usage requiring JSON serialisation... - URL url; - try { - url = new URL("https", "session.minecraft.net", address.getPort(), address.getFile()); - } catch (MalformedURLException e) { - throw new AssertionError("url should be valid", e); } - return UrlUtils.openConnection(url, proxy); + Map params = new HashMap<>(); + String query = address.getQuery(); + String[] entries = query.split("&"); + for (String entry : entries) { + String[] pair = entry.split("="); + if (pair.length == 2) { + params.put(pair[0], pair[1]); + } + } + + String user = params.get("user"); + if (user == null) { + throw new AssertionError("missing user"); + } + String serverId = params.get("serverId"); + if (serverId == null) { + throw new AssertionError("missing serverId"); + } + String sessionId = params.get("sessionId"); + if (sessionId == null) { + throw new AssertionError("missing sessionId"); + } + + // sessionId hashas the form: + // token:: + String accessToken = sessionId.split(":")[1]; + + String uuid = null; + uuid = MojangApi.getUuid(user); + if (uuid == null) { + return new ByteArrayUrlConnection(("Couldn't find UUID of " + user).getBytes("utf-8")); + } + + URL url = new URL(ApiServers.getSessionURL() + "/session/minecraft/join"); + HttpURLConnection connection = (HttpURLConnection) UrlUtils.openConnection(url, proxy); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Accept", "application/json"); + try (OutputStream os = connection.getOutputStream()) { + String payload = "{" + + "\"accessToken\": \"" + accessToken + "\"," + + "\"selectedProfile\": \"" + uuid + "\"," + + "\"serverId\": \"" + serverId + "\"" + + "}"; + os.write(payload.getBytes("utf-8")); + } + int responseCode = connection.getResponseCode(); + + if (responseCode == 204) { + return new ByteArrayUrlConnection("OK".getBytes("utf-8")); + } else { + return new ByteArrayUrlConnection("Bad login".getBytes("utf-8")); + } } } diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/ApiServers.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/ApiServers.java new file mode 100644 index 000000000..6a90db0c4 --- /dev/null +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/ApiServers.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Fjord Launcher - Minecraft Launcher + * Copyright (C) 2024 Evan Goode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.legacy.utils.api; + +public class ApiServers { + public static String getAuthURL() { + return getPropertyWithFallback("minecraft.api.auth.host", "https://authserver.mojang.com"); + } + public static String getAccountURL() { + return getPropertyWithFallback("minecraft.api.account.host", "https://api.mojang.com"); + } + public static String getSessionURL() { + return getPropertyWithFallback("minecraft.api.session.host", "https://sessionserver.mojang.com"); + } + public static String getServicesURL() { + return getPropertyWithFallback("minecraft.api.services.host", "https://api.minecraftservices.com"); + } + private static String getPropertyWithFallback(String key, String fallback) { + String value = System.getProperty(key); + if (value == null) { + return fallback; + } + return value; + } +} diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java index 41f7f9114..977cb2769 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/api/MojangApi.java @@ -36,6 +36,7 @@ package org.prismlauncher.legacy.utils.api; import org.prismlauncher.legacy.utils.Base64; +import org.prismlauncher.legacy.utils.api.ApiServers; import org.prismlauncher.legacy.utils.json.JsonParser; import java.io.IOException; @@ -49,7 +50,7 @@ import java.util.Map; @SuppressWarnings("unchecked") public final class MojangApi { public static String getUuid(String username) throws IOException { - try (InputStream in = new URL("https://api.mojang.com/users/profiles/minecraft/" + username).openStream()) { + try (InputStream in = new URL(ApiServers.getAccountURL() + "/users/profiles/minecraft/" + username).openStream()) { Map map = (Map) JsonParser.parse(in); return (String) map.get("id"); } @@ -79,7 +80,7 @@ public final class MojangApi { } public static Map getTextures(String player) throws IOException { - try (InputStream profileIn = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + player).openStream()) { + try (InputStream profileIn = new URL(ApiServers.getSessionURL() + "/session/minecraft/profile/" + player).openStream()) { Map profile = (Map) JsonParser.parse(profileIn); for (Map property : (Iterable>) profile.get("properties")) {