diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java b/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java index e55e7323..fd44f2ad 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/SkinUploader.java @@ -179,7 +179,7 @@ public class SkinUploader implements Closeable { sendingSkin = true; status = statusMsg; - return gateway.uploadSkin(mc.getSession(), new SkinUpload(skinType, localSkin == null ? null : localSkin.toURI(), skinMetadata)).handle((response, throwable) -> { + return gateway.uploadSkin(new SkinUpload(mc.getSession(), skinType, localSkin == null ? null : localSkin.toURI(), skinMetadata)).handle((response, throwable) -> { if (throwable == null) { LiteLoaderLogger.info("Upload completed with: %s", response); setError(null); diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java index 22acd615..2ea0924a 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/BethlehemSkinServer.java @@ -10,7 +10,6 @@ import com.mojang.util.UUIDTypeAdapter; import com.voxelmodpack.hdskins.util.IndentedToStringStyle; import com.voxelmodpack.hdskins.util.MoreHttpResponses; import com.voxelmodpack.hdskins.util.NetClient; -import net.minecraft.util.Session; import java.io.IOException; import java.util.Locale; @@ -40,12 +39,12 @@ public class BethlehemSkinServer implements SkinServer { } @Override - public SkinUploadResponse performSkinUpload(Session session, SkinUpload upload) throws IOException, AuthenticationException { - SkinServer.verifyServerConnection(session, SERVER_ID); + public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException { + SkinServer.verifyServerConnection(upload.getSession(), SERVER_ID); NetClient client = new NetClient("POST", address); - client.putHeaders(createHeaders(session, upload)); + client.putHeaders(createHeaders(upload)); if (upload.getImage() != null) { client.putFile(upload.getType().toString().toLowerCase(Locale.US), "image/png", upload.getImage()); @@ -59,11 +58,11 @@ public class BethlehemSkinServer implements SkinServer { } } - protected Map createHeaders(Session session, SkinUpload upload) { + protected Map createHeaders(SkinUpload upload) { Builder builder = ImmutableMap.builder() - .put("accessToken", session.getToken()) - .put("user", session.getUsername()) - .put("uuid", UUIDTypeAdapter.fromUUID(session.getProfile().getId())) + .put("accessToken", upload.getSession().getToken()) + .put("user", upload.getSession().getUsername()) + .put("uuid", UUIDTypeAdapter.fromUUID(upload.getSession().getProfile().getId())) .put("type", upload.getType().toString().toLowerCase(Locale.US)); if (upload.getImage() == null) { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java index 3a18c9d5..f7f051ab 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/LegacySkinServer.java @@ -16,7 +16,6 @@ import com.voxelmodpack.hdskins.util.MoreHttpResponses; import com.voxelmodpack.hdskins.util.NetClient; import com.voxelmodpack.hdskins.util.TexturesPayloadBuilder; import net.minecraft.client.Minecraft; -import net.minecraft.util.Session; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHeaders; @@ -109,16 +108,16 @@ public class LegacySkinServer implements SkinServer { } @Override - public SkinUploadResponse performSkinUpload(Session session, SkinUpload upload) throws IOException, AuthenticationException { + public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException { if (Strings.isNullOrEmpty(gateway)) { throw gatewayUnsupported(); } - SkinServer.verifyServerConnection(session, SERVER_ID); + SkinServer.verifyServerConnection(upload.getSession(), SERVER_ID); NetClient client = new NetClient("POST", gateway); - client.putFormData(createHeaders(session, upload), "image/png"); + client.putFormData(createHeaders(upload), "image/png"); if (upload.getImage() != null) { client.putFile(upload.getType().toString().toLowerCase(Locale.US), "image/png", upload.getImage()); @@ -141,10 +140,10 @@ public class LegacySkinServer implements SkinServer { return new UnsupportedOperationException("Server does not have a gateway."); } - private Map createHeaders(Session session, SkinUpload upload) { + private Map createHeaders(SkinUpload upload) { Builder builder = ImmutableMap.builder() - .put("user", session.getUsername()) - .put("uuid", UUIDTypeAdapter.fromUUID(session.getProfile().getId())) + .put("user", upload.getSession().getUsername()) + .put("uuid", UUIDTypeAdapter.fromUUID(upload.getSession().getProfile().getId())) .put("type", upload.getType().toString().toLowerCase(Locale.US)); if (upload.getImage() == null) { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java index 1fa6505d..60383684 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinServer.java @@ -32,10 +32,10 @@ public interface SkinServer extends Exposable { MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException; - SkinUploadResponse performSkinUpload(Session session, SkinUpload upload) throws IOException, AuthenticationException; + SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException; - default CompletableFuture uploadSkin(Session session, SkinUpload upload) { - return CallableFutures.asyncFailableFuture(() -> performSkinUpload(session, upload), HDSkinManager.skinUploadExecutor); + default CompletableFuture uploadSkin(SkinUpload upload) { + return CallableFutures.asyncFailableFuture(() -> performSkinUpload(upload), HDSkinManager.skinUploadExecutor); } default CompletableFuture getPreviewTextures(GameProfile profile) { diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinUpload.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinUpload.java index 9789bc10..5e514019 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinUpload.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/SkinUpload.java @@ -1,24 +1,35 @@ package com.voxelmodpack.hdskins.server; +import net.minecraft.util.Session; + import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type; import java.net.URI; import java.util.Map; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +@Immutable public class SkinUpload { - private URI image; - private Map metadata; - private MinecraftProfileTexture.Type type; + private final Session session; + private final URI image; + private final Map metadata; + private final Type type; - public SkinUpload(MinecraftProfileTexture.Type type, @Nullable URI image, Map metadata) { + public SkinUpload(Session session, Type type, @Nullable URI image, Map metadata) { + this.session = session; this.image = image; this.metadata = metadata; this.type = type; } + public Session getSession() { + return session; + } + @Nullable public URI getImage() { return image; diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java b/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java index f5a9ece7..c874f261 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/server/ValhallaSkinServer.java @@ -1,7 +1,6 @@ package com.voxelmodpack.hdskins.server; import com.google.common.base.Preconditions; -import com.google.gson.JsonObject; import com.google.gson.annotations.Expose; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; @@ -14,23 +13,18 @@ import com.voxelmodpack.hdskins.util.MoreHttpResponses; import net.minecraft.client.Minecraft; import net.minecraft.util.Session; import org.apache.http.HttpHeaders; -import org.apache.http.NameValuePair; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.message.BasicNameValuePair; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.Locale; -import java.util.Map; import java.util.UUID; -import javax.annotation.Nullable; - @ServerType("valhalla") public class ValhallaSkinServer implements SkinServer { @@ -45,88 +39,80 @@ public class ValhallaSkinServer implements SkinServer { @Override public MinecraftTexturesPayload loadProfileData(GameProfile profile) throws IOException { - try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, new HttpGet(getTexturesURI(profile)))) { if (response.ok()) { - return readJson(response, MinecraftTexturesPayload.class); + return response.unwrapAsJson(MinecraftTexturesPayload.class); } + throw new IOException("Server sent non-ok response code: " + response.getResponseCode()); } } @Override - public SkinUploadResponse performSkinUpload(Session session, SkinUpload upload) throws IOException, AuthenticationException { - URI image = upload.getImage(); - Map metadata = upload.getMetadata(); - MinecraftProfileTexture.Type type = upload.getType(); - - authorize(session); - + public SkinUploadResponse performSkinUpload(SkinUpload upload) throws IOException, AuthenticationException { try { - return upload(session, image, type, metadata); + return uploadPlayerSkin(upload); } catch (IOException e) { if (e.getMessage().equals("Authorization failed")) { accessToken = null; - authorize(session); - return upload(session, image, type, metadata); + return uploadPlayerSkin(upload); } + throw e; } } - private SkinUploadResponse upload(Session session, @Nullable URI image, MinecraftProfileTexture.Type type, Map metadata) throws IOException { - GameProfile profile = session.getProfile(); + private SkinUploadResponse uploadPlayerSkin(SkinUpload upload) throws IOException, AuthenticationException { + authorize(upload.getSession()); - if (image == null) { - return resetSkin(profile, type); - } - switch (image.getScheme()) { + switch (upload.getSchemaAction()) { + case "none": + return resetSkin(upload); case "file": - return uploadFile(new File(image), profile, type, metadata); + return uploadFile(upload); case "http": case "https": - return uploadUrl(image, profile, type, metadata); + return uploadUrl(upload); default: - throw new IOException("Unsupported URI scheme: " + image.getScheme()); + throw new IOException("Unsupported URI scheme: " + upload.getSchemaAction()); } - } - private SkinUploadResponse resetSkin(GameProfile profile, MinecraftProfileTexture.Type type) throws IOException { + private SkinUploadResponse resetSkin(SkinUpload upload) throws IOException { return upload(RequestBuilder.delete() - .setUri(buildUserTextureUri(profile, type)) + .setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType())) .addHeader(HttpHeaders.AUTHORIZATION, this.accessToken) .build()); } - private SkinUploadResponse uploadFile(File file, GameProfile profile, MinecraftProfileTexture.Type type, Map metadata) throws IOException { - MultipartEntityBuilder b = MultipartEntityBuilder.create(); - b.addBinaryBody("file", file, ContentType.create("image/png"), file.getName()); - metadata.forEach(b::addTextBody); + private SkinUploadResponse uploadFile(SkinUpload upload) throws IOException { + final File file = new File(upload.getImage()); + + MultipartEntityBuilder b = MultipartEntityBuilder.create() + .addBinaryBody("file", file, ContentType.create("image/png"), file.getName()); + + upload.getMetadata().forEach(b::addTextBody); return upload(RequestBuilder.put() - .setUri(buildUserTextureUri(profile, type)) + .setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType())) .addHeader(HttpHeaders.AUTHORIZATION, this.accessToken) .setEntity(b.build()) .build()); } - private SkinUploadResponse uploadUrl(URI uri, GameProfile profile, MinecraftProfileTexture.Type type, Map metadata) throws IOException { - + private SkinUploadResponse uploadUrl(SkinUpload upload) throws IOException { return upload(RequestBuilder.post() - .setUri(buildUserTextureUri(profile, type)) + .setUri(buildUserTextureUri(upload.getSession().getProfile(), upload.getType())) .addHeader(HttpHeaders.AUTHORIZATION, this.accessToken) - .addParameter("file", uri.toString()) - .addParameters(metadata.entrySet().stream() - .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) - .toArray(NameValuePair[]::new)) + .addParameter("file", upload.getImage().toString()) + .addParameters(MoreHttpResponses.mapAsParameters(upload.getMetadata())) .build()); } private SkinUploadResponse upload(HttpUriRequest request) throws IOException { try (MoreHttpResponses response = MoreHttpResponses.execute(HDSkinManager.httpClient, request)) { - return readJson(response, SkinUploadResponse.class); + return response.unwrapAsJson(SkinUploadResponse.class); } } @@ -152,24 +138,12 @@ public class ValhallaSkinServer implements SkinServer { this.accessToken = response.accessToken; } - private T readJson(MoreHttpResponses resp, Class cl) throws IOException { - String type = resp.getResponse().getEntity().getContentType().getValue(); - if (!"application/json".equals(type)) { - throw new IOException("Server returned a non-json response!"); - } - if (resp.ok()) { - return resp.json(cl); - } - throw new IOException(resp.json(JsonObject.class).get("message").getAsString()); - - } - private AuthHandshake authHandshake(String name) throws IOException { try (MoreHttpResponses resp = MoreHttpResponses.execute(HDSkinManager.httpClient, RequestBuilder.post() .setUri(getHandshakeURI()) .addParameter("name", name) .build())) { - return readJson(resp, AuthHandshake.class); + return resp.unwrapAsJson(AuthHandshake.class); } } @@ -179,7 +153,7 @@ public class ValhallaSkinServer implements SkinServer { .addParameter("name", name) .addParameter("verifyToken", String.valueOf(verifyToken)) .build())) { - return readJson(resp, AuthResponse.class); + return resp.unwrapAsJson(AuthResponse.class); } } diff --git a/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java b/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java index 1f0df66f..22502be8 100644 --- a/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java +++ b/src/hdskins/java/com/voxelmodpack/hdskins/util/MoreHttpResponses.java @@ -1,12 +1,15 @@ package com.voxelmodpack.hdskins.util; import com.google.common.io.CharStreams; +import com.google.gson.JsonObject; import com.voxelmodpack.hdskins.server.SkinServer; import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; import java.io.BufferedReader; import java.io.IOException; @@ -14,6 +17,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.stream.Stream; /** @@ -32,6 +36,10 @@ public interface MoreHttpResponses extends AutoCloseable { return getResponse().getStatusLine().getStatusCode(); } + default String getContentType() { + return getResponse().getEntity().getContentType().getValue(); + } + default InputStream getInputStream() throws IOException { return getResponse().getEntity().getContent(); } @@ -64,6 +72,18 @@ public interface MoreHttpResponses extends AutoCloseable { } } + default T unwrapAsJson(Type type) throws IOException { + if (!"application/json".equals(getContentType())) { + throw new IOException("Server returned a non-json response!"); + } + + if (ok()) { + return json(type); + } + + throw new IOException(json(JsonObject.class).get("message").getAsString()); + } + @Override default void close() throws IOException { this.getResponse().close(); @@ -73,4 +93,12 @@ public interface MoreHttpResponses extends AutoCloseable { CloseableHttpResponse response = client.execute(request); return () -> response; } + + static NameValuePair[] mapAsParameters(Map parameters) { + return parameters.entrySet().stream() + .map(entry -> + new BasicNameValuePair(entry.getKey(), entry.getValue()) + ) + .toArray(NameValuePair[]::new); + } }