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);
  }