package com.alecgorge.minecraft.jsonapi.packets.netty; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.util.CharsetUtil; import org.json.simpleForBukkit.JSONArray; import org.json.simpleForBukkit.JSONObject; import org.json.simpleForBukkit.parser.JSONParser; import org.json.simpleForBukkit.parser.ParseException; import com.alecgorge.minecraft.jsonapi.JSONAPI; import com.alecgorge.minecraft.jsonapi.api.v2.JSONResponse; import com.alecgorge.minecraft.jsonapi.config.UsersConfig; import com.alecgorge.minecraft.jsonapi.dynamic.Caller; import com.alecgorge.minecraft.jsonapi.permissions.JSONAPIAuthResponse; import com.alecgorge.minecraft.jsonapi.permissions.JSONAPIUser; import com.alecgorge.minecraft.jsonapi.streams.StreamingResponse; public class APIv2Handler { static Logger jsonapiLog = JSONAPI.instance.outLog; static String CONTENT_TYPE_JSON = "application/json"; FullHttpRequest request; QueryStringDecoder uri; List<JSONResponse> requests = new ArrayList<JSONResponse>(); JSONParser parser = new JSONParser(); public APIv2Handler (FullHttpRequest req) { request = req; uri = new QueryStringDecoder(request.getUri()); } public static boolean canServe(QueryStringDecoder u) { JSONAPI.dbug("can serve? " + u.path()); return u.path().startsWith("/api/2/") && !u.path().equals("/api/2/websocket"); } public FullHttpResponse serve() { try { if(uri.path().equals("/api/2/call")) { readPayload(false); return call(); } // just use websocket for subscriptions, ok? // else if(uri.path().equals("/api/2/subscribe")) { // readPayload(true); // return subscribe(); // } else if(uri.path().equals("/api/2/version")) { return version(); } else { return resp(HttpResponseStatus.NOT_FOUND, "text/plain", "Not found.\n"); } } catch (ParseException e) { StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); return resp(HttpResponseStatus.BAD_REQUEST, CONTENT_TYPE_JSON, "["+JSONResponse.APIError(errors.toString(), 4, "JSON_PARSE_ERROR", "").toJSONString()+"]\n"); } } public boolean isStream() { return uri.path().equals("/api/2/subscribe"); } public FullHttpResponse call() { JSONArray a = new JSONArray(); for(JSONResponse resp : requests) { a.add(resp.getJSONObject()); } String json = a.toJSONString(); JSONAPI.dbug("returning: " + json); return resp(HttpResponseStatus.OK, CONTENT_TYPE_JSON, json + "\n"); } public FullHttpResponse version() { JSONObject versionObj = new JSONObject(); versionObj.put("version", JSONAPI.instance.getDescription().getVersion()); versionObj.put("server_version", JSONAPI.instance.getServer().getVersion()); return resp(HttpResponseStatus.OK, CONTENT_TYPE_JSON, versionObj.toJSONString()); } public FullHttpResponse resp(HttpResponseStatus resp, String type, String body) { if(uri.parameters().containsKey("callback") && type.equals(CONTENT_TYPE_JSON)) { body = uri.parameters().get("callback") + "(" + body + ");"; } ByteBuf buf = Unpooled.copiedBuffer(body, CharsetUtil.UTF_8); FullHttpResponse r = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, resp, buf); r.headers().set("Access-Control-Allow-Origin", "*"); r.headers().set("Content-Length", buf.readableBytes()); r.headers().set("Content-Type", type); return r; } public StreamingResponse subscribe() { try { readPayload(true); } catch (ParseException e) { e.printStackTrace(); } List<String> sourceLists = new ArrayList<String>(); List<Boolean> showOlder = new ArrayList<Boolean>(); List<String> tag = new ArrayList<String>(); List<JSONObject>defaults = new ArrayList<JSONObject>(); for(JSONResponse resp : requests) { JSONAPIAuthResponse auth = resp.testLogin(true); if(auth.isAllowed() && auth.isAuthenticated()) { sourceLists.add(resp.getMethodName()); showOlder.add(resp.isShowOlder()); tag.add(resp.getTag()); } else { defaults.add(resp.getJSONObject()); } } StreamingResponse streams = new StreamingResponse(JSONAPI.instance, sourceLists, uri.parameters().containsKey("callback") ? uri.parameters().get("callback").get(0) : null, showOlder, tag, defaults); return streams; } public void readPayload(boolean stream) throws ParseException { String json = null; if (uri.parameters().containsKey("json")) { json = uri.parameters().get("json").get(0); } else { ByteBuf byteBuf = request.content(); if (byteBuf.isReadable()) { json = byteBuf.toString(Charset.forName("UTF-8")); } } if (json != null) { Object o = parser.parse(json); JSONAPI.dbug("json obj: "+ o); if(o instanceof JSONObject) { requests.add(new JSONResponse((JSONObject)o, stream)); } else if(o instanceof JSONArray) { for(Object obj : (JSONArray) o) { if(obj instanceof JSONObject) { requests.add(new JSONResponse((JSONObject)obj, stream)); } } } String username = requests.get(0).getUsername(); JSONAPIUser user = UsersConfig.config().getUser(username); if(user == null || user.getLogging()) { // logging StringBuilder b = new StringBuilder("[JSONAPI] "); b.append(stream ? "[Stream Request] " : "[API Request] "); b.append(requests.get(0).getUsername()).append(" requested: "); for(JSONResponse r : requests) { b.append(r.getMethodName()).append("(").append(r.getArguments() == null ? "" : r.getArguments()).append(")"); JSONAPIAuthResponse a = r.testLogin(false); b.append("{").append(a.isAuthenticated() ? "AUTHED" : "NOT AUTHED").append(", "); b.append(a.isAllowed() ? "ALLOWED" : "NOT ALLOWED"); Caller c = JSONAPI.instance.jsonServer.getCaller(); if(!c.methodExists(r.getMethodName()) && !JSONAPI.instance.getStreamManager().streamExists(r.getMethodName())) { b.append(", NO-EXIST"); } b.append("} "); } jsonapiLog.info(b.toString()); } } } }