diff --git a/app/src/main/java/org/skinner/RestAPI.java b/app/src/main/java/org/skinner/RestAPI.java index 202a210..51fd013 100644 --- a/app/src/main/java/org/skinner/RestAPI.java +++ b/app/src/main/java/org/skinner/RestAPI.java @@ -1,21 +1,24 @@ package org.skinner; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.*; +import java.util.Base64; -import org.skinner.json.*; +import org.skinner.json.JSON; +import org.skinner.json.JSONArray; +import org.skinner.json.JSONObject; -import com.sun.net.httpserver.*; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; public class RestAPI extends SafeHttpHandler { - + RestAPI(String root) { super(root); } + @Override protected void handle(HttpExchange exchange, URI uri) throws Exception { String path = uri.getPath(); switch (exchange.getRequestMethod().toUpperCase()) @@ -26,11 +29,11 @@ public class RestAPI extends SafeHttpHandler { case "/skins": getSkins(exchange); return; - + case "/profiles": profiles(exchange); return; - + // Template-ish routes default: if (path.startsWith("/skin/")) { @@ -40,14 +43,14 @@ public class RestAPI extends SafeHttpHandler { break; } break; - + case "POST": switch (path) { case "/skin": addSkin(exchange); return; - + case "/profile": register(exchange); return; @@ -57,14 +60,14 @@ public class RestAPI extends SafeHttpHandler { break; } break; - + case "PATCH": switch (path) { case "/profile": updateProfile(exchange, uri); return; - + default: if (path.startsWith("/skin/")) { renameSkin(exchange, uri); @@ -72,7 +75,7 @@ public class RestAPI extends SafeHttpHandler { } break; } - + case "DELETE": switch (path) { @@ -86,26 +89,26 @@ public class RestAPI extends SafeHttpHandler { } Headers headers = exchange.getResponseHeaders(); headers.add("Content-Type", "text/plain"); - + byte[] response = "Missing API endpoint".getBytes(); exchange.sendResponseHeaders(404, response.length); exchange.getResponseBody().write(response); exchange.close(); return; } - + protected void getSkin(HttpExchange exchange, URI uri) throws Exception { String[] pathParts = uri.getPath().split("/"); String hash = pathParts[pathParts.length-1].split("\\.")[0]; - + Skin skin = Database.getSkin(hash); if (skin == null) { notfound(exchange, "Skin not found"); return; } - + Query query = new Query(uri.getQuery()); - + byte[] response = query.getOrDefault("legacy", "false").toLowerCase().equals("true") ? skin.getPngOld() : skin.getPng(); @@ -114,10 +117,10 @@ public class RestAPI extends SafeHttpHandler { exchange.getResponseBody().write(response); exchange.close(); } - + protected void getSkins(HttpExchange exchange) throws Exception { HashLabelPair[] pairs = Database.getSkinHashesAndLabels(); - + JSONArray array = new JSONArray(); for (HashLabelPair pair : pairs) { JSONObject object = new JSONObject(); @@ -127,66 +130,6 @@ public class RestAPI extends SafeHttpHandler { object.put("png_old", JSON.from(getRoot() + "skin/"+pair.getHash()+".png?legacy=true")); array.add(object); } - - byte[] response = array.toString().getBytes(); - exchange.getResponseHeaders().add("Content-Type", "application/json"); - exchange.sendResponseHeaders(200, response.length); - exchange.getResponseBody().write(response); - exchange.close(); - } - - protected void getSkinsFull(HttpExchange exchange) throws Exception { - Skin[] skins = Database.getSkins(); - - JSONArray array = new JSONArray(); - for (Skin skin : skins) - array.add(skinToResponseJSON(skin)); - - byte[] response = array.toString().getBytes(); - exchange.getResponseHeaders().add("Content-Type", "application/json"); - exchange.sendResponseHeaders(200, response.length); - exchange.getResponseBody().write(response); - exchange.close(); - } - - protected void addSkin(HttpExchange exchange) throws Exception { - MultipartForm form = new MultipartForm(exchange); - - if (!form.contains("skin")) { - text(exchange, 400, "Expected \"skin\" field not provided"); - return; - } - - InputStream is = new ByteArrayInputStream(form.get("skin")); - Skin skin = Skin.loadFromImage(is, form.getString("label")); - Database.addSkin(skin); - ok(exchange); - } - - protected void renameSkin(HttpExchange exchange, URI uri) throws Exception { - String[] pathParts = uri.getPath().split("/"); - String hash = pathParts[pathParts.length-1]; - - String label = new String(exchange.getRequestBody().readAllBytes()); - - Database.updateSkinLabel(hash, label); - ok(exchange); - } - - protected void deleteSkin(HttpExchange exchange, URI uri) throws Exception { - String[] pathParts = uri.getPath().split("/"); - String hash = pathParts[pathParts.length-1]; - - Database.deleteSkin(hash); - ok(exchange); - } - - protected void profiles(HttpExchange exchange) throws Exception { - Profile[] profiles = Database.getProfiles(); - - JSONArray array = new JSONArray(); - for (Profile profile : profiles) - array.add(profileToResponseJSON(profile)); byte[] response = array.toString().getBytes(); exchange.getResponseHeaders().add("Content-Type", "application/json"); @@ -194,10 +137,72 @@ public class RestAPI extends SafeHttpHandler { exchange.getResponseBody().write(response); exchange.close(); } - + + protected void getSkinsFull(HttpExchange exchange) throws Exception { + Skin[] skins = Database.getSkins(); + + JSONArray array = new JSONArray(); + for (Skin skin : skins) { + array.add(skinToResponseJSON(skin)); + } + + byte[] response = array.toString().getBytes(); + exchange.getResponseHeaders().add("Content-Type", "application/json"); + exchange.sendResponseHeaders(200, response.length); + exchange.getResponseBody().write(response); + exchange.close(); + } + + protected void addSkin(HttpExchange exchange) throws Exception { + MultipartForm form = new MultipartForm(exchange); + + if (!form.contains("skin")) { + text(exchange, 400, "Expected \"skin\" field not provided"); + return; + } + + InputStream is = new ByteArrayInputStream(form.get("skin")); + Skin skin = Skin.loadFromImage(is, form.getString("label")); + Database.addSkin(skin); + ok(exchange); + } + + protected void renameSkin(HttpExchange exchange, URI uri) throws Exception { + String[] pathParts = uri.getPath().split("/"); + String hash = pathParts[pathParts.length-1]; + + String label = new String(exchange.getRequestBody().readAllBytes()); + + Database.updateSkinLabel(hash, label); + ok(exchange); + } + + protected void deleteSkin(HttpExchange exchange, URI uri) throws Exception { + String[] pathParts = uri.getPath().split("/"); + String hash = pathParts[pathParts.length-1]; + + Database.deleteSkin(hash); + ok(exchange); + } + + protected void profiles(HttpExchange exchange) throws Exception { + Profile[] profiles = Database.getProfiles(); + + JSONArray array = new JSONArray(); + for (Profile profile : profiles) { + array.add(profileToResponseJSON(profile)); + } + + byte[] response = array.toString().getBytes(); + exchange.getResponseHeaders().add("Content-Type", "application/json"); + exchange.sendResponseHeaders(200, response.length); + exchange.getResponseBody().write(response); + exchange.close(); + } + protected void register(HttpExchange exchange) throws Exception { MultipartForm form = new MultipartForm(exchange); - + if (!form.contains("username")) { text(exchange, 400, "Expected \"username\" field not provided"); return; @@ -206,14 +211,14 @@ public class RestAPI extends SafeHttpHandler { text(exchange, 400, "Expected \"password\" field not provided"); return; } - + String username = form.getString("username"); String password = form.getString("password"); - + String salt = Profile.generateSalt(); String hash = Profile.generateHash(password, salt); String uuid = Profile.generateUUID(); - + Database.addProfile(new Profile( -1, username, @@ -224,7 +229,7 @@ public class RestAPI extends SafeHttpHandler { )); ok(exchange); } - + protected void updateProfile(HttpExchange exchange, URI uri) throws Exception { Headers headers = exchange.getRequestHeaders(); if (!headers.containsKey("Authorization")) { @@ -237,33 +242,34 @@ public class RestAPI extends SafeHttpHandler { } String auth = headers.getFirst("Authorization"); String[] authParts = auth.trim().split(" "); - + if (!authParts[0].equals("Basic")) { text(exchange, 401, "Invalid authorization method"); return; } - + String[] cerd = new String(Base64.getDecoder().decode(authParts[1])).split(":"); if (cerd.length != 2) { text(exchange, 401, "Invalid cerdentials format"); return; } - + Profile profile = Database.getProfileByName(cerd[0]); - + if (profile == null || !Profile.generateHash(cerd[1], profile.getSalt()).equals(profile.getPassword())) { text(exchange, 401, "Failed to authorize"); return; } - + Query query = new Query(uri.getQuery()); - - if (query.containsKey("skin")) + + if (query.containsKey("skin")) { Database.setProfileSkin(profile, Database.getSkin(query.get("skin"))); - + } + ok(exchange); } - + private JSONObject skinToResponseJSON(Skin skin) { JSONObject object = new JSONObject(); object.put("hash", JSON.from(skin.getHash())); @@ -271,7 +277,7 @@ public class RestAPI extends SafeHttpHandler { object.put("png_old", JSON.from(getRoot() + "skin/"+skin.getHash()+".png?legacy=true")); return object; } - + private JSONObject profileToResponseJSON(Profile profile) { JSONObject object = new JSONObject(); object.put("username", JSON.from(profile.getUsername())); @@ -279,5 +285,5 @@ public class RestAPI extends SafeHttpHandler { object.put("skin", skinToResponseJSON(profile.getSkin())); return object; } - + }