package com.forgeessentials.remote; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import net.minecraft.server.MinecraftServer; import com.forgeessentials.api.APIRegistry; import com.forgeessentials.api.UserIdent; import com.forgeessentials.api.remote.RemoteHandler; import com.forgeessentials.api.remote.RemoteHandler.PermissionException; import com.forgeessentials.api.remote.RemoteHandler.RemoteException; import com.forgeessentials.api.remote.RemoteManager; import com.forgeessentials.api.remote.RemoteRequest; import com.forgeessentials.api.remote.RemoteRequest.JsonRemoteRequest; import com.forgeessentials.api.remote.RemoteResponse; import com.forgeessentials.api.remote.RemoteSession; import com.forgeessentials.util.output.LoggingHandler; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; /** * */ public class Session implements Runnable, RemoteSession { private static final String SEPARATOR = "\n\n\n"; private final Socket socket; private final Thread thread; private UserIdent ident; /** * @param socket */ public Session(Socket socket) { this.socket = socket; this.thread = new Thread(this); this.thread.start(); } /* * Main session loop */ @Override public void run() { try { final SocketStreamSplitter sss = new SocketStreamSplitter(socket.getInputStream(), SEPARATOR); while (true) { try { final String msg = sss.readNext(); if (msg == null) { LoggingHandler.felog.warn("[remote] Connection closed: " + getRemoteHostname()); break; } processMessage(msg); } catch (IOException e) { LoggingHandler.felog.warn("[remote] Socket error: " + e.getMessage()); break; } } } catch (IOException e) { LoggingHandler.felog.warn("[remote] Error opening input stream."); } close(); } /** * All received messages start being processed here * * @param message * @throws IOException */ protected void processMessage(String message) throws IOException { try { JsonRemoteRequest request = getGson().fromJson(message, JsonRemoteRequest.class); LoggingHandler.felog.info(String.format("[remote] Request [%s]: %s", request.id, request.data == null ? "null" : request.data.toString())); if (request.auth != null) { ident = UserIdent.get(request.auth.username); if (!request.auth.password.equals(ModuleRemote.getInstance().getPasskey(ident))) { close("authentication failed", request); return; } } // Check if user was banned if (ident != null && MinecraftServer.getServer().getConfigurationManager().func_152608_h().func_152702_a(ident.getGameProfile())) { close("banned", request); return; } // Check for remote permission if (!APIRegistry.perms.checkUserPermission(ident, ModuleRemote.PERM)) { close(ident == null ? "need authentication" : "access denied", request); return; } // Get the correct remote handler RemoteHandler handler = ModuleRemote.getInstance().getHandler(request.id); if (handler == null) { sendMessage(RemoteResponse.error(request, "unknown message identifier")); return; } // Check permission for remote handler String p = handler.getPermission(); if (p != null && !APIRegistry.perms.checkUserPermission(ident, p)) { sendMessage(RemoteResponse.error(request, RemoteHandler.MSG_NO_PERMISSION)); return; } // Handle request try { RemoteResponse<?> response = handler.handle(this, request); if (response != null) { response.rid = request.rid; sendMessage(response); } } catch (PermissionException e) { sendMessage(RemoteResponse.error(request, RemoteHandler.MSG_NO_PERMISSION)); return; } catch (RemoteException e) { sendMessage(RemoteResponse.error(request, e.getMessage())); return; } catch (Exception e) { sendMessage(RemoteResponse.error(request, RemoteHandler.MSG_EXCEPTION)); LoggingHandler.felog.warn("[remote] Exception while handling message"); e.printStackTrace(); return; } } catch (IllegalArgumentException | JsonSyntaxException e) { LoggingHandler.felog.warn("[remote] Message error: " + e.getMessage()); sendMessage(RemoteResponse.error(null, 0, e.getMessage())); close(); } } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#sendMessage(java.lang.Object) */ @Override public synchronized void sendMessage(RemoteResponse<?> response) throws IOException { OutputStreamWriter ow = new OutputStreamWriter(socket.getOutputStream()); ow.write(getGson().toJson(response) + SEPARATOR); ow.flush(); } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#sendMessage(java.lang.Object) */ @Override public boolean trySendMessage(RemoteResponse<?> response) { if (isClosed()) return false; try { sendMessage(response); return true; } catch (IOException e) { return false; } } /* * (non-Javadoc) * * @see * com.forgeessentials.api.remote.RemoteSession#transformRemoteRequest(com.forgeessentials.api.remote.RemoteRequest, * java.lang.Class) */ @Override public <T> RemoteRequest<T> transformRemoteRequest(JsonRemoteRequest request, Class<T> clazz) { return RemoteRequest.transform(request, getGson().fromJson(request.data, clazz)); } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#getRemoteHostname() */ @Override public String getRemoteHostname() { return ((InetSocketAddress) socket.getRemoteSocketAddress()).getHostName(); } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#getRemoteHostname() */ @Override public String getRemoteAddress() { return ((InetSocketAddress) socket.getRemoteSocketAddress()).getAddress().getHostAddress(); } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#getRemoteHostname() */ @Override public synchronized UserIdent getUserIdent() { return ident; } /** * Terminates the session */ @Override public void close() { try { socket.close(); } catch (IOException e) { /* ignore */ } } /** * Terminates the session * * @throws IOException */ @Override public void close(String reason, int rid) { trySendMessage(RemoteResponse.error("close", rid, reason)); close(); } /** * Terminates the session * * @throws IOException */ public void close(String error, RemoteRequest<?> request) { LoggingHandler.felog.warn(String.format("[remote] Error: %s. Terminating session to %s", error, getRemoteAddress())); trySendMessage(RemoteResponse.error("close", request.rid, error)); close(); } /* * (non-Javadoc) * * @see com.forgeessentials.api.remote.RemoteSession#isClosed() */ @Override public boolean isClosed() { return socket.isClosed(); } /** * Get the Gson instance from ModuleRemote */ public Gson getGson() { return ModuleRemote.getInstance().getGson(); } @Override public RemoteManager getRemoteManager() { return ModuleRemote.getInstance(); } }