Merge branch 'untested' into staging

This commit is contained in:
Steven Lawson 2013-09-03 16:41:12 -04:00
commit 8f70fa2c82
9 changed files with 375 additions and 23 deletions

View File

@ -1,7 +1,7 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import static org.apache.commons.lang3.StringEscapeUtils.*; import static org.apache.commons.lang3.StringEscapeUtils.*;
@ -41,7 +41,7 @@ public class HTMLGenerationTools
return output.toString(); return output.toString();
} }
public static <T> String list(List<T> list) public static <T> String list(Collection<T> list)
{ {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();

View File

@ -1,28 +1,64 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; 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 java.util.Map;
import org.apache.commons.lang.StringUtils; 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.*; import static me.StevenLawson.TotalFreedomMod.HTTPD.HTMLGenerationTools.*;
public class Module_dump extends TFM_HTTPD_Module public class Module_dump extends TFM_HTTPD_Module
{ {
public Module_dump(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) private File echoFile = null;
private final String body;
public Module_dump(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{ {
super(uri, method, headers, params, files); 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 @Override
public String getBody() public String getBody()
{
return body;
}
private String body()
{ {
StringBuilder responseBody = new StringBuilder(); StringBuilder responseBody = new StringBuilder();
String remoteAddress = socket.getInetAddress().getHostAddress();
String[] args = StringUtils.split(uri, "/"); String[] args = StringUtils.split(uri, "/");
responseBody responseBody
.append(paragraph("URI: " + uri)) .append(paragraph("URI: " + uri))
.append(paragraph("args (Length: " + args.length + "): " + StringUtils.join(args, ","))) .append(paragraph("args (Length: " + args.length + "): " + StringUtils.join(args, ",")))
.append(paragraph("Method: " + method.toString())) .append(paragraph("Method: " + method.toString()))
.append(paragraph("Remote Address: " + remoteAddress))
.append(paragraph("Headers:")) .append(paragraph("Headers:"))
.append(list(headers)) .append(list(headers))
.append(paragraph("Params:")) .append(paragraph("Params:"))
@ -30,6 +66,39 @@ public class Module_dump extends TFM_HTTPD_Module
.append(paragraph("Files:")) .append(paragraph("Files:"))
.append(list(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)
{
TFM_Log.severe(ex);
}
}
}
return responseBody.toString(); return responseBody.toString();
} }

View File

@ -5,6 +5,7 @@ import java.io.FileInputStream;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -22,7 +23,7 @@ import me.StevenLawson.TotalFreedomMod.TFM_ConfigEntry;
public class Module_file extends TFM_HTTPD_Module public class Module_file extends TFM_HTTPD_Module
{ {
private final File rootDir = new File(TFM_ConfigEntry.HTTPD_PUBLIC_FOLDER.getString()); private final File rootDir = new File(TFM_ConfigEntry.HTTPD_PUBLIC_FOLDER.getString());
private static final Map<String, String> MIME_TYPES = new HashMap<String, String>(); public static final Map<String, String> MIME_TYPES = new HashMap<String, String>();
static static
{ {
@ -53,9 +54,9 @@ public class Module_file extends TFM_HTTPD_Module
MIME_TYPES.put("class", "application/octet-stream"); MIME_TYPES.put("class", "application/octet-stream");
} }
public Module_file(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) public Module_file(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{ {
super(uri, method, headers, params, files); super(uri, method, headers, params, files, socket);
} }
private File getRootDir() private File getRootDir()

View File

@ -1,5 +1,6 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -20,9 +21,9 @@ import static org.apache.commons.lang3.StringEscapeUtils.*;
public class Module_help extends TFM_HTTPD_Module public class Module_help extends TFM_HTTPD_Module
{ {
public Module_help(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) public Module_help(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{ {
super(uri, method, headers, params, files); super(uri, method, headers, params, files, socket);
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.Map; import java.util.Map;
import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList; import me.StevenLawson.TotalFreedomMod.TFM_SuperadminList;
import me.StevenLawson.TotalFreedomMod.TFM_Util; import me.StevenLawson.TotalFreedomMod.TFM_Util;
@ -8,9 +9,9 @@ import org.bukkit.entity.Player;
public class Module_list extends TFM_HTTPD_Module public class Module_list extends TFM_HTTPD_Module
{ {
public Module_list(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) public Module_list(String uri, NanoHTTPD.Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{ {
super(uri, method, headers, params, files); super(uri, method, headers, params, files, socket);
} }
@Override @Override

View File

@ -0,0 +1,218 @@
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<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{
super(uri, method, headers, params, files, socket);
}
@Override
public String getTitle()
{
return "TotalFreedomMod :: Schematic Manager";
}
@Override
public String getBody()
{
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());
schematicsFormatted.add("<li><a href=\"/schematic/download?schematicName=" + filename + "\">" + filename + "</a></li>");
}
Collections.sort(schematicsFormatted);
out
.append(HTMLGenerationTools.heading("Schematics:", 1))
.append("<ul>")
.append(StringUtils.join(schematicsFormatted, "\r\n"))
.append("</ul>");
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:
{
out.append(HTMLGenerationTools.paragraph("Invalid request mode."));
break;
}
}
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);
//
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;
}
}
private 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()));
}
}

View File

@ -134,7 +134,7 @@ public abstract class NanoHTTPD
{ {
outputStream = finalAccept.getOutputStream(); outputStream = finalAccept.getOutputStream();
TempFileManager tempFileManager = tempFileManagerFactory.create(); TempFileManager tempFileManager = tempFileManagerFactory.create();
HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream); HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept);
while (!finalAccept.isClosed()) while (!finalAccept.isClosed())
{ {
session.execute(); session.execute();
@ -200,7 +200,7 @@ public abstract class NanoHTTPD
* @return HTTP response, see class Response for details * @return HTTP response, see class Response for details
*/ */
public abstract Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, public abstract Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,
Map<String, String> files); Map<String, String> files, Socket socket);
/** /**
* Override this to customize the server. * Override this to customize the server.
@ -232,7 +232,8 @@ public abstract class NanoHTTPD
Method method = session.getMethod(); Method method = session.getMethod();
Map<String, String> parms = session.getParms(); Map<String, String> parms = session.getParms();
Map<String, String> headers = session.getHeaders(); Map<String, String> headers = session.getHeaders();
return serve(uri, method, headers, parms, files); Socket socket = session.getSocket();
return serve(uri, method, headers, parms, files, socket);
} }
/** /**
@ -742,12 +743,14 @@ public abstract class NanoHTTPD
private Method method; private Method method;
private Map<String, String> parms; private Map<String, String> parms;
private Map<String, String> headers; private Map<String, String> headers;
private Socket socket;
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) private HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, Socket socket)
{ {
this.tempFileManager = tempFileManager; this.tempFileManager = tempFileManager;
this.inputStream = inputStream; this.inputStream = inputStream;
this.outputStream = outputStream; this.outputStream = outputStream;
this.socket = socket;
} }
public void execute() throws IOException public void execute() throws IOException
@ -1281,6 +1284,11 @@ public abstract class NanoHTTPD
{ {
return inputStream; return inputStream;
} }
public final Socket getSocket()
{
return socket;
}
} }
private static final class ResponseException extends Exception private static final class ResponseException extends Exception

View File

@ -1,10 +1,16 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.Socket;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Future; 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 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_ConfigEntry;
import me.StevenLawson.TotalFreedomMod.TFM_Log; import me.StevenLawson.TotalFreedomMod.TFM_Log;
import me.StevenLawson.TotalFreedomMod.TotalFreedomMod; import me.StevenLawson.TotalFreedomMod.TotalFreedomMod;
@ -13,6 +19,8 @@ import org.bukkit.Bukkit;
public class TFM_HTTPD_Manager public class TFM_HTTPD_Manager
{ {
private static final Pattern EXT_REGEX = Pattern.compile("\\.([^\\.\\s]+)$");
//
public static final int PORT = TFM_ConfigEntry.HTTPD_PORT.getInteger(); public static final int PORT = TFM_ConfigEntry.HTTPD_PORT.getInteger();
// //
private final TFM_HTTPD httpd = new TFM_HTTPD(PORT); private final TFM_HTTPD httpd = new TFM_HTTPD(PORT);
@ -64,7 +72,8 @@ public class TFM_HTTPD_Manager
DUMP(false, "dump"), DUMP(false, "dump"),
HELP(true, "help"), HELP(true, "help"),
LIST(true, "list"), LIST(true, "list"),
FILE(false, "file"); FILE(false, "file"),
SCHEMATIC(false, "schematic");
private final boolean runOnBukkitThread; private final boolean runOnBukkitThread;
private final String name; private final String name;
@ -110,7 +119,13 @@ public class TFM_HTTPD_Manager
} }
@Override @Override
public Response serve(final String uri, final Method method, final Map<String, String> headers, final Map<String, String> params, final Map<String, String> files) public Response serve(
final String uri,
final Method method,
final Map<String, String> headers,
final Map<String, String> params,
final Map<String, String> files,
final Socket socket)
{ {
Response response = null; Response response = null;
@ -127,9 +142,9 @@ public class TFM_HTTPD_Manager
switch (moduleType) switch (moduleType)
{ {
case HELP: case HELP:
return new Module_help(uri, method, headers, params, files).getResponse(); return new Module_help(uri, method, headers, params, files, socket).getResponse();
case LIST: case LIST:
return new Module_list(uri, method, headers, params, files).getResponse(); return new Module_list(uri, method, headers, params, files, socket).getResponse();
default: default:
return null; return null;
} }
@ -150,14 +165,17 @@ public class TFM_HTTPD_Manager
switch (moduleType) switch (moduleType)
{ {
case DUMP: case DUMP:
response = new Module_dump(uri, method, headers, params, files).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();
break; break;
default: default:
response = new Module_file(uri, method, headers, params, files).getResponse(); response = new Module_file(uri, method, headers, params, files, socket).getResponse();
} }
} }
if (response == null) 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."); return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Error 404: Not Found - The requested resource was not found on this server.");
@ -169,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() public static TFM_HTTPD_Manager getInstance()
{ {
return TFM_HTTPDManagerHolder.INSTANCE; return TFM_HTTPDManagerHolder.INSTANCE;

View File

@ -1,5 +1,6 @@
package me.StevenLawson.TotalFreedomMod.HTTPD; package me.StevenLawson.TotalFreedomMod.HTTPD;
import java.net.Socket;
import java.util.Map; import java.util.Map;
import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.*; import me.StevenLawson.TotalFreedomMod.HTTPD.NanoHTTPD.*;
@ -10,14 +11,16 @@ public abstract class TFM_HTTPD_Module
protected final Map<String, String> headers; protected final Map<String, String> headers;
protected final Map<String, String> params; protected final Map<String, String> params;
protected final Map<String, String> files; protected final Map<String, String> files;
protected final Socket socket;
public TFM_HTTPD_Module(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) public TFM_HTTPD_Module(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files, Socket socket)
{ {
this.uri = uri; this.uri = uri;
this.method = method; this.method = method;
this.headers = headers; this.headers = headers;
this.params = params; this.params = params;
this.files = files; this.files = files;
this.socket = socket;
} }
public String getBody() public String getBody()