From ead59c0246639837bbdbfcc6a0ca443a1ab06d6c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 20 Nov 2022 09:16:30 +0000 Subject: [PATCH] Improve the skin fix code - Spoof 404 instead of keeping original URL. - Move JsonParseException to the package. - Pass proxy as null to reduce code duplication. Signed-off-by: TheKodeToad --- libraries/launcher/CMakeLists.txt | 3 +- .../exception/JsonParseException.java | 48 ++++++++++++++ .../launcher/org/prismlauncher/fix/Fixes.java | 6 +- .../org/prismlauncher/fix/skins/Handler.java | 60 +++++++---------- .../org/prismlauncher/fix/skins/SkinFix.java | 2 +- .../org/prismlauncher/utils/JsonParser.java | 12 +--- .../utils/url/NullConnection.java | 66 +++++++++++++++++++ .../utils/{ => url}/UrlUtils.java | 35 ++-------- 8 files changed, 153 insertions(+), 79 deletions(-) create mode 100644 libraries/launcher/org/prismlauncher/exception/JsonParseException.java create mode 100644 libraries/launcher/org/prismlauncher/utils/url/NullConnection.java rename libraries/launcher/org/prismlauncher/utils/{ => url}/UrlUtils.java (74%) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index acc276218..65a8241eb 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -23,7 +23,8 @@ set(SRC org/prismlauncher/utils/JsonParser.java org/prismlauncher/utils/Parameters.java org/prismlauncher/utils/ReflectionUtils.java - org/prismlauncher/utils/UrlUtils.java + org/prismlauncher/utils/url/NullConnection.java + org/prismlauncher/utils/url/UrlUtils.java org/prismlauncher/utils/logging/Level.java org/prismlauncher/utils/logging/Log.java net/minecraft/Launcher.java diff --git a/libraries/launcher/org/prismlauncher/exception/JsonParseException.java b/libraries/launcher/org/prismlauncher/exception/JsonParseException.java new file mode 100644 index 000000000..5983eb883 --- /dev/null +++ b/libraries/launcher/org/prismlauncher/exception/JsonParseException.java @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 TheKodeToad + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.exception; + +import java.io.IOException; + +public class JsonParseException extends IOException { + + private static final long serialVersionUID = 1L; + + public JsonParseException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/libraries/launcher/org/prismlauncher/fix/Fixes.java b/libraries/launcher/org/prismlauncher/fix/Fixes.java index ad472fea5..e099b653c 100644 --- a/libraries/launcher/org/prismlauncher/fix/Fixes.java +++ b/libraries/launcher/org/prismlauncher/fix/Fixes.java @@ -45,11 +45,11 @@ public final class Fixes { private static final Fix[] FIXES = { new SkinFix() }; - public static void apply(Parameters parameters) { - List fixes = parameters.getList("fixes", Collections.emptyList()); + public static void apply(Parameters params) { + List fixes = params.getList("fixes", Collections.emptyList()); for (Fix fix : FIXES) - if (fixes.contains(fix.getName()) && fix.isApplicable(parameters)) + if (fixes.contains(fix.getName()) && fix.isApplicable(params)) fix.apply(); } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java index 2bffeae96..0f5b556f8 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/Handler.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/Handler.java @@ -45,61 +45,51 @@ import java.util.Map; import org.prismlauncher.utils.Base64; import org.prismlauncher.utils.JsonParser; -import org.prismlauncher.utils.UrlUtils; +import org.prismlauncher.utils.url.NullConnection; +import org.prismlauncher.utils.url.UrlUtils; @SuppressWarnings("unchecked") final class Handler extends URLStreamHandler { private URL redirect(URL address) throws IOException { String skinOwner = findSkinOwner(address); - if (skinOwner != null) - return convertSkin(address, skinOwner); + return convert(skinOwner, "SKIN"); String capeOwner = findCapeOwner(address); - if (capeOwner != null) - return convertCape(address, capeOwner); + return convert(capeOwner, "CAPE"); return address; } @Override protected URLConnection openConnection(URL address) throws IOException { - address = redirect(address); - return UrlUtils.openHttpConnection(address); + return openConnection(address, null); } @Override protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { address = redirect(address); + + if (address == null) + return NullConnection.INSTANCE; + return UrlUtils.openHttpConnection(address, proxy); } - private URL convertSkin(URL defaultUrl, String owner) throws IOException { + private URL convert(String owner, String name) throws IOException { Map textures = getTextures(owner); if (textures != null) { - textures = (Map) textures.get("SKIN"); - return new URL((String) textures.get("url")); - } - - return defaultUrl; - } - - private URL convertCape(URL defaultUrl, String owner) throws IOException { - Map textures = getTextures(owner); - - if (textures != null) { - textures = (Map) textures.get("CAPE"); - + textures = (Map) textures.get(name); if (textures == null) - return defaultUrl; + return null; return new URL((String) textures.get("url")); } - return defaultUrl; + return null; } private static Map getTextures(String owner) throws IOException { @@ -116,6 +106,7 @@ final class Handler extends URLStreamHandler { Map result = (Map) JsonParser .parse(new String(Base64.decode((String) property.get("value")))); result = (Map) result.get("textures"); + return result; } } @@ -128,11 +119,11 @@ final class Handler extends URLStreamHandler { private static String findSkinOwner(URL address) { switch (address.getHost()) { case "www.minecraft.net": - return stripPng(strip(address.getPath(), "/skin/")); + return stripIfPrefixed(address.getPath(), "/skin/"); case "s3.amazonaws.com": case "skins.minecraft.net": - return stripPng(strip(address.getPath(), "/MinecraftSkins/")); + return stripIfPrefixed(address.getPath(), "/MinecraftSkins/"); } return null; @@ -141,26 +132,25 @@ final class Handler extends URLStreamHandler { private static String findCapeOwner(URL address) { switch (address.getHost()) { case "www.minecraft.net": - return stripPng(strip(address.getQuery(), "user=")); + return stripIfPrefixed(address.getQuery(), "user="); case "s3.amazonaws.com": case "skins.minecraft.net": - return stripPng(strip(address.getPath(), "/MinecraftCloaks/")); + return stripIfPrefixed(address.getPath(), "/MinecraftCloaks/"); } return null; } - private static String stripPng(String string) { - if (string != null && string.endsWith(".png")) - return string.substring(0, string.lastIndexOf('.')); + private static String stripIfPrefixed(String string, String prefix) { + if (string != null && string.startsWith(prefix)) { + string = string.substring(prefix.length()); - return string; - } + if (string.endsWith(".png")) + string = string.substring(0, string.lastIndexOf('.')); - private static String strip(String string, String prefix) { - if (string != null && string.startsWith(prefix)) - return string.substring(prefix.length()); + return string; + } return null; } diff --git a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java index 927c10142..e26dfb6f1 100644 --- a/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java +++ b/libraries/launcher/org/prismlauncher/fix/skins/SkinFix.java @@ -41,8 +41,8 @@ import java.net.URLStreamHandlerFactory; import org.prismlauncher.fix.Fix; import org.prismlauncher.utils.Parameters; -import org.prismlauncher.utils.UrlUtils; import org.prismlauncher.utils.logging.Log; +import org.prismlauncher.utils.url.UrlUtils; /** * Fixes skins by redirecting to other URLs. diff --git a/libraries/launcher/org/prismlauncher/utils/JsonParser.java b/libraries/launcher/org/prismlauncher/utils/JsonParser.java index d1803c896..378e58235 100644 --- a/libraries/launcher/org/prismlauncher/utils/JsonParser.java +++ b/libraries/launcher/org/prismlauncher/utils/JsonParser.java @@ -59,6 +59,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.prismlauncher.exception.JsonParseException; + /** * Single-file JSON parser to allow for usage in versions without GSON. */ @@ -420,14 +422,4 @@ public final class JsonParser { return character() == 'n' && read() == 'u' && read() == 'l' && read() == 'l'; } - public static class JsonParseException extends IOException { - - private static final long serialVersionUID = 1L; - - public JsonParseException(String message) { - super(message); - } - - } - } \ No newline at end of file diff --git a/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java b/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java new file mode 100644 index 000000000..628488e8d --- /dev/null +++ b/libraries/launcher/org/prismlauncher/utils/url/NullConnection.java @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 TheKodeToad + * + * 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. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.utils.url; + +import java.io.IOException; +import java.net.HttpURLConnection; + +/** + * Spoof 404 response from server to avoid unnecessary requests. + */ +public final class NullConnection extends HttpURLConnection { + + public static final NullConnection INSTANCE = new NullConnection(); + + public NullConnection() { + super(null); + } + + @Override + public void connect() throws IOException { + responseCode = 404; + } + + @Override + public void disconnect() { + } + + @Override + public boolean usingProxy() { + return false; + } + +} diff --git a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java similarity index 74% rename from libraries/launcher/org/prismlauncher/utils/UrlUtils.java rename to libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java index 362feb4e3..751f833fd 100644 --- a/libraries/launcher/org/prismlauncher/utils/UrlUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/url/UrlUtils.java @@ -33,7 +33,7 @@ * along with this program. If not, see . */ -package org.prismlauncher.utils; +package org.prismlauncher.utils.url; import java.io.IOException; import java.lang.invoke.MethodHandle; @@ -54,7 +54,6 @@ public final class UrlUtils { private static URLStreamHandler http; private static MethodHandle openConnection; - private static MethodHandle openConnectionProxied; static { try { @@ -64,18 +63,10 @@ public final class UrlUtils { getURLStreamHandler.setAccessible(true); http = (URLStreamHandler) getURLStreamHandler.invoke(null, "http"); - // reflection is required due to not having access - // unreflect is used due to the potential frequency of calls - Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class); + Method openConnectionReflect = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, + Proxy.class); openConnectionReflect.setAccessible(true); openConnection = MethodHandles.lookup().unreflect(openConnectionReflect); - - // a second method which takes in a proxy is required for a fully-functional - // URLStreamHandler - Method openConnectionReflectProxied = URLStreamHandler.class.getDeclaredMethod("openConnection", URL.class, - Proxy.class); - openConnectionReflectProxied.setAccessible(true); - openConnectionProxied = MethodHandles.lookup().unreflect(openConnectionReflectProxied); } catch (Throwable e) { Log.error("URL reflection failed - some features may not work", e); } @@ -87,20 +78,16 @@ public final class UrlUtils { * @return true if all features can be used */ public static boolean isSupported() { - return http != null && openConnection != null && openConnectionProxied != null; - } - - public static URLConnection openHttpConnection(URL url) throws IOException { - return openConnection(http, url); + return http != null && openConnection != null; } public static URLConnection openHttpConnection(URL url, Proxy proxy) throws IOException { return openConnection(http, url, proxy); } - public static URLConnection openConnection(URLStreamHandler handler, URL url) throws IOException { + public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { try { - return (URLConnection) openConnection.invokeExact(handler, url); + return (URLConnection) openConnection.invokeExact(handler, url, proxy); } catch (IOException | Error | RuntimeException e) { throw e; // rethrow if possible } catch (Throwable e) { @@ -108,14 +95,4 @@ public final class UrlUtils { } } - public static URLConnection openConnection(URLStreamHandler handler, URL url, Proxy proxy) throws IOException { - try { - return (URLConnection) openConnectionProxied.invokeExact(handler, url, proxy); - } catch (IOException | Error | RuntimeException e) { - throw e; - } catch (Throwable e) { - throw new Error(e); - } - } - }