All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fitnesse.responders.files.FileResponder Maven / Gradle / Ivy

There is a newer version: 20241026
Show newest version
// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.responders.files;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.text.ParseException;
import java.util.Date;

import fitnesse.authentication.SecureOperation;
import fitnesse.authentication.SecureResponder;
import fitnesse.util.Clock;
import fitnesse.wiki.PathParser;
import util.FileUtil;
import util.StreamReader;
import fitnesse.FitNesseContext;
import fitnesse.http.InputStreamResponse;
import fitnesse.http.Request;
import fitnesse.http.Response;
import fitnesse.http.SimpleResponse;
import fitnesse.responders.ErrorResponder;
import fitnesse.responders.NotFoundResponder;

public class FileResponder implements SecureResponder {
  // 1000-trick: remove milliseconds.
  private static final Date LAST_MODIFIED_FOR_RESOURCES = new Date((Clock.currentTimeInMillis() / 1000) * 1000);

  private static final int RESOURCE_SIZE_LIMIT = 262144 * 2;
  private static final FileNameMap fileNameMap = URLConnection.getFileNameMap();
  String resource;
  File requestedFile;
  Date lastModifiedDate;

  @Override
  public Response makeResponse(FitNesseContext context, Request request) throws Exception {
    String rootPath = context.getRootPagePath();
    try {
      resource = URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING);
    } catch (UnsupportedEncodingException e) {
      return new ErrorResponder(e).makeResponse(context, request);
    }

    requestedFile = new File(rootPath, resource);

    if (!isInFilesDirectory(new File(rootPath), requestedFile)) {
      return new ErrorResponder("Invalid path: " + resource).makeResponse(context, request);
    } else if (requestedFile.isDirectory()) {
      return new DirectoryResponder(resource, requestedFile).makeResponse(context, request);
    }
    if (requestedFile.exists()) {
      return makeFileResponse(request);
    } else if (canLoadFromClasspath()) {
      return makeClasspathResponse(context, request);
    } else {
      return new NotFoundResponder().makeResponse(context, request);
    }
  }


  private boolean canLoadFromClasspath() {
    return resource.startsWith("files/fitnesse/");
  }

  private Response makeClasspathResponse(FitNesseContext context, Request request) throws Exception {

    determineLastModifiedInfo(LAST_MODIFIED_FOR_RESOURCES);

    if (isNotModified(request))
      return createNotModifiedResponse();

    String classpathResource = "/fitnesse/resources/" + resource.substring("files/fitnesse/".length());
    InputStream input;

    input = Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathResource);

    if (input == null) {
      //remove leading slash so path will work with resources inside a JAR file
      while (classpathResource.startsWith("/")) {
        classpathResource = classpathResource.substring(1);
      }
      input = Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathResource);
    }
    if (input == null) {
      return new NotFoundResponder().makeResponse(context, request);
    }
    StreamReader reader = new StreamReader(input);
    // Set a hard limit on the amount of data that can be read:
    byte[] content = reader.readBytes(RESOURCE_SIZE_LIMIT);
    SimpleResponse response = new SimpleResponse();
    response.setContent(content);
    setContentType(classpathResource, response);
    lastModifiedDate = LAST_MODIFIED_FOR_RESOURCES;
    response.setLastModifiedHeader(lastModifiedDate);

    return response;
  }

  private Response makeFileResponse(Request request) throws FileNotFoundException {
    InputStreamResponse response = new InputStreamResponse();
    determineLastModifiedInfo(new Date(requestedFile.lastModified()));

    if (isNotModified(request))
      return createNotModifiedResponse();
    else {
      response.setBody(requestedFile);
      setContentType(requestedFile.getName(), response);
      response.setLastModifiedHeader(lastModifiedDate);
    }
    return response;
  }

  private boolean isNotModified(Request request) {
    if (request.hasHeader("If-Modified-Since")) {
      String queryDateString = request.getHeader("If-Modified-Since");
      try {
        Date queryDate = Response.makeStandardHttpDateFormat().parse(queryDateString);
        if (!queryDate.before(lastModifiedDate))
          return true;
      } catch (ParseException e) {
        //Some browsers use local date formats that we can't parse.
        //So just ignore this exception if we can't parse the date.
      }
    }
    return false;
  }

  private Response createNotModifiedResponse() {
    Response response = new SimpleResponse();
    response.notModified(lastModifiedDate, Clock.currentDate());
    return response;
  }

  private void determineLastModifiedInfo(Date lastModified) {
    // remove milliseconds
    lastModifiedDate = new Date((lastModified.getTime() / 1000) * 1000);
  }

  private void setContentType(String filename, Response response) {
    String contentType = getContentType(filename);
    response.setContentType(contentType);
  }

  public static String getContentType(String filename) {
    String contentType = fileNameMap.getContentTypeFor(filename);
    if (contentType == null) {
      if (filename.endsWith(".css")) {
        contentType = "text/css";
      } else if (filename.endsWith(".js")) {
        contentType = "text/javascript";
      } else if (filename.endsWith(".jar")) {
        contentType = "application/x-java-archive";
      } else if ((filename.endsWith(".jpg")) || (filename.endsWith(".jpeg"))) {
        contentType = "image/jpeg";
      } else if (filename.endsWith(".png")) {
        contentType = "image/png";
      } else if (filename.endsWith(".gif")) {
        contentType = "image/gif";
      } else if (filename.endsWith(".svg")) {
        contentType = "image/svg+xml";
      } else {
        contentType = "text/plain";
      }
    }
    return contentType;
  }

  public static boolean isInFilesDirectory(File rootPath, File file) throws IOException {
    return isInSubDirectory(new File(rootPath, PathParser.FILES).getCanonicalFile(),
      file.getCanonicalFile());
  }

  public static boolean isInFilesFitNesseDirectory(File rootPath, File file) throws IOException {
    return isInSubDirectory(new File(new File(rootPath, PathParser.FILES), "fitnesse").getCanonicalFile(),
      file.getCanonicalFile());
  }

  private static boolean isInSubDirectory(File dir, File file) {
    return file != null && (file.equals(dir) || isInSubDirectory(dir, file.getParentFile()));
  }

  @Override
  public SecureOperation getSecureOperation() {
    return (context, request) -> {
      try {
        return new File(context.getRootPagePath(), URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING)).isDirectory();
      } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException("Invalid URL encoding", e);
      }
    };
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy