mcexts/src/shared/RPC.java

143 lines
4.6 KiB
Java

package net.flashii.mcexts;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class RPC {
private static final String DEFAULT_SECRET = "meow";
private static RPCPayloadParser parser = null;
public static RPCPayloadParser getParser() {
if(parser == null)
parser = new RPCPayloadParser();
return parser;
}
public static String getSecretKey() {
return Config.getValue("RPCSecretKey.txt", DEFAULT_SECRET);
}
public static String getRequestTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
public static String createParamString(Map<String, Object> params) {
StringBuilder sb = new StringBuilder();
params.forEach((key, value) -> {
sb.append(key);
sb.append('=');
try {
sb.append(URLEncoder.encode(value.toString(), "UTF-8"));
} catch(UnsupportedEncodingException ex) {}
sb.append('&');
});
// remove the trailing ampersand
int length = sb.length();
if(length > 0)
sb.setLength(length - 1);
return sb.toString();
}
public static String createRequestSignature(String time, String path, String params)
throws Exception {
return Base64.getEncoder().encodeToString(HMAC.calculate(
HMAC.SHA256, getSecretKey(), String.format("%s#%s?%s", time, path, params)
));
}
// sigStr and bodyStr because sigStr must also include the query params if those are present
public static RPCPayload callRpc(String path, String sigStr, String bodyStr)
throws Exception {
boolean hasBody = bodyStr != null;
String time = getRequestTimestamp();
String hash = createRequestSignature(time, URLs.getRpcPath(path), sigStr);
HttpURLConnection conn = (HttpURLConnection)(new URL(URLs.getRpcUrl(path))).openConnection();
conn.setRequestMethod(hasBody ? "POST" : "GET");
conn.setRequestProperty("X-Mince-Time", time);
conn.setRequestProperty("X-Mince-Hash", hash);
if(hasBody) {
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try(OutputStream stream = conn.getOutputStream()) {
byte[] input = bodyStr.getBytes(StandardCharsets.UTF_8);
stream.write(input, 0, input.length);
}
}
StringBuilder sb = new StringBuilder();
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while((line = br.readLine()) != null)
sb.append(line);
}
return getParser().parse(sb.toString());
}
public static RPCPayload callRpc(String path, String paramStr)
throws Exception {
return callRpc(path, paramStr, paramStr);
}
public static RPCPayload callRpc(String path, Map<String, Object> params)
throws Exception {
return callRpc(path, createParamString(params));
}
public static UUID createOfflinePlayerUUID(String name) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes());
}
public static RPCPayload postAuth(String name, String remoteAddr)
throws Exception {
return postAuth(
createOfflinePlayerUUID(name),
name, remoteAddr
);
}
public static RPCPayload postAuth(UUID id, String name, String remoteAddr)
throws Exception {
HashMap<String, Object> params = new HashMap<>();
params.put("id", id);
params.put("name", name);
params.put("ip", remoteAddr);
return callRpc("/auth", params);
}
public static RPCPayload postAuth(UUID id, String name, InetAddress remoteAddr)
throws Exception {
return postAuth(id, name, remoteAddr.getHostAddress());
}
public static RPCPayload postAuth(UUID id, String name, SocketAddress sockAddr)
throws Exception {
InetAddress remoteAddr = sockAddr instanceof InetSocketAddress
? ((InetSocketAddress)sockAddr).getAddress()
: InetAddress.getLoopbackAddress();
return postAuth(id, name, remoteAddr);
}
}