Mavenized project

This commit is contained in:
JeromSar
2015-11-18 21:41:51 +01:00
parent 0c3bc40b03
commit 89a317b7df
207 changed files with 247 additions and 1569 deletions

View File

@ -0,0 +1,58 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
public class HTMLGenerationTools
{
private HTMLGenerationTools()
{
throw new AssertionError();
}
public static String paragraph(String data)
{
return "<p>" + escapeHtml4(data) + "</p>\r\n";
}
public static String heading(String data, int level)
{
return "<h" + level + ">" + escapeHtml4(data) + "</h" + level + ">\r\n";
}
public static <K, V> String list(Map<K, V> map)
{
StringBuilder output = new StringBuilder();
output.append("<ul>\r\n");
Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<K, V> entry = it.next();
output.append("<li>").append(escapeHtml4(entry.getKey().toString() + " = " + entry.getValue().toString())).append("</li>\r\n");
}
output.append("</ul>\r\n");
return output.toString();
}
public static <T> String list(Collection<T> list)
{
StringBuilder output = new StringBuilder();
output.append("<ul>\r\n");
for (T entry : list)
{
output.append("<li>").append(escapeHtml4(entry.toString())).append("</li>\r\n");
}
output.append("</ul>\r\n");
return output.toString();
}
}

View File

@ -0,0 +1,70 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.HTTPSession;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Method;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response;
import me.totalfreedom.totalfreedommod.util.FLog;
public abstract class HTTPDModule
{
protected final String uri;
protected final Method method;
protected final Map<String, String> headers;
protected final Map<String, String> params;
protected final Socket socket;
protected final HTTPSession session;
public HTTPDModule(HTTPSession session)
{
this.uri = session.getUri();
this.method = session.getMethod();
this.headers = session.getHeaders();
this.params = session.getParms();
this.socket = session.getSocket();
this.session = session;
}
public String getBody()
{
return null;
}
public String getTitle()
{
return null;
}
public String getStyle()
{
return null;
}
public String getScript()
{
return null;
}
public Response getResponse()
{
return new HTTPDPageBuilder(getBody(), getTitle(), getStyle(), getScript()).getResponse();
}
protected final Map<String, String> getFiles()
{
Map<String, String> files = new HashMap<String, String>();
try
{
session.parseBody(files);
}
catch (Exception ex)
{
FLog.severe(ex);
}
return files;
}
}

View File

@ -0,0 +1,73 @@
package me.totalfreedom.totalfreedommod.httpd;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response;
public class HTTPDPageBuilder
{
private static final String TEMPLATE = "<!DOCTYPE html>\r\n"
+ "<html>\r\n"
+ "<head>\r\n"
+ "<title>{$TITLE}</title>\r\n"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n"
+ "{$STYLE}"
+ "{$SCRIPT}"
+ "</head>\r\n"
+ "<body>\r\n{$BODY}</body>\r\n"
+ "</html>\r\n";
private static final String STYLE = "<style type=\"text/css\">{$STYLE}</style>\r\n";
private static final String SCRIPT = "<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\"></script>\r\n"
+ "<script src=\"//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js\"></script>\r\n"
+ "<script>\r\n{$SCRIPT}\r\n</script>\r\n";
//
private String body = null;
private String title = null;
private String style = null;
private String script = null;
public HTTPDPageBuilder()
{
}
public HTTPDPageBuilder(String body, String title, String style, String script)
{
this.body = body;
this.title = title;
this.style = style;
this.script = script;
}
public void setBody(String body)
{
this.body = body;
}
public void setTitle(String title)
{
this.title = title;
}
public void setStyle(String style)
{
this.style = style;
}
public void setScript(String script)
{
this.script = script;
}
public Response getResponse()
{
return new Response(this.toString());
}
@Override
public String toString()
{
return TEMPLATE
.replace("{$BODY}", this.body == null ? "" : this.body)
.replace("{$TITLE}", this.title == null ? "" : this.title)
.replace("{$STYLE}", this.style == null ? "" : STYLE.replace("{$STYLE}", this.style))
.replace("{$SCRIPT}", this.script == null ? "" : SCRIPT.replace("{$SCRIPT}", this.script));
}
}

View File

@ -0,0 +1,285 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import me.totalfreedom.totalfreedommod.config.ConfigEntry;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.HTTPSession;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response;
import me.totalfreedom.totalfreedommod.util.FLog;
import net.pravian.aero.component.service.AbstractService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.bukkit.Bukkit;
public class HTTPDaemon extends AbstractService<TotalFreedomMod>
{
public static String MIME_DEFAULT_BINARY = "application/octet-stream";
//
private static final Pattern EXT_REGEX = Pattern.compile("\\.([^\\.\\s]+)$");
//
public static final int PORT = ConfigEntry.HTTPD_PORT.getInteger();
//
private static final TFM_HTTPD HTTPD = new TFM_HTTPD(PORT);
public HTTPDaemon(TotalFreedomMod plugin)
{
super(plugin);
}
@Override
public void onStart()
{
if (!ConfigEntry.HTTPD_ENABLED.getBoolean())
{
return;
}
try
{
HTTPD.start();
if (HTTPD.isAlive())
{
FLog.info("TFM HTTPd started. Listening on port: " + HTTPD.getListeningPort());
}
else
{
FLog.info("Error starting TFM HTTPd.");
}
}
catch (IOException ex)
{
FLog.severe(ex);
}
}
@Override
public void onStop()
{
if (!ConfigEntry.HTTPD_ENABLED.getBoolean())
{
return;
}
HTTPD.stop();
FLog.info("TFM HTTPd stopped.");
}
private static enum ModuleType
{
DUMP(new ModuleExecutable(false, "dump")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Response(Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "The DUMP module is disabled. It is intended for debugging use only.");
}
}),
HELP(new ModuleExecutable(true, "help")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_help(session).getResponse();
}
}),
LIST(new ModuleExecutable(true, "list")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_list(session).getResponse();
}
}),
FILE(new ModuleExecutable(false, "file")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_file(session).getResponse();
}
}),
SCHEMATIC(new ModuleExecutable(false, "schematic")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_schematic(session).getResponse();
}
}),
PERMBANS(new ModuleExecutable(false, "permbans")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_permbans(session).getResponse();
}
}),
PLAYERS(new ModuleExecutable(true, "players")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_players(session).getResponse();
}
}),
LOGS(new ModuleExecutable(false, "logs")
{
@Override
public Response getResponse(HTTPSession session)
{
return new Module_logs(session).getResponse();
}
});
//
private final ModuleExecutable moduleExecutable;
private ModuleType(ModuleExecutable moduleExecutable)
{
this.moduleExecutable = moduleExecutable;
}
private abstract static class ModuleExecutable
{
private final boolean runOnBukkitThread;
private final String name;
private ModuleExecutable(boolean runOnBukkitThread, String name)
{
this.runOnBukkitThread = runOnBukkitThread;
this.name = name;
}
public Response execute(final HTTPSession session)
{
try
{
if (this.runOnBukkitThread)
{
return Bukkit.getScheduler().callSyncMethod(TotalFreedomMod.plugin, new Callable<Response>()
{
@Override
public Response call() throws Exception
{
return getResponse(session);
}
}).get();
}
else
{
return getResponse(session);
}
}
catch (Exception ex)
{
FLog.severe(ex);
}
return null;
}
public abstract Response getResponse(HTTPSession session);
public String getName()
{
return name;
}
}
public ModuleExecutable getModuleExecutable()
{
return moduleExecutable;
}
private static ModuleType getByName(String needle)
{
for (ModuleType type : values())
{
if (type.getModuleExecutable().getName().equalsIgnoreCase(needle))
{
return type;
}
}
return FILE;
}
}
private static class TFM_HTTPD extends NanoHTTPD
{
public TFM_HTTPD(int port)
{
super(port);
}
public TFM_HTTPD(String hostname, int port)
{
super(hostname, port);
}
@Override
public Response serve(HTTPSession session)
{
Response response;
try
{
final String[] args = StringUtils.split(session.getUri(), "/");
final ModuleType moduleType = args.length >= 1 ? ModuleType.getByName(args[0]) : ModuleType.FILE;
response = moduleType.getModuleExecutable().execute(session);
}
catch (Exception ex)
{
response = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Error 500: Internal Server Error\r\n" + ex.getMessage() + "\r\n" + ExceptionUtils.getStackTrace(ex));
}
if (response == null)
{
response = new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Error 404: Not Found - The requested resource was not found on this server.");
}
return response;
}
}
public static Response serveFileBasic(File file)
{
Response response = null;
if (file != null && file.exists())
{
try
{
String mimetype = null;
Matcher matcher = EXT_REGEX.matcher(file.getCanonicalPath());
if (matcher.find())
{
mimetype = Module_file.MIME_TYPES.get(matcher.group(1));
}
if (mimetype == null || mimetype.trim().isEmpty())
{
mimetype = MIME_DEFAULT_BINARY;
}
response = new Response(Response.Status.OK, mimetype, new FileInputStream(file));
response.addHeader("Content-Length", "" + file.length());
}
catch (IOException ex)
{
FLog.severe(ex);
}
}
return response;
}
}

View File

@ -0,0 +1,111 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.list;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.paragraph;
import me.totalfreedom.totalfreedommod.util.FLog;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
public class Module_dump extends HTTPDModule
{
private File echoFile = null;
private final String body;
public Module_dump(NanoHTTPD.HTTPSession session)
{
super(session);
//Body needs to be computed before getResponse, so we know if a text response or a file echo is needed.
this.body = body();
}
@Override
public NanoHTTPD.Response getResponse()
{
String echo = params.get("echo");
boolean doEcho = echo != null && ((echo = echo.toLowerCase().trim()).equalsIgnoreCase("true") || echo.equalsIgnoreCase("1"));
if (doEcho && this.echoFile != null && this.echoFile.exists())
{
return HTTPDaemon.serveFileBasic(this.echoFile);
}
else
{
return super.getResponse();
}
}
@Override
public String getBody()
{
return body;
}
private String body()
{
StringBuilder responseBody = new StringBuilder();
String remoteAddress = socket.getInetAddress().getHostAddress();
String[] args = StringUtils.split(uri, "/");
Map<String, String> files = getFiles();
responseBody
.append(paragraph("URI: " + uri))
.append(paragraph("args (Length: " + args.length + "): " + StringUtils.join(args, ",")))
.append(paragraph("Method: " + method.toString()))
.append(paragraph("Remote Address: " + remoteAddress))
.append(paragraph("Headers:"))
.append(list(headers))
.append(paragraph("Params:"))
.append(list(params))
.append(paragraph("Files:"))
.append(list(files));
Iterator<Map.Entry<String, String>> it = files.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<String, String> entry = it.next();
String formName = entry.getKey();
String tempFileName = entry.getValue();
String origFileName = params.get(formName);
File tempFile = new File(tempFileName);
if (tempFile.exists())
{
this.echoFile = tempFile;
if (origFileName.contains("../"))
{
continue;
}
String targetFileName = "./public_html/uploads/" + origFileName;
File targetFile = new File(targetFileName);
try
{
FileUtils.copyFile(tempFile, targetFile);
}
catch (IOException ex)
{
FLog.severe(ex);
}
}
}
return responseBody.toString();
}
@Override
public String getTitle()
{
return "TotalFreedomMod :: Request Debug Dumper";
}
}

View File

@ -0,0 +1,378 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import me.totalfreedom.totalfreedommod.config.ConfigEntry;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response;
import org.apache.commons.lang3.StringUtils;
/*
* This class was adapted from https://github.com/NanoHttpd/nanohttpd/blob/master/webserver/src/main/java/fi/iki/elonen/SimpleWebServer.java
*/
public class Module_file extends HTTPDModule
{
private final File rootDir = new File(ConfigEntry.HTTPD_PUBLIC_FOLDER.getString());
public static final Map<String, String> MIME_TYPES = new HashMap<String, String>();
static
{
MIME_TYPES.put("css", "text/css");
MIME_TYPES.put("htm", "text/html");
MIME_TYPES.put("html", "text/html");
MIME_TYPES.put("xml", "text/xml");
MIME_TYPES.put("java", "text/x-java-source, text/java");
MIME_TYPES.put("txt", "text/plain");
MIME_TYPES.put("asc", "text/plain");
MIME_TYPES.put("yml", "text/yaml");
MIME_TYPES.put("gif", "image/gif");
MIME_TYPES.put("jpg", "image/jpeg");
MIME_TYPES.put("jpeg", "image/jpeg");
MIME_TYPES.put("png", "image/png");
MIME_TYPES.put("mp3", "audio/mpeg");
MIME_TYPES.put("m3u", "audio/mpeg-url");
MIME_TYPES.put("mp4", "video/mp4");
MIME_TYPES.put("ogv", "video/ogg");
MIME_TYPES.put("flv", "video/x-flv");
MIME_TYPES.put("mov", "video/quicktime");
MIME_TYPES.put("swf", "application/x-shockwave-flash");
MIME_TYPES.put("js", "application/javascript");
MIME_TYPES.put("pdf", "application/pdf");
MIME_TYPES.put("doc", "application/msword");
MIME_TYPES.put("ogg", "application/x-ogg");
MIME_TYPES.put("zip", "application/octet-stream");
MIME_TYPES.put("exe", "application/octet-stream");
MIME_TYPES.put("class", "application/octet-stream");
}
public Module_file(NanoHTTPD.HTTPSession session)
{
super(session);
}
private File getRootDir()
{
return rootDir;
}
private String encodeUri(String uri)
{
String newUri = "";
StringTokenizer st = new StringTokenizer(uri, "/ ", true);
while (st.hasMoreTokens())
{
String tok = st.nextToken();
if (tok.equals("/"))
{
newUri += "/";
}
else if (tok.equals(" "))
{
newUri += "%20";
}
else
{
try
{
newUri += URLEncoder.encode(tok, "UTF-8");
}
catch (UnsupportedEncodingException ignored)
{
}
}
}
return newUri;
}
public Response serveFile(String uri, Map<String, String> params, File homeDir)
{
Response res = null;
// Make sure we won't die of an exception later
if (!homeDir.isDirectory())
{
res = new Response(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory.");
}
if (res == null)
{
// Remove URL arguments
uri = uri.trim().replace(File.separatorChar, '/');
if (uri.indexOf('?') >= 0)
{
uri = uri.substring(0, uri.indexOf('?'));
}
// Prohibit getting out of current directory
if (uri.startsWith("src/main") || uri.endsWith("src/main") || uri.contains("../"))
{
res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons.");
}
}
File f = new File(homeDir, uri);
if (res == null && !f.exists())
{
res = new Response(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found.");
}
// List the directory, if necessary
if (res == null && f.isDirectory())
{
// Browsers get confused without '/' after the
// directory, send a redirect.
if (!uri.endsWith("/"))
{
uri += "/";
res = new Response(Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri
+ "</a></body></html>");
res.addHeader("Location", uri);
}
if (res == null)
{
// First try index.html and index.htm
if (new File(f, "index.html").exists())
{
f = new File(homeDir, uri + "/index.html");
}
else if (new File(f, "index.htm").exists())
{
f = new File(homeDir, uri + "/index.htm");
}
else if (f.canRead())
{
// No index file, list the directory if it is readable
res = new Response(listDirectory(uri, f));
}
else
{
res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: No directory listing.");
}
}
}
try
{
if (res == null)
{
// Get MIME type from file name extension, if possible
String mime = null;
int dot = f.getCanonicalPath().lastIndexOf('.');
if (dot >= 0)
{
mime = MIME_TYPES.get(f.getCanonicalPath().substring(dot + 1).toLowerCase());
}
if (mime == null)
{
mime = HTTPDaemon.MIME_DEFAULT_BINARY;
}
// Calculate etag
String etag = Integer.toHexString((f.getAbsolutePath() + f.lastModified() + "" + f.length()).hashCode());
final long fileLen = f.length();
long startFrom = 0;
long endAt = -1;
final String range = params.get("range");
if (range != null)
{
final String[] rangeParams = StringUtils.split(range, "=");
if (rangeParams.length >= 2)
{
if ("bytes".equalsIgnoreCase(rangeParams[0]))
{
try
{
int minus = rangeParams[1].indexOf('-');
if (minus > 0)
{
startFrom = Long.parseLong(rangeParams[1].substring(0, minus));
endAt = Long.parseLong(rangeParams[1].substring(minus + 1));
}
}
catch (NumberFormatException ignored)
{
}
}
else if ("tail".equalsIgnoreCase(rangeParams[0]))
{
try
{
final long tailLen = Long.parseLong(rangeParams[1]);
if (tailLen < fileLen)
{
startFrom = fileLen - tailLen - 2;
if (startFrom < 0)
{
startFrom = 0;
}
}
}
catch (NumberFormatException ignored)
{
}
}
}
}
// Change return code and add Content-Range header when skipping is requested
if (range != null && startFrom >= 0)
{
if (startFrom >= fileLen)
{
res = new Response(Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
res.addHeader("Content-Range", "bytes 0-0/" + fileLen);
res.addHeader("ETag", etag);
}
else
{
if (endAt < 0)
{
endAt = fileLen - 1;
}
long newLen = endAt - startFrom + 1;
if (newLen < 0)
{
newLen = 0;
}
final long dataLen = newLen;
FileInputStream fis = new FileInputStream(f)
{
@Override
public int available() throws IOException
{
return (int) dataLen;
}
};
fis.skip(startFrom);
res = new Response(Response.Status.PARTIAL_CONTENT, mime, fis);
res.addHeader("Content-Length", "" + dataLen);
res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
res.addHeader("ETag", etag);
}
}
else
{
res = new Response(Response.Status.OK, mime, new FileInputStream(f));
res.addHeader("Content-Length", "" + fileLen);
res.addHeader("ETag", etag);
}
}
}
catch (IOException ioe)
{
res = new Response(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: Reading file failed.");
}
res.addHeader("Accept-Ranges", "bytes"); // Announce that the file server accepts partial content requestes
return res;
}
private String listDirectory(String uri, File f)
{
String heading = "Directory " + uri;
String msg = "<html><head><title>" + heading + "</title><style><!--\n"
+ "span.dirname { font-weight: bold; }\n"
+ "span.filesize { font-size: 75%; }\n"
+ "// -->\n"
+ "</style>"
+ "</head><body><h1>" + heading + "</h1>";
String up = null;
if (uri.length() > 1)
{
String u = uri.substring(0, uri.length() - 1);
int slash = u.lastIndexOf('/');
if (slash >= 0 && slash < u.length())
{
up = uri.substring(0, slash + 1);
}
}
List<String> _files = Arrays.asList(f.list(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return new File(dir, name).isFile();
}
}));
Collections.sort(_files);
List<String> directories = Arrays.asList(f.list(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return new File(dir, name).isDirectory();
}
}));
Collections.sort(directories);
if (up != null || directories.size() + _files.size() > 0)
{
msg += "<ul>";
if (up != null || directories.size() > 0)
{
msg += "<section class=\"directories\">";
if (up != null)
{
msg += "<li><a rel=\"directory\" href=\"" + up + "\"><span class=\"dirname\">..</span></a></b></li>";
}
for (int i = 0; i < directories.size(); i++)
{
String dir = directories.get(i) + "/";
msg += "<li><a rel=\"directory\" href=\"" + encodeUri(uri + dir) + "\"><span class=\"dirname\">" + dir + "</span></a></b></li>";
}
msg += "</section>";
}
if (_files.size() > 0)
{
msg += "<section class=\"files\">";
for (int i = 0; i < _files.size(); i++)
{
String file = _files.get(i);
msg += "<li><a href=\"" + encodeUri(uri + file) + "\"><span class=\"filename\">" + file + "</span></a>";
File curFile = new File(f, file);
long len = curFile.length();
msg += "&nbsp;<span class=\"filesize\">(";
if (len < 1024)
{
msg += len + " bytes";
}
else if (len < 1024 * 1024)
{
msg += len / 1024 + "." + (len % 1024 / 10 % 100) + " KB";
}
else
{
msg += len / (1024 * 1024) + "." + len % (1024 * 1024) / 10 % 100 + " MB";
}
msg += ")</span></li>";
}
msg += "</section>";
}
msg += "</ul>";
}
msg += "</body></html>";
return msg;
}
@Override
public Response getResponse()
{
return serveFile(uri, params, getRootDir());
}
}

View File

@ -0,0 +1,157 @@
package me.totalfreedom.totalfreedommod.httpd;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import me.totalfreedom.totalfreedommod.commands.FreedomCommand;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.heading;
import static me.totalfreedom.totalfreedommod.httpd.HTMLGenerationTools.paragraph;
import me.totalfreedom.totalfreedommod.rank.PlayerRank;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import net.pravian.aero.command.CommandReflection;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.SimpleCommandMap;
public class Module_help extends HTTPDModule
{
public Module_help(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
public String getBody()
{
final CommandMap map = CommandReflection.getCommandMap();
if (map == null || !(map instanceof SimpleCommandMap))
{
return paragraph("Error loading commands.");
}
final StringBuilder responseBody = new StringBuilder()
.append(heading("Command Help", 1))
.append(paragraph(
"This page is an automatically generated listing of all plugin commands that are currently live on the server. "
+ "Please note that it does not include vanilla server commands."));
final Collection<Command> knownCommands = ((SimpleCommandMap) map).getCommands();
final Map<String, List<Command>> commandsByPlugin = new HashMap<String, List<Command>>();
for (Command command : knownCommands)
{
String pluginName = "Bukkit";
if (command instanceof PluginIdentifiableCommand)
{
pluginName = ((PluginIdentifiableCommand) command).getPlugin().getName();
}
List<Command> pluginCommands = commandsByPlugin.get(pluginName);
if (pluginCommands == null)
{
pluginCommands = Lists.newArrayList();
commandsByPlugin.put(pluginName, pluginCommands);
}
pluginCommands.add(command);
}
final Iterator<Map.Entry<String, List<Command>>> it = commandsByPlugin.entrySet().iterator();
while (it.hasNext())
{
final Map.Entry<String, List<Command>> entry = it.next();
final String pluginName = entry.getKey();
final List<Command> commands = entry.getValue();
Collections.sort(commands, new CommandComparator());
responseBody.append(heading(pluginName, 2)).append("<ul>\r\n");
PlayerRank lastTfmCommandLevel = null;
for (Command command : commands)
{
if (!TotalFreedomMod.pluginName.equals(pluginName))
{
responseBody.append(buildDescription(command));
continue;
}
PlayerRank tfmCommandLevel = FreedomCommand.getCommand(command).getPerms().level();
if (lastTfmCommandLevel == null || lastTfmCommandLevel != tfmCommandLevel)
{
responseBody.append("</ul>\r\n").append(heading(tfmCommandLevel.getName(), 3)).append("<ul>\r\n");
}
lastTfmCommandLevel = tfmCommandLevel;
responseBody.append(buildDescription(command));
}
responseBody.append("</ul>\r\n");
}
return responseBody.toString();
}
private static String buildDescription(Command command)
{
StringBuilder sb = new StringBuilder();
sb.append(
"<li><span class=\"commandName\">{$CMD_NAME}</span> - Usage: <span class=\"commandUsage\">{$CMD_USAGE}</span>"
.replace("{$CMD_NAME}", escapeHtml4(command.getName().trim()))
.replace("{$CMD_USAGE}", escapeHtml4(command.getUsage().trim())));
if (!command.getAliases().isEmpty())
{
sb.append(
" - Aliases: <span class=\"commandAliases\">{$CMD_ALIASES}</span>"
.replace("{$CMD_ALIASES}", escapeHtml4(StringUtils.join(command.getAliases(), ", "))));
}
sb.append(
"<br><span class=\"commandDescription\">{$CMD_DESC}</span></li>\r\n"
.replace("{$CMD_DESC}", escapeHtml4(command.getDescription().trim())));
return sb.toString();
}
@Override
public String getTitle()
{
return "TotalFreedomMod :: Command Help";
}
@Override
public String getStyle()
{
return ".commandName{font-weight:bold;}.commandDescription{padding-left:15px;}li{margin:.15em;padding:.15em;}";
}
public static class CommandComparator implements Comparator<Command>
{
@Override
public int compare(Command a, Command b)
{
FreedomCommand ca = FreedomCommand.getCommand(a);
FreedomCommand cb = FreedomCommand.getCommand(b);
if (ca == null
|| cb == null
|| ca.getPerms() == null
|| cb.getPerms() == null)
{
return a.getName().compareTo(b.getName());
}
return ca.getPerms().level().getName().compareTo(cb.getPerms().level().getName());
}
}
}

View File

@ -0,0 +1,42 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.util.Collection;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class Module_list extends HTTPDModule
{
public Module_list(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
public String getBody()
{
final StringBuilder body = new StringBuilder();
final Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
body.append("<p>There are ").append(onlinePlayers.size()).append("/").append(Bukkit.getMaxPlayers()).append(" players online:</p>\r\n");
body.append("<ul>\r\n");
for (Player player : onlinePlayers)
{
String tag = TotalFreedomMod.plugin.rm.getDisplayRank(player).getTag();
body.append("<li>").append(tag).append(player.getName()).append("</li>\r\n");
}
body.append("</ul>\r\n");
return body.toString();
}
@Override
public String getTitle()
{
return "Total Freedom - Online Users";
}
}

View File

@ -0,0 +1,26 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import me.totalfreedom.totalfreedommod.config.ConfigEntry;
public class Module_logs extends Module_file
{
public Module_logs(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
public NanoHTTPD.Response getResponse()
{
if (ConfigEntry.LOGS_SECRET.getString().equals(params.get("password")))
{
return serveFile("latest.log", params, new File("./logs"));
}
else
{
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "Incorrect password.");
}
}
}

View File

@ -0,0 +1,27 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
public class Module_permbans extends HTTPDModule
{
public Module_permbans(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
public NanoHTTPD.Response getResponse()
{
File permbanFile = new File(TotalFreedomMod.plugin.getDataFolder(), TotalFreedomMod.PERMBAN_FILENAME);
if (permbanFile.exists())
{
return HTTPDaemon.serveFileBasic(new File(TotalFreedomMod.plugin.getDataFolder(), TotalFreedomMod.PERMBAN_FILENAME));
}
else
{
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT,
"Error 404: Not Found - The requested resource was not found on this server.");
}
}
}

View File

@ -0,0 +1,68 @@
package me.totalfreedom.totalfreedommod.httpd;
import me.totalfreedom.totalfreedommod.admin.Admin;
import me.totalfreedom.totalfreedommod.util.FUtil;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class Module_players extends HTTPDModule
{
public Module_players(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
@SuppressWarnings("unchecked")
public NanoHTTPD.Response getResponse()
{
final JSONObject responseObject = new JSONObject();
final JSONArray players = new JSONArray();
final JSONArray superadmins = new JSONArray();
final JSONArray telnetadmins = new JSONArray();
final JSONArray senioradmins = new JSONArray();
final JSONArray developers = new JSONArray();
// All online players
for (Player player : Bukkit.getOnlinePlayers())
{
players.add(player.getName());
}
// Admins
for (Admin admin : TotalFreedomMod.plugin.al.getAllAdmins().values())
{
final String username = admin.getName();
switch (admin.getRank())
{
case SUPER_ADMIN:
superadmins.add(username);
break;
case TELNET_ADMIN:
telnetadmins.add(username);
break;
case SENIOR_ADMIN:
senioradmins.add(senioradmins);
break;
}
}
// Developers
developers.addAll(FUtil.DEVELOPERS);
responseObject.put("players", players);
responseObject.put("superadmins", superadmins);
responseObject.put("telnetadmins", telnetadmins);
responseObject.put("senioradmins", senioradmins);
responseObject.put("developers", developers);
final NanoHTTPD.Response response = new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_JSON, responseObject.toString());
response.addHeader("Access-Control-Allow-Origin", "*");
return response;
}
}

View File

@ -0,0 +1,303 @@
package me.totalfreedom.totalfreedommod.httpd;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import me.totalfreedom.totalfreedommod.admin.Admin;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Method;
import me.totalfreedom.totalfreedommod.httpd.NanoHTTPD.Response;
import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.TotalFreedomMod;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
public class Module_schematic extends HTTPDModule
{
private static final File SCHEMATIC_FOLDER = new File("./plugins/WorldEdit/schematics/");
private static final String REQUEST_FORM_FILE_ELEMENT_NAME = "schematicFile";
private static final Pattern SCHEMATIC_FILENAME_LC = Pattern.compile("^[a-z0-9_'!,\\-]{1,30}\\.schematic$");
private static final String[] SCHEMATIC_FILTER = new String[]
{
"schematic"
};
private static final String UPLOAD_FORM = "<form method=\"post\" name=\"schematicForm\" id=\"schematicForm\" action=\"/schematic/upload/\" enctype=\"multipart/form-data\">\n"
+ "<p>Select a schematic file to upload. Filenames must be alphanumeric, between 1 and 30 characters long (inclusive), and have a .schematic extension.</p>\n"
+ "<input type=\"file\" id=\"schematicFile\" name=\"schematicFile\" />\n"
+ "<br />\n"
+ "<button type=\"submit\">Submit</button>\n"
+ "</form>";
public Module_schematic(NanoHTTPD.HTTPSession session)
{
super(session);
}
@Override
public Response getResponse()
{
try
{
return new HTTPDPageBuilder(body(), title(), null, null).getResponse();
}
catch (ResponseOverrideException ex)
{
return ex.getResponse();
}
}
public String title()
{
return "TotalFreedomMod :: Schematic Manager";
}
public String body() throws ResponseOverrideException
{
if (!SCHEMATIC_FOLDER.exists())
{
return HTMLGenerationTools.paragraph("Can't find the WorldEdit schematic folder.");
}
final StringBuilder out = new StringBuilder();
final String[] args = StringUtils.split(uri, "/");
final ModuleMode mode = ModuleMode.getMode(getArg(args, 1));
switch (mode)
{
case LIST:
{
Collection<File> schematics = FileUtils.listFiles(SCHEMATIC_FOLDER, SCHEMATIC_FILTER, false);
final List<String> schematicsFormatted = new ArrayList<String>();
for (File schematic : schematics)
{
String filename = StringEscapeUtils.escapeHtml4(schematic.getName());
if (SCHEMATIC_FILENAME_LC.matcher(filename.trim().toLowerCase()).find())
{
schematicsFormatted.add("<li><a href=\"/schematic/download?schematicName=" + filename + "\">" + filename + "</a></li>");
}
else
{
schematicsFormatted.add("<li>" + filename + " - (Illegal filename, can't download)</li>");
}
}
Collections.sort(schematicsFormatted, new Comparator<String>()
{
@Override
public int compare(String a, String b)
{
return a.toLowerCase().compareTo(b.toLowerCase());
}
});
out
.append(HTMLGenerationTools.heading("Schematics:", 1))
.append("<ul>")
.append(StringUtils.join(schematicsFormatted, "\r\n"))
.append("</ul>");
break;
}
case DOWNLOAD:
{
try
{
throw new ResponseOverrideException(downloadSchematic(params.get("schematicName")));
}
catch (SchematicTransferException ex)
{
out.append(HTMLGenerationTools.paragraph("Error downloading schematic: " + ex.getMessage()));
}
break;
}
case UPLOAD:
{
final String remoteAddress = socket.getInetAddress().getHostAddress();
if (!isAuthorized(remoteAddress))
{
out.append(HTMLGenerationTools.paragraph("Schematic upload access denied: Your IP, " + remoteAddress + ", is not registered to a superadmin on this server."));
}
else
{
if (method == Method.POST)
{
try
{
uploadSchematic();
out.append(HTMLGenerationTools.paragraph("Schematic uploaded successfully."));
}
catch (SchematicTransferException ex)
{
out.append(HTMLGenerationTools.paragraph("Error uploading schematic: " + ex.getMessage()));
}
}
else
{
out.append(UPLOAD_FORM);
}
}
break;
}
default:
{
out.append(HTMLGenerationTools.paragraph("Invalid request mode."));
break;
}
}
return out.toString();
}
private boolean uploadSchematic() throws SchematicTransferException
{
Map<String, String> files = getFiles();
final String tempFileName = files.get(REQUEST_FORM_FILE_ELEMENT_NAME);
if (tempFileName == null)
{
throw new SchematicTransferException("No file transmitted to server.");
}
final File tempFile = new File(tempFileName);
if (!tempFile.exists())
{
throw new SchematicTransferException();
}
String origFileName = params.get(REQUEST_FORM_FILE_ELEMENT_NAME);
if (origFileName == null || (origFileName = origFileName.trim()).isEmpty())
{
throw new SchematicTransferException("Can't resolve original file name.");
}
if (tempFile.length() > FileUtils.ONE_KB * 64L)
{
throw new SchematicTransferException("Schematic is too big (64kb max).");
}
if (!SCHEMATIC_FILENAME_LC.matcher(origFileName.toLowerCase()).find())
{
throw new SchematicTransferException("File name must be alphanumeric, between 1 and 30 characters long (inclusive), and have a \".schematic\" extension.");
}
final File targetFile = new File(SCHEMATIC_FOLDER.getPath(), origFileName);
if (targetFile.exists())
{
throw new SchematicTransferException("Schematic already exists on the server.");
}
try
{
FileUtils.copyFile(tempFile, targetFile);
}
catch (IOException ex)
{
FLog.severe(ex);
throw new SchematicTransferException();
}
return true;
}
private Response downloadSchematic(String schematicName) throws SchematicTransferException
{
if (schematicName == null || !SCHEMATIC_FILENAME_LC.matcher((schematicName = schematicName.trim()).toLowerCase()).find())
{
throw new SchematicTransferException("Invalid schematic name requested: " + schematicName);
}
final File targetFile = new File(SCHEMATIC_FOLDER.getPath(), schematicName);
if (!targetFile.exists())
{
throw new SchematicTransferException("Schematic not found: " + schematicName);
}
Response response = HTTPDaemon.serveFileBasic(targetFile);
response.addHeader("Content-Disposition", "attachment; filename=" + targetFile.getName() + ";");
return response;
}
private boolean isAuthorized(String remoteAddress)
{
Admin entry = TotalFreedomMod.plugin.al.getEntryByIp(remoteAddress);
return entry != null && entry.isActivated();
}
private static class SchematicTransferException extends Exception
{
public SchematicTransferException()
{
}
public SchematicTransferException(String string)
{
super(string);
}
}
private static class ResponseOverrideException extends Exception
{
private final Response response;
public ResponseOverrideException(Response response)
{
this.response = response;
}
public Response getResponse()
{
return response;
}
}
private static String getArg(String[] args, int index)
{
String out = (args.length == index + 1 ? args[index] : null);
return (out == null ? null : (out.trim().isEmpty() ? null : out.trim()));
}
private static enum ModuleMode
{
LIST("list"),
UPLOAD("upload"),
DOWNLOAD("download"),
INVALID(null);
//
private final String modeName;
private ModuleMode(String modeName)
{
this.modeName = modeName;
}
@Override
public String toString()
{
return this.modeName;
}
public static ModuleMode getMode(String needle)
{
for (ModuleMode mode : values())
{
final String haystack = mode.toString();
if (haystack != null && haystack.equalsIgnoreCase(needle))
{
return mode;
}
}
return INVALID;
}
}
}

File diff suppressed because it is too large Load Diff