/* * Copyright (C) 2014 me * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package nars.web; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Properties; import static nars.web.HTTPServer.HTTP_FORBIDDEN; import static nars.web.HTTPServer.HTTP_INTERNALERROR; import static nars.web.HTTPServer.HTTP_NOTFOUND; import static nars.web.HTTPServer.HTTP_OK; import static nars.web.HTTPServer.HTTP_REDIRECT; import static nars.web.HTTPServer.MIME_DEFAULT_BINARY; import static nars.web.HTTPServer.MIME_HTML; import static nars.web.HTTPServer.MIME_PLAINTEXT; /** * * @author me */ public class HTTPServeFiles extends HTTPServer { File staticFilePath; public HTTPServeFiles(int port, File staticFilePath) throws IOException { super(port); this.staticFilePath = staticFilePath; } @Override public Response serve(String uri, String method, Properties header, Properties parms) { return serveFile(uri, header, staticFilePath, true); } // // File server code // /** * Serves file from homeDir and its' subdirectories (only). Uses only URI, * ignores all headers and HTTP parameters. */ public Response serveFile(String uri, Properties header, File homeDir, boolean allowDirectoryListing) { // Make sure we won't die of an exception later if (!homeDir.isDirectory()) { return new Response(HTTP_INTERNALERROR, MIME_PLAINTEXT, "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); } // 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("..") || uri.endsWith("..") || uri.contains("../")) { return new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); } File f = new File(homeDir, uri); if (!f.exists()) { return new Response(HTTP_NOTFOUND, MIME_PLAINTEXT, "Error 404, file not found."); } // List the directory, if necessary if (f.isDirectory()) { // Browsers get confused without '/' after the // directory, send a redirect. if (!uri.endsWith("/")) { uri += "/"; Response r = new Response(HTTP_REDIRECT, MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri + "</a></body></html>"); r.addHeader("Location", uri); return r; } // 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"); } // No index file, list the directory else if (allowDirectoryListing) { String[] files = f.list(); String msg = "<html><body><h1>Directory " + uri + "</h1><br/>"; if (uri.length() > 1) { String u = uri.substring(0, uri.length() - 1); int slash = u.lastIndexOf('/'); if (slash >= 0 && slash < u.length()) { msg += "<b><a href=\"" + uri.substring(0, slash + 1) + "\">..</a></b><br/>"; } } for (int i = 0; i < files.length; ++i) { File curFile = new File(f, files[i]); boolean dir = curFile.isDirectory(); if (dir) { msg += "<b>"; files[i] += "/"; } try { msg += "<a href=\"" + encodeUri(uri + files[i]) + "\">" + files[i] + "</a>"; } catch (UnsupportedEncodingException ex) { msg += "<a>" + files[i] + "</a>"; } // Show file size if (curFile.isFile()) { long len = curFile.length(); msg += "  <font size=2>("; if (len < 1024) { msg += curFile.length() + " bytes"; } else if (len < 1024 * 1024) { msg += curFile.length() / 1024 + "." + (curFile.length() % 1024 / 10 % 100) + " KB"; } else { msg += curFile.length() / (1024 * 1024) + "." + curFile.length() % (1024 * 1024) / 10 % 100 + " MB"; } msg += ")</font>"; } msg += "<br/>"; if (dir) { msg += "</b>"; } } return new Response(HTTP_OK, MIME_HTML, msg); } else { return new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: No directory listing."); } } try { // Get MIME type from file name extension, if possible String mime = null; int dot = f.getCanonicalPath().lastIndexOf('.'); if (dot >= 0) { mime = (String) theMimeTypes.get(f.getCanonicalPath().substring(dot + 1).toLowerCase()); } if (mime == null) { mime = MIME_DEFAULT_BINARY; } // Support (simple) skipping: long startFrom = 0; String range = header.getProperty("Range"); if (range != null) { if (range.startsWith("bytes=")) { range = range.substring("bytes=".length()); int minus = range.indexOf('-'); if (minus > 0) { range = range.substring(0, minus); } try { startFrom = Long.parseLong(range); } catch (NumberFormatException nfe) { } } } FileInputStream fis = new FileInputStream(f); fis.skip(startFrom); Response r = new Response(HTTP_OK, mime, fis); r.addHeader("Content-length", "" + (f.length() - startFrom)); r.addHeader("Content-range", "" + startFrom + "-" + (f.length() - 1) + "/" + f.length()); return r; } catch (IOException ioe) { return new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); } } }