public Response serve(IHTTPSession session) { Map<String, String> header = session.getHeaders(); Map<String, String> parms = session.getParms(); String uri = session.getUri(); { if (uri.startsWith("/")) { int nextSlash = uri.indexOf("/", 1); String appName = uri.substring(1, nextSlash); StringBuilder b = new StringBuilder(); b.append(session.getMethod()).append(" '").append(uri).append("' \n"); Iterator<String> e = header.keySet().iterator(); while (e.hasNext()) { String value = e.next(); b.append(" HDR: '") .append(value) .append("' = '") .append(header.get(value)) .append("'\n"); } e = parms.keySet().iterator(); while (e.hasNext()) { String value = e.next(); b.append(" PRM: '") .append(value) .append("' = '") .append(header.get(value)) .append("'\n"); } WebLogger.getLogger(appName).i(t, b.toString()); } } // Make sure we won't die of an exception later File root = new File(ODKFileUtils.getOdkFolder()); try { ODKFileUtils.verifyExternalStorageAvailability(); if (!root.exists() || !root.isDirectory()) { return getInternalErrorResponse( "given path is not a directory (" + root.getAbsolutePath() + ")."); } } catch (Exception e) { return getInternalErrorResponse( "exception " + e.toString() + " accessing directory (" + root.getAbsolutePath() + ")."); } return respond(Collections.unmodifiableMap(header), session, uri); }
private boolean canServeUri(String uri, File homeDir) { boolean canServeUri; File f = new File(homeDir, uri); canServeUri = f.exists(); if (canServeUri) { // TODO: more rigorous checks for handling "../"? File base = f.getAbsoluteFile(); String appName = ODKFileUtils.extractAppNameFromPath(base); String relativePath = ODKFileUtils.asRelativePath(appName, base); Set<String> exclusions = ODKFileUtils.getDirectoriesToExcludeFromWebServer(); // further restrict relativePath to not reference one of the private directories String[] parts = relativePath.split("/"); canServeUri = parts.length > 1 && !exclusions.contains(parts[1]); } return canServeUri; }
private Response respond(Map<String, String> headers, IHTTPSession session, String uri) { // 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("src/main") || uri.endsWith("src/main") || uri.contains("../")) { return getForbiddenResponse("Won't serve ../ for security reasons."); } boolean canServeUri = false; File homeDir = new File(ODKFileUtils.getOdkFolder()); canServeUri = canServeUri(uri, homeDir); if (!canServeUri) { return getNotFoundResponse(); } // Browsers get confused without '/' after the directory, send a redirect. File f = new File(homeDir, uri); if (f.isDirectory() && !uri.endsWith("/")) { uri += "/"; Response res = createResponse( Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri + "</a></body></html>"); res.addHeader("Location", uri); return res; } if (f.isDirectory()) { // First look for index files (index.html, index.htm, etc) and if none found, list the // directory if readable. String indexFile = findIndexFileInDirectory(f); if (indexFile == null) { if (f.canRead()) { // No index file, list the directory if it is readable return createResponse(Response.Status.OK, NanoHTTPD.MIME_HTML, listDirectory(uri, f)); } else { return getForbiddenResponse("No directory listing."); } } else { return respond(headers, session, uri + indexFile); } } String mimeTypeForFile = getMimeTypeForFile(uri); Response response = null; response = serveFile(uri, headers, f, mimeTypeForFile); return response != null ? response : getNotFoundResponse(); }
/** * Creates required directories on the SDCard (or other external storage) * * @return true if there are tables present * @throws RuntimeException if there is no SDCard or the directory exists as a non directory */ public static void createODKDirs(String appName) throws RuntimeException { ODKFileUtils.verifyExternalStorageAvailability(); ODKFileUtils.assertDirectoryStructure(appName); }