diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/handlers/fileserver/FileServerHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/handlers/fileserver/FileServerHandler.java index 393443cb..2f91224f 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/handlers/fileserver/FileServerHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/handlers/fileserver/FileServerHandler.java @@ -7,13 +7,13 @@ import io.netty.handler.stream.ChunkedFile; import io.netty.util.CharsetUtil; import pro.gravit.launchserver.socket.handlers.ContentType; +import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.VerifyHelper; import java.io.File; import java.io.FileNotFoundException; import java.io.RandomAccessFile; -import java.net.URI; -import java.net.URISyntaxException; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Clock; @@ -182,12 +182,18 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr return; } - final String uri = request.uri(); + // URLDecoder tries to decode string as application/x-www-form-urlencoded which allows + character to be used as space escape therefore breaking file names with + + final String uri = IOHelper.urlDecodeStrict(request.uri()); final String path; + if (uri == null) { + sendError(ctx, NOT_FOUND); + return; + } + try { - path = Paths.get(new URI(uri).getPath()).normalize().toString().substring(1); - } catch (URISyntaxException e) { + path = Paths.get(IOHelper.getPathFromUrlFragment(uri)).normalize().toString().substring(1); + } catch (InvalidPathException e) { sendError(ctx, BAD_REQUEST); return; } diff --git a/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java b/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java index b1a4686f..1b9d17b0 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/helper/IOHelper.java @@ -5,12 +5,15 @@ import java.awt.image.BufferedImage; import java.io.*; import java.net.*; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; +import java.util.HexFormat; import java.util.Set; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -559,6 +562,34 @@ public static String urlEncode(String s) { return URLEncoder.encode(s, UNICODE_CHARSET); } + public static String urlDecodeStrict(String s) { + var builder = new StringBuilder(); + char[] charArray = s.toCharArray(); + for (int i = 0; i < charArray.length; i++) { + char c = charArray[i]; + if (c != '%') { + builder.append(c); + continue; + } + + if (i + 2 >= charArray.length) { + return null; + } + + var buffer = UNICODE_CHARSET.decode(ByteBuffer.wrap(HexFormat.of().parseHex(CharBuffer.wrap(charArray, i + 1, 2)))); + + builder.append(buffer); + + i += 2; + } + + return builder.toString(); + } + + public static String getPathFromUrlFragment(String urlFragment) { + return urlFragment.indexOf('?') < 0 ? urlFragment : urlFragment.substring(0, urlFragment.indexOf('?')); + } + public static String verifyFileName(String fileName) { return VerifyHelper.verify(fileName, IOHelper::isValidFileName, String.format("Invalid file name: '%s'", fileName)); }