Compare commits

..

No commits in common. "1998000cf78db29a0a1a224b9699ab23cfed7c24" and "0a71b04b669d83b7563cfaf7e4ab00d1999a9c8c" have entirely different histories.

6 changed files with 55 additions and 104 deletions

View file

@ -30,40 +30,28 @@ public class McWebCommand {
.executes(context -> {
String name = StringArgumentType.getString(context, "Name");
String expires = StringArgumentType.getString(context, "Expiration Time (example: 1y3d4h -> 1 Year, 3 Days, 4 Hours)");
if (context.getSource().isExecutedByPlayer()) {
String result = registerToken(name, expires);
if (result.equals("exists")) {
context.getSource().sendFeedback(() -> Text.of("A token with that name already exists"), false);
return 0;
} else if (result.equals("failed")) {
context.getSource().sendFeedback(() -> Text.of("Failed to create token (Unknown Error)"), false);
return 0;
} else {
context.getSource().sendFeedback(() -> Text.of("Token Created! - Expires " + convertToHumanReadable(convertExpirationDate(expires))), true);
if (MC_SERVER != null) {
if (context.getSource().isExecutedByPlayer()) {
// get the player name
String playerName = Objects.requireNonNull(context.getSource().getPlayer()).getName().getString();
MC_SERVER.getCommandManager().executeWithPrefix(MC_SERVER.getCommandSource(), "tellraw " + playerName + " [\"\",\"Token (will only show once): \",\"\\n\",\"[\",{\"text\":\"" + result + "\",\"color\":\"green\",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Click to Copy to Clipboard\"]}},\"]\"]");
}
return 1;
}
context.getSource().sendFeedback(() -> Text.of("Failed to create token (Unknown Error)"), false);
return 0;
}
if (result.equals("exists")) {
context.getSource().sendFeedback(() -> Text.of("A token with that name already exists"), false);
return 0;
} else if (result.equals("failed")) {
context.getSource().sendFeedback(() -> Text.of("Failed to create token (Unknown Error)"), false);
return 0;
} else {
context.getSource().sendFeedback(() -> Text.of("ERROR: Due to Security concerns it is not possible to generate tokens from the server console as they are saved in server logs. Tokens must be created by a player! (more info at: https://wiki.jonasjones.dev/McWebserver/Tokens)"), false);
context.getSource().sendFeedback(() -> Text.of("Token Created!\nExpires " + convertToHumanReadable(convertExpirationDate(expires))), true);
if (MC_SERVER != null) {
// get the player name
String playerName = Objects.requireNonNull(context.getSource().getPlayer()).getName().getString();
MC_SERVER.getCommandManager().executeWithPrefix(MC_SERVER.getCommandSource(), "tellraw " + playerName + " [\"\",\"Token (will only show once): \",\"\\n\",\"[\",{\"text\":\"" + result + "\",\"color\":\"green\",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Click to Copy to Clipboard\"]}},\"]\"]");
return 1;
}
context.getSource().sendFeedback(() -> Text.of("Failed to create token (Unknown Error)"), false);
return 0;
}
}))))
.then(literal("list")
.executes(context -> {
try {
context.getSource().sendFeedback(() -> Text.of(listTokens()), false);
return 1;
} catch (Exception e) {
e.printStackTrace();
}
context.getSource().sendFeedback(() -> Text.of(listTokens()), false);
return 1;
}))
.then(literal("delete")

View file

@ -19,6 +19,7 @@ public class ModConfigs {
public static Boolean SERVER_API_ENABLED;
public static Boolean ADV_API_ENABLED;
public static Boolean API_INGAME_COMMAND_ENABLED;
public static String WEB_FILE_NOSUPPORT;
public static Boolean PHP_ENABLED;
public static Boolean VERBOSE = false; //needs to be set to false since the verbose logger is called before config file is fully loaded
@ -49,6 +50,7 @@ public class ModConfigs {
config.addKeyValuePair(new Pair<>("web.api", true), "whether or not the webserver api should be enabled or not");
config.addKeyValuePair(new Pair<>("web.api.adv", true), "whether or not the api should expose information such as player coordinates and inventory");
config.addKeyValuePair(new Pair<>("web.api.cmd", true), "whether or not the ingame command to manage tokens should be enabled or not");
config.addKeyValuePair(new Pair<>("web.file.notSupported", "not_supported.html"), "the name of the html file for 'not supported' page");
config.addKeyValuePair(new Pair<>("logger.verbose", false), "whether or not to log verbose output");
config.addKeyValuePair(new Pair<>("web.php", false), "enable php");
}
@ -63,6 +65,7 @@ public class ModConfigs {
SERVER_API_ENABLED = CONFIG.getOrDefault("web.api", true);
ADV_API_ENABLED = CONFIG.getOrDefault("web.api.adv", true);
API_INGAME_COMMAND_ENABLED = CONFIG.getOrDefault("web.api.cmd", true);
WEB_FILE_NOSUPPORT = CONFIG.getOrDefault("web.file.notSupported", "not_supported.html");
VERBOSE = CONFIG.getOrDefault("logger.verbose", true);
PHP_ENABLED = CONFIG.getOrDefault("web.php", false);
}

View file

@ -25,7 +25,6 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Objects;
import java.util.StringTokenizer;
import static me.jonasjones.mcwebserver.web.ServerHandler.mcserveractive;
@ -36,8 +35,11 @@ public class HttpServer implements Runnable {
static Path WEB_ROOT;
static final String DEFAULT_FILE = ModConfigs.WEB_FILE_ROOT;
static final String FILE_NOT_FOUND = ModConfigs.WEB_FILE_404;
static final String METHOD_NOT_SUPPORTED = ModConfigs.WEB_FILE_NOSUPPORT;
// port to listen connection
static final int PORT = ModConfigs.WEB_PORT;
private static final byte[] NOT_IMPLEMENTED = "HTTP/1.1 405 Method Not Allowed\r\n".getBytes(StandardCharsets.UTF_8);
private static final byte[] OK = "HTTP/1.1 200 OK\r\n".getBytes(StandardCharsets.UTF_8);
private static final byte[] NOT_FOUND = "HTTP/1.1 404 Not Found\r\n".getBytes(StandardCharsets.UTF_8);
private static final byte[] HEADERS = String.join(
@ -129,17 +131,24 @@ public class HttpServer implements Runnable {
// we support only GET and HEAD methods, we check
if (!method.equals("GET") && !method.equals("HEAD")) {
isApiv1Request = false;
VerboseLogger.error("501 Not Implemented : " + method + " method.");
VerboseLogger.info("501 Not Implemented : " + method + " method.");
dataOut.write("HTTP/1.1 501 Not Implemented\r\n".getBytes(StandardCharsets.UTF_8));
// we return the not supported file to the client
Path file = WEB_ROOT.resolve(METHOD_NOT_SUPPORTED);
long fileLength = Files.size(file);
String contentMimeType = "text/html";
//read content to return to client
byte[] fileData = readFileData(file);
// we send HTTP Headers with data to client
dataOut.write(NOT_IMPLEMENTED);
dataOut.write(HEADERS); //hopefully enough credits
dataOut.write("Date: %s\r\n".formatted(Instant.now()).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Type: application/json\r\n".getBytes(StandardCharsets.UTF_8));
String jsonString = ErrorHandler.notImplementedErrorString();
byte[] jsonBytes = jsonString.getBytes(StandardCharsets.UTF_8);
int contentLength = jsonBytes.length;
dataOut.write(("Content-Length: " + contentLength + "\r\n").getBytes(StandardCharsets.UTF_8));
dataOut.write("\r\n".getBytes(StandardCharsets.UTF_8)); // Blank line before content
dataOut.write(jsonBytes, 0, contentLength);
dataOut.write("Content-Type: %s\r\n".formatted(contentMimeType).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Length: %s\r\n".formatted(fileLength).getBytes(StandardCharsets.UTF_8));
dataOut.write(CRLF); // blank line between headers and content, very important !
// file
dataOut.write(fileData, 0, fileData.length);
dataOut.flush();
} else if (isApiV1Request(fileRequested)) {
isApiv1Request = true;
@ -350,41 +359,22 @@ public class HttpServer implements Runnable {
}
private void fileNotFound(PrintWriter out, OutputStream dataOut, String fileRequested) throws IOException {
try {
Path file = WEB_ROOT.resolve(FILE_NOT_FOUND);
int fileLength = (int) Files.size(file);
String contentType = "text/html";
byte[] fileData = readFileData(file);
Path file = WEB_ROOT.resolve(FILE_NOT_FOUND);
int fileLength = (int) Files.size(file);
String contentType = "text/html";
byte[] fileData = readFileData(file);
dataOut.write(NOT_FOUND);
dataOut.write(HEADERS);
dataOut.write("Date: %s\r\n".formatted(Instant.now()).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Type: %s\r\n".formatted(contentType).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Length: %s\r\n".formatted(fileLength).getBytes(StandardCharsets.UTF_8));
dataOut.write(CRLF); // blank line between headers and content, very important !
out.flush(); // flush character output stream buffer
dataOut.write(NOT_FOUND);
dataOut.write(HEADERS);
dataOut.write("Date: %s\r\n".formatted(Instant.now()).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Type: %s\r\n".formatted(contentType).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Length: %s\r\n".formatted(fileLength).getBytes(StandardCharsets.UTF_8));
dataOut.write(CRLF); // blank line between headers and content, very important !
out.flush(); // flush character output stream buffer
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
VerboseLogger.error("File " + fileRequested + " not found");
} catch (Exception e) {
VerboseLogger.error("Error with file not found exception : " + e.getMessage());
if (Objects.equals(e.getMessage(), WEB_ROOT.resolve(FILE_NOT_FOUND).toString())) {
VerboseLogger.error("Couldn't find fallback 404 file. Sending JSON 404 error.");
}
dataOut.write("HTTP/1.1 404 Not Found\r\n".getBytes(StandardCharsets.UTF_8));
dataOut.write("Date: %s\r\n".formatted(Instant.now()).getBytes(StandardCharsets.UTF_8));
dataOut.write("Content-Type: application/json\r\n".getBytes(StandardCharsets.UTF_8));
String jsonString = ErrorHandler.notFoundErrorString();
byte[] jsonBytes = jsonString.getBytes(StandardCharsets.UTF_8);
int contentLength = jsonBytes.length;
dataOut.write(("Content-Length: " + contentLength + "\r\n").getBytes(StandardCharsets.UTF_8));
dataOut.write("\r\n".getBytes(StandardCharsets.UTF_8)); // Blank line before content
dataOut.write(jsonBytes, 0, contentLength);
dataOut.flush();
VerboseLogger.error("File " + fileRequested + " not found");
}
VerboseLogger.error("File " + fileRequested + " not found");
}
}

View file

@ -101,8 +101,10 @@ public class ServerHandler implements Runnable {
Path path = FabricLoader.getInstance().getGameDir();
Path webroot = path.resolve(ModConfigs.WEB_ROOT);
Path index = webroot.resolve(ModConfigs.WEB_FILE_ROOT);
Path notsupported = webroot.resolve(ModConfigs.WEB_FILE_NOSUPPORT);
Path notfound = webroot.resolve(ModConfigs.WEB_FILE_404);
index.toFile().mkdirs();
notsupported.toFile().mkdirs();
notfound.toFile().mkdirs();
}
}

View file

@ -45,14 +45,6 @@ public class ErrorHandler {
return gson.toJsonTree(notFoundError()).getAsJsonObject().toString();
}
public static Error notImplementedError() {
return new Error(501, "Not Implemented");
}
public static String notImplementedErrorString() {
return gson.toJsonTree(notImplementedError()).getAsJsonObject().toString();
}
public static Error customError(int status, String message) {
return new Error(status, message);
}

View file

@ -140,34 +140,10 @@ public class TokenManager {
if (tokens.size() == 0) {
return "No active tokens.";
}
int longestName = 0;
int longestExpires = 0;
for (Token token : tokens) {
if (token.getName().length() > longestName) {
longestName = token.getName().length();
}
if (convertToHumanReadable(token.getExpires()).length() > longestExpires) {
longestExpires = convertToHumanReadable(token.getExpires()).length();
}
}
sb.append("Active Tokens:\n");
sb.append("Name")
.append(" ".repeat(Math.max(longestName - 4, 0)))
.append(" | ").append("Expires")
.append(" ".repeat(Math.max(longestExpires - 6, 0)))
.append(" | TokenStart\n");
sb.append("-".repeat(Math.max(longestName, 4)))
.append(" | ")
.append("-".repeat(Math.max(longestExpires, 7)))
.append(" | ")
.append("-".repeat(10))
.append("\n");
sb.append("Name | Expiration Date | Beginning of Token value\n");
for (Token token : tokens) {
String humanExpires = convertToHumanReadable(token.getExpires());
sb.append(token.getName())
.append(" ".repeat(Math.max(longestName - token.getName().length(), 4 - token.getName().length())))
.append(" | ").append(humanExpires).append(" ".repeat(Math.max(longestExpires - humanExpires.length(), 7 - humanExpires.length())))
.append(" | ").append(token.getTokenStart()).append("...").append("\n");
sb.append(token.getName()).append(" | ").append(convertToHumanReadable(token.getExpires())).append(" | ").append(token.getTokenStart()).append("...").append("\n");
}
return sb.toString();
}