From 41cca7cd6ad9b83b358fb60646bfd005e9408635 Mon Sep 17 00:00:00 2001 From: StevenLawson Date: Tue, 3 Sep 2013 15:20:28 -0400 Subject: [PATCH] Working on schematic uploader. Sorry for not branching out on this, but its almost done. --- .../HTTPD/HTMLGenerationTools.java | 4 +- .../TotalFreedomMod/HTTPD/Module_dump.java | 68 +++++++++ .../TotalFreedomMod/HTTPD/Module_file.java | 2 +- .../HTTPD/Module_schematic.java | 134 +++++++++++++++++- .../HTTPD/TFM_HTTPD_Manager.java | 44 +++++- 5 files changed, 246 insertions(+), 6 deletions(-) diff --git a/src/me/StevenLawson/TotalFreedomMod/HTTPD/HTMLGenerationTools.java b/src/me/StevenLawson/TotalFreedomMod/HTTPD/HTMLGenerationTools.java index d970b949..35c869f7 100644 --- a/src/me/StevenLawson/TotalFreedomMod/HTTPD/HTMLGenerationTools.java +++ b/src/me/StevenLawson/TotalFreedomMod/HTTPD/HTMLGenerationTools.java @@ -1,7 +1,7 @@ package me.StevenLawson.TotalFreedomMod.HTTPD; +import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Map; import static org.apache.commons.lang3.StringEscapeUtils.*; @@ -41,7 +41,7 @@ public class HTMLGenerationTools return output.toString(); } - public static String list(List list) + public static String list(Collection list) { StringBuilder output = new StringBuilder(); diff --git a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_dump.java b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_dump.java index a6a955c1..57ca4343 100644 --- a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_dump.java +++ b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_dump.java @@ -1,29 +1,64 @@ package me.StevenLawson.TotalFreedomMod.HTTPD; +import java.io.File; +import java.io.IOException; import java.net.Socket; +import java.util.Iterator; import java.util.Map; import org.apache.commons.lang.StringUtils; +import org.apache.commons.io.FileUtils; +import me.StevenLawson.TotalFreedomMod.TFM_Log; import static me.StevenLawson.TotalFreedomMod.HTTPD.HTMLGenerationTools.*; public class Module_dump extends TFM_HTTPD_Module { + private File echoFile = null; + private final String body; + public Module_dump(String uri, NanoHTTPD.Method method, Map headers, Map params, Map files, Socket socket) { super(uri, method, headers, params, files, socket); + + //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 TFM_HTTPD_Manager.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, "/"); 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:")) @@ -31,6 +66,39 @@ public class Module_dump extends TFM_HTTPD_Module .append(paragraph("Files:")) .append(list(files)); + Iterator> it = files.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry 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) + { + TFM_Log.severe(ex); + } + } + } + return responseBody.toString(); } diff --git a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_file.java b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_file.java index 1454848b..6bfd9ae5 100644 --- a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_file.java +++ b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_file.java @@ -23,7 +23,7 @@ import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry; public class Module_file extends TFM_HTTPD_Module { private final File rootDir = new File(TFM_ConfigEntry.HTTPD_PUBLIC_FOLDER.getString()); - private static final Map MIME_TYPES = new HashMap(); + public static final Map MIME_TYPES = new HashMap(); static { diff --git a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_schematic.java b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_schematic.java index 92bcf3db..0e3e9354 100644 --- a/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_schematic.java +++ b/src/me/StevenLawson/TotalFreedomMod/HTTPD/Module_schematic.java @@ -1,11 +1,31 @@ package me.StevenLawson.TotalFreedomMod.HTTPD; +import java.io.File; +import java.io.IOException; import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; +import me.StevenLawson.TotalFreedomMod.TFM_Log; +import me.StevenLawson.TotalFreedomMod.TFM_Superadmin; +import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringEscapeUtils; public class Module_schematic extends TFM_HTTPD_Module { + 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 = Pattern.compile("^[a-zA-Z0-9]+\\.schematic$"); + private static final String[] SCHEMATIC_FILTER = new String[] + { + "schematic" + }; + public Module_schematic(String uri, NanoHTTPD.Method method, Map headers, Map params, Map files, Socket socket) { super(uri, method, headers, params, files, socket); @@ -23,16 +43,55 @@ public class Module_schematic extends TFM_HTTPD_Module final StringBuilder out = new StringBuilder(); final String[] args = StringUtils.split(uri, "/"); - ModuleMode mode = ModuleMode.getMode(getArg(args, 1)); + final ModuleMode mode = ModuleMode.getMode(getArg(args, 1)); switch (mode) { + case LIST: + { + Collection schematics = FileUtils.listFiles(SCHEMATIC_FOLDER, SCHEMATIC_FILTER, false); + + final List schematicsFormatted = new ArrayList(); + for (File schematic : schematics) + { + String filename = StringEscapeUtils.escapeHtml4(schematic.getName()); + schematicsFormatted.add("
  • " + filename + "
  • "); + } + + Collections.sort(schematicsFormatted); + + out + .append(HTMLGenerationTools.heading("Schematics:", 1)) + .append("
      ") + .append(StringUtils.join(schematicsFormatted, "\r\n")) + .append("
    "); + + break; + } case DOWNLOAD: { + out.append(HTMLGenerationTools.paragraph("Not yet implemented - Download: " + params.get("schematicName"))); 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 + { + try + { + uploadSchematic(); + out.append(HTMLGenerationTools.paragraph("Schematic uploaded successfully.")); + } + catch (SchematicUploadException ex) + { + out.append(HTMLGenerationTools.paragraph("Error uploading schematic.")); + } + } break; } default: @@ -45,8 +104,81 @@ public class Module_schematic extends TFM_HTTPD_Module return out.toString(); } + private boolean uploadSchematic() throws SchematicUploadException + { + final String tempFileName = files.get(REQUEST_FORM_FILE_ELEMENT_NAME); + if (tempFileName != null) + { + final String origFileName = params.get(REQUEST_FORM_FILE_ELEMENT_NAME).trim(); + if (origFileName == null || origFileName.trim().isEmpty()) + { + throw new SchematicUploadException("Can't resolve original file name."); + } + else + { + final File tempFile = new File(tempFileName); + if (tempFile.exists()) + { + if (SCHEMATIC_FILENAME.matcher(origFileName).find()) + { + final File targetFile = new File(SCHEMATIC_FOLDER.getPath(), origFileName); + if (targetFile.exists()) + { + throw new SchematicUploadException("Schematic exists on the server already."); + } + else + { + try + { + FileUtils.copyFile(tempFile, targetFile); + } + catch (IOException ex) + { + TFM_Log.severe(ex); + throw new SchematicUploadException(); + } + } + } + else + { + throw new SchematicUploadException("File name must be alphanumeric with a \".schematic\" extension."); + } + } + else + { + throw new SchematicUploadException(); + } + } + } + else + { + throw new SchematicUploadException("No file transmitted to server."); + } + + return true; + } + + private static class SchematicUploadException extends Exception + { + public SchematicUploadException() + { + } + + public SchematicUploadException(String string) + { + super(string); + } + } + + private boolean isAuthorized(String remoteAddress) + { + TFM_Superadmin entry = TFM_SuperadminList.getAdminEntryByIP(remoteAddress); + return entry != null && entry.isActivated(); + } + private static enum ModuleMode { + LIST("list"), UPLOAD("upload"), DOWNLOAD("download"), INVALID(null); diff --git a/src/me/StevenLawson/TotalFreedomMod/HTTPD/TFM_HTTPD_Manager.java b/src/me/StevenLawson/TotalFreedomMod/HTTPD/TFM_HTTPD_Manager.java index 13efcb89..49626e92 100644 --- a/src/me/StevenLawson/TotalFreedomMod/HTTPD/TFM_HTTPD_Manager.java +++ b/src/me/StevenLawson/TotalFreedomMod/HTTPD/TFM_HTTPD_Manager.java @@ -1,11 +1,16 @@ package me.StevenLawson.TotalFreedomMod.HTTPD; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.net.Socket; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.MIME_PLAINTEXT; +import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.Response; import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry; import me.StevenLawson.TotalFreedomMod.TFM_Log; import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; @@ -14,6 +19,8 @@ import org.bukkit.Bukkit; public class TFM_HTTPD_Manager { + private static final Pattern EXT_REGEX = Pattern.compile("\\.([^\\.\\s]+)$"); + // public static final int PORT = TFM_ConfigEntry.HTTPD_PORT.getInteger(); // private final TFM_HTTPD httpd = new TFM_HTTPD(PORT); @@ -158,7 +165,8 @@ public class TFM_HTTPD_Manager switch (moduleType) { case DUMP: - response = new Module_dump(uri, method, headers, params, files, socket).getResponse(); + //response = new Module_dump(uri, method, headers, params, files, socket).getResponse(); + response = new Response(Response.Status.OK, MIME_PLAINTEXT, "The DUMP module is disabled. It is intended for debugging use only."); break; case SCHEMATIC: response = new Module_schematic(uri, method, headers, params, files, socket).getResponse(); @@ -168,7 +176,6 @@ public class TFM_HTTPD_Manager } } - if (response == null) { return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Error 404: Not Found - The requested resource was not found on this server."); @@ -180,6 +187,39 @@ public class TFM_HTTPD_Manager } } + 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 = NanoHTTPD.MIME_DEFAULT_BINARY; + } + + response = new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, mimetype, new FileInputStream(file)); + response.addHeader("Content-Length", "" + file.length()); + } + catch (IOException ex) + { + TFM_Log.severe(ex); + } + } + + return response; + } + public static TFM_HTTPD_Manager getInstance() { return TFM_HTTPDManagerHolder.INSTANCE;