diff --git a/readme.md b/readme.md index c20faf1..32fa356 100644 --- a/readme.md +++ b/readme.md @@ -25,7 +25,7 @@ Logging: - [Log4J Simple](https://github.com/apache/logging-log4j2) - **2.0.5** - [Junit5](https://github.com/junit-team/junit5) - **5.9.2** -## Download ![Static Badge](https://img.shields.io/badge/version-v0.1.3-%230679b6) +## Download ![Static Badge](https://img.shields.io/badge/version-v0.1.4-%230679b6) Currently this project only supports [GitHub Release](https://github.com/ShuraBlack/JhttpFileShare/releases) with a **.jar**. ## Java Arguments @@ -36,16 +36,17 @@ USAGE: java -jar JhttpFileShare.jar [options/flags] FLAGS: - -ip Shows all Network Interfaces - -v, -verbose Enables verbose mode (more informations Server-side) - -nr Disables the root folder restriction (Access entire file browser) - -h, -help Shows this help + -ip Shows all Network Interfaces + -v, -verbose Enables verbose mode (more informations Server-side) + -nr Disables the root folder restriction (Access entire file browser) + -up Enables uploading to the host mashine + -h, -help Shows this help OPTIONS: - -ip= Sets the IP Address to the given network name [default: 0.0.0.0] - -p, -port= Sets the Port to the given port [default: 80] - -threads= Sets the Thread Pool Size to the given size [default: 3] - -root= Sets the root folder [default: user.dir] + -ip= Sets the IP Address to the given network name [default: 0.0.0.0] + -p, -port= Sets the Port to the given port [default: 80] + -threads= Sets the Thread Pool Size to the given size [default: 3] + -root= Sets the root folder [default: user.dir] ``` ## Usage diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java index 6c8ec71..309f8a5 100644 --- a/src/main/java/controller/Controller.java +++ b/src/main/java/controller/Controller.java @@ -16,7 +16,7 @@ * Main component for the JhttpFileShare application.

* This class is the entry point and will initialize the server and the command line interface. * - * @version 0.1.0 + * @version 0.1.4 * @since 19.Oct.2023 * @author ShuraBlack */ @@ -55,7 +55,7 @@ public Controller(String[] args) { System.out.println( "==================================================================================\n" - + "= JhttpFileShare - Version 0.1.2 =\n" + + "= JhttpFileShare - Version 0.1.4 =\n" + "= Code by ShuraBlack =\n" + "==================================================================================\n" + getProperties() + "\n" @@ -97,16 +97,15 @@ public static List getSessions() { * @return The properties */ private static String getProperties() { - String properties = String.format("= Verbose: %s, " - + "Root Restriction: %s, " - + "Port: %s, " - + "Thread Pool Size: %s", + return String.format("= Verbose: %-27s IP: %-59s =%n" + + "= Root Restriction: %-18s Port: %-57s =%n" + + "= Upload Allowed: %-20s Thread Pool Size: %-45s =", colorize(Config.isVerbose()), + "\033[0;36m" + Config.getIpAddress() + "\033[0m", colorize(Config.isRootRestricted()), - Config.getPort(), - Config.getThreadPoolSize()); - properties += " ".repeat(102 - properties.length()) + " ="; - return properties; + "\033[0;36m" + Config.getPort() + "\033[0m", + colorize(Config.isUploadAllowed()), + "\033[0;36m" + Config.getThreadPoolSize() + "\033[0m"); } /** diff --git a/src/main/java/model/data/FileData.java b/src/main/java/model/data/FileData.java index 5691ce3..744a475 100644 --- a/src/main/java/model/data/FileData.java +++ b/src/main/java/model/data/FileData.java @@ -79,7 +79,7 @@ public static void getDirectoryStructure(String path) { * @param file The file to add. * @param depth The depth of the file in the directory structure. */ - public static void addFile(File file, int depth, StringBuilder builder) { + private static void addFile(File file, int depth, StringBuilder builder) { builder.append(" ".repeat(depth)).append("> ").append(file.getName()).append("
"); for (File f : file.listFiles()) { diff --git a/src/main/java/model/data/Structure.java b/src/main/java/model/data/Structure.java deleted file mode 100644 index 30f1d9c..0000000 --- a/src/main/java/model/data/Structure.java +++ /dev/null @@ -1,23 +0,0 @@ -package model.data; - -/** - * Holds structure of the user dir. - * - * @version 0.1.0 - * @since 19.Oct.2023 - * @author ShuraBlack - */ -public class Structure { - - /** - * Root tree of the user dir. - */ - public static String ROOT_TREE; - - /** - * Root dir of the application. - * This is the {@link System#getProperty(String)} of {@code user.dir}. - */ - public static String ROOT_DIR = System.getProperty("user.dir").replaceAll("\\\\", "/"); - -} diff --git a/src/main/java/model/interpreter/ArgsInterpreter.java b/src/main/java/model/interpreter/ArgsInterpreter.java index 63ee2ca..9029fe9 100644 --- a/src/main/java/model/interpreter/ArgsInterpreter.java +++ b/src/main/java/model/interpreter/ArgsInterpreter.java @@ -4,27 +4,16 @@ import org.apache.logging.log4j.Logger; import util.Config; +import java.io.File; import java.net.*; import java.util.*; -import java.util.regex.Pattern; /** * Interpreter for the command line arguments. *

* This class will interpret the command line arguments and set the corresponding values. - *

- * The following arguments are available: - *
    - *
  • -ip
  • - *
  • -p, -port
  • - *
  • -v, -verbose
  • - *
  • -h, -help
  • - *
  • -nolimit
  • - *
  • -threads
  • - *
  • -root
  • - *
* - * @version 0.1.0 + * @version 0.1.4 * @since 19.Oct.2023 * @author ShuraBlack */ @@ -86,6 +75,9 @@ public static void interpret(String[] args) { case "-root": interpretRoot(entry.getValue()); break; + case "-up": + interpretUpload(); + break; case "-help": case "-h": interpretHelp(); @@ -108,10 +100,13 @@ public static void interpret(String[] args) { private static void interpretIP(String value) { try { if (value == null) { - LOGGER.info("Network Interfaces:\n(Search for the the local network name you use on the devices)\n"); Enumeration nets = NetworkInterface.getNetworkInterfaces(); + StringBuilder builder = new StringBuilder(); + builder.append("\nNetwork Interfaces:\n(Search for the the local network name you use on the devices)\n"); for (NetworkInterface netint : Collections.list(nets)) - displayInterfaceInformation(netint); + displayInterfaceInformation(builder, netint); + + LOGGER.info(builder.toString()); System.exit(0); } else { NetworkInterface netint = NetworkInterface.getByName(value); @@ -196,44 +191,53 @@ private static void interpretThreads(String value) { * @param value The path or null. */ private static void interpretRoot(String value) { - if (value == null || value.isEmpty() || !Pattern.matches("([a-zA-Z]:)?(\\\\[a-zA-Z0-9_.-]+)+\\\\?",value)) { + if (value == null) { LOGGER.warn("Root not found! Use default root."); return; } + File file = new File(value); + if (file.isFile()) { + LOGGER.warn("selected a file, not a directory! Use default root."); + } Config.set("ROOT_DIRECTORY", value.replaceAll("\\\\", "/")); LOGGER.info("Set Root to {}", Config.getRootDirectory()); } + private static void interpretUpload() { + Config.set("UPLOAD_ALLOWED", "true"); + LOGGER.info("Upload got enabled"); + } + /** * Prints out the help. */ private static void interpretHelp() { - System.out.println("JhttpFileShare Server 0.1.2\n"); - System.out.println("USAGE:\n\tjava -jar JhttpFileShare.jar [options/flags]\n"); - System.out.println("FLAGS:"); - System.out.println("\t-ip\t\t\t\t\tShows all Network Interfaces\n" - + "\t-v, -verbose\t\t\t\tEnables verbose mode (more informations Server-side)\n" - + "\t-nr\t\t\t\tDisables the root folder restriction (Access entire file browser)\n" - + "\t-h, -help\t\t\t\tShows this help\n"); - System.out.println("OPTIONS:\n" - + "\t-ip=\t\t\tSets the IP Address to the given network name [default: 0.0.0.0]\n" - + "\t-p, -port=\t\t\tSets the Port to the given port [default: 80]\n" - + "\t-threads=\t\t\t\tSets the Thread Pool Size to the given size [default: 3]\n" - + "\t-root=\t\t\t\tSets the root folder [default: user.dir]\n"); + LOGGER.info("\nJhttpFileShare Server 0.1.4\n\n" + + "USAGE:\n\tjava -jar JhttpFileShare.jar [options/flags]\n\n" + + "FLAGS:\n" + + "\t-ip\t\t\t\t\t\t\tShows all Network Interfaces\n" + + "\t-v, -verbose\t\t\t\tEnables verbose mode (more informations Server-side)\n" + + "\t-nr\t\t\t\t\t\t\tDisables the root folder restriction (Access entire file browser)\n" + + "\t-up\t\t\t\t\t\t\tEnables uploading to the host mashine\n" + + "\t-h, -help\t\t\t\t\tShows this help\n\n" + + "OPTIONS:\n" + + "\t-ip=\t\t\tSets the IP Address to the given network name [default: 0.0.0.0]\n" + + "\t-p, -port=\t\t\tSets the Port to the given port [default: 80]\n" + + "\t-threads=\t\t\t\tSets the Thread Pool Size to the given size [default: 3]\n" + + "\t-root=\t\t\t\tSets the root folder [default: user.dir]\n"); System.exit(0); } /** - * Prints out the network interfaces. + * Appends the network interface information to the given string builder. + * @param builder The string builder. * @param netint The network interface. */ - private static void displayInterfaceInformation(NetworkInterface netint) { - StringBuilder sb = new StringBuilder(); + private static void displayInterfaceInformation(StringBuilder builder, NetworkInterface netint) { Enumeration inetAddresses = netint.getInetAddresses(); for (InetAddress inetAddress : Collections.list(inetAddresses)) { if (inetAddress instanceof Inet6Address) continue; - sb.append(netint.getName()).append(" - ").append(inetAddress.getHostAddress()).append("\n"); + builder.append(netint.getName()).append(" - ").append(inetAddress.getHostAddress()).append("\n"); } - System.out.printf(sb.toString()); } } diff --git a/src/main/java/model/pages/DirectoryPage.java b/src/main/java/model/pages/DirectoryPage.java index a6de847..c8079ac 100644 --- a/src/main/java/model/pages/DirectoryPage.java +++ b/src/main/java/model/pages/DirectoryPage.java @@ -15,8 +15,8 @@ import java.util.Map; import java.util.Optional; -import static util.FileSize.convertSize; -import static util.Query.queryToMap; +import static util.FileSize.convert; +import static util.Query.toMap; /** * This class is responsible for the directory page request. @@ -48,7 +48,7 @@ public void handle(HttpExchange he) throws IOException { userSession = session.get(); } - Map params = queryToMap(he.getRequestURI().getQuery()); + Map params = toMap(he.getRequestURI().getQuery()); if (params.containsKey("folder")) { userSession.setWorkDirectory(params.get("folder")); userSession.clear(); @@ -90,7 +90,7 @@ public static String htmlFromUserSession(UserSession session) { html.append("
").append(name).append("
\n"); if (file.getSize() > 0) { - html.append("
").append(convertSize(file.getSize())).append("
\n"); + html.append("
").append(convert(file.getSize())).append("
\n"); } } html.append("\n"); @@ -111,6 +111,7 @@ public static String htmlFromUserSession(UserSession session) { * @return The filled content. */ public static String populateContent(UserSession userSession, String content) { + content = content.replace("{{uploadDisabled}}", !Config.isUploadAllowed() ? "display: none;" : "display: inline-block;"); content = content.replace("{{returnDisabled}}", Config.isRootRestricted() && userSession.getWorkDirectory().equals(Config.getRootDirectory()) ? "display: none;" : "display: inline-block;"); diff --git a/src/main/java/model/pages/DownloadPage.java b/src/main/java/model/pages/DownloadPage.java index cfdc94a..53203ce 100644 --- a/src/main/java/model/pages/DownloadPage.java +++ b/src/main/java/model/pages/DownloadPage.java @@ -13,7 +13,7 @@ import java.nio.file.Files; import java.util.Map; -import static util.Query.queryToMap; +import static util.Query.toMap; /** * This class is responsible for the download page request. @@ -36,7 +36,7 @@ public class DownloadPage implements HttpHandler { @Override public void handle(HttpExchange he) throws IOException { - Map params = queryToMap(he.getRequestURI().getQuery()); + Map params = toMap(he.getRequestURI().getQuery()); if (params.isEmpty() || !params.containsKey(FILENAME) || !params.get(FILENAME).contains(Config.getRootDirectory())) { return; } diff --git a/src/main/java/model/pages/UploadPage.java b/src/main/java/model/pages/UploadPage.java index dce9183..1bf9f7a 100644 --- a/src/main/java/model/pages/UploadPage.java +++ b/src/main/java/model/pages/UploadPage.java @@ -12,6 +12,7 @@ import java.io.*; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,11 +39,15 @@ public class UploadPage implements HttpHandler { @Override public void handle(HttpExchange httpExchange) throws IOException { + if (!Config.isUploadAllowed()) { + return; + } + Headers headers = httpExchange.getRequestHeaders(); String contentType = headers.getFirst("Content-Type"); if(contentType.startsWith("multipart/form-data")){ String boundary = contentType.substring(contentType.indexOf("boundary=")+9); - byte[] boundaryBytes = ("\r\n--" + boundary).getBytes(Charset.forName("UTF-8")); + byte[] boundaryBytes = ("\r\n--" + boundary).getBytes(StandardCharsets.UTF_8); byte[] payload = getInputAsBinary(httpExchange.getRequestBody()); ArrayList list = new ArrayList<>(); @@ -56,7 +61,7 @@ public void handle(HttpExchange httpExchange) throws IOException { } byte[] part = Arrays.copyOfRange(payload,startPart,endPart); - int headerEnd = indexOf(part,"\r\n\r\n".getBytes(Charset.forName("UTF-8")),0,part.length-1); + int headerEnd = indexOf(part,"\r\n\r\n".getBytes(StandardCharsets.UTF_8),0,part.length-1); if(headerEnd>0) { MultiPart p = new MultiPart(); byte[] head = Arrays.copyOfRange(part, 0, headerEnd); diff --git a/src/main/java/model/server/Server.java b/src/main/java/model/server/Server.java index 4a9f342..06f5701 100644 --- a/src/main/java/model/server/Server.java +++ b/src/main/java/model/server/Server.java @@ -1,7 +1,6 @@ package model.server; import com.sun.net.httpserver.HttpServer; -import controller.Controller; import model.pages.DirectoryPage; import model.pages.DownloadPage; import model.pages.UploadPage; @@ -17,7 +16,7 @@ * This class represents the server component of the JhttpFileShare application.

* It will create a HttpServer instance and register the needed contexts. * - * @version 0.1.0 + * @version 0.1.4 * @since 19.Oct.2023 * @author ShuraBlack */ @@ -43,13 +42,18 @@ public Server() { httpServer.createContext("/directory", new DirectoryPage()); if (Config.isVerbose()) LOGGER.info("Created context /directory"); + httpServer.createContext("/download", new DownloadPage()); if (Config.isVerbose()) LOGGER.info("Created context /download"); + httpServer.createContext("/upload", new UploadPage()); - if (Config.isVerbose()) - LOGGER.info("Created context /upload"); - httpServer.setExecutor(Executors.newFixedThreadPool(Config.getThreadPoolSize())); + if (Config.isUploadAllowed()) { + if (Config.isVerbose()) + LOGGER.info("Created context /upload"); + httpServer.setExecutor(Executors.newFixedThreadPool(Config.getThreadPoolSize())); + } + httpServer.start(); } catch (IOException e) { LOGGER.error("Couldnt start Server!", e); diff --git a/src/main/java/model/session/UserSession.java b/src/main/java/model/session/UserSession.java index f0c3733..7ebdaa7 100644 --- a/src/main/java/model/session/UserSession.java +++ b/src/main/java/model/session/UserSession.java @@ -109,4 +109,5 @@ public void setWorkDirectory(String workDirectory) { this.workDirectory = workDirectory; this.workDirectory = this.workDirectory.replaceAll("\\\\", "/"); } + } diff --git a/src/main/java/util/Config.java b/src/main/java/util/Config.java index 3b674e6..e96edf9 100644 --- a/src/main/java/util/Config.java +++ b/src/main/java/util/Config.java @@ -6,7 +6,7 @@ * This class is used to store the configuration of the server. * It also contains the default values. * - * @version 0.1.1 + * @version 0.1.4 * @since 20.Oct.2023 * @autor ShuraBlack */ @@ -33,6 +33,7 @@ public static void init() { APP_CONFIG.put("VERBOSE", "false"); APP_CONFIG.put("ROOT_DIRECTORY", System.getProperty("user.dir").replaceAll("\\\\", "/")); APP_CONFIG.put("ROOT_STRUCTURE", ""); + APP_CONFIG.put("UPLOAD_ALLOWED", "false"); } // ================================================================================================================= @@ -111,4 +112,12 @@ public static String getRootDirectory() { public static String getRootStructure() { return APP_CONFIG.getProperty("ROOT_STRUCTURE"); } + + /** + * Get the upload mode of the server. + * @return The upload mode. + */ + public static boolean isUploadAllowed() { + return Boolean.parseBoolean(APP_CONFIG.getProperty("UPLOAD_ALLOWED")); + } } diff --git a/src/main/java/util/FileSize.java b/src/main/java/util/FileSize.java index 1975dc6..4e2aab1 100644 --- a/src/main/java/util/FileSize.java +++ b/src/main/java/util/FileSize.java @@ -19,7 +19,7 @@ private FileSize() { } * @param bytes The size in bytes. * @return The human-readable string. */ - public static String convertSize(long bytes) { + public static String convert(long bytes) { if (bytes < 1024) { return bytes + " byte"; } else if (bytes < 1024 * 1024) { diff --git a/src/main/java/util/Query.java b/src/main/java/util/Query.java index 7e420bb..a885d30 100644 --- a/src/main/java/util/Query.java +++ b/src/main/java/util/Query.java @@ -23,7 +23,7 @@ private Query() { } * @param query The query string. * @return The map with the key value pairs. */ - public static Map queryToMap(String query) { + public static Map toMap(String query) { if(query == null || query.isEmpty()){ return Collections.emptyMap(); } diff --git a/src/main/resources/html/Directory.html b/src/main/resources/html/Directory.html index de7668c..4a92ab2 100644 --- a/src/main/resources/html/Directory.html +++ b/src/main/resources/html/Directory.html @@ -93,6 +93,10 @@ margin-left: 3rem; } + .upload { + {{uploadDisabled}} + } + .uploadForm { background-color: transparent; } @@ -121,11 +125,13 @@
-

📤 Upload

-
- - -
+
+

📤 Upload

+
+ + +
+

📂 Directory

{{workingDir}}

diff --git a/src/test/java/util/QueryTest.java b/src/test/java/util/QueryTest.java index 9dce69c..ff2f899 100644 --- a/src/test/java/util/QueryTest.java +++ b/src/test/java/util/QueryTest.java @@ -13,7 +13,7 @@ class QueryTest { @DisplayName("queryToMap() - valid input - return mapping") void queryToMap_validInput() { final String query = "key1=value1&key2=value2&key3=value3"; - final Map map = Query.queryToMap(query); + final Map map = Query.toMap(query); assertEquals(3, map.size()); assertEquals("value1", map.get("key1")); @@ -25,7 +25,7 @@ void queryToMap_validInput() { @DisplayName("queryToMap() - empty input - return empty mapping") void queryToMap_emptyInput() { final String query = ""; - final Map map = Query.queryToMap(query); + final Map map = Query.toMap(query); assertEquals(0, map.size()); } @@ -34,7 +34,7 @@ void queryToMap_emptyInput() { @DisplayName("queryToMap() - null input - return empty mapping") void queryToMap_nullInput() { final String query = null; - final Map map = Query.queryToMap(query); + final Map map = Query.toMap(query); assertEquals(0, map.size()); }