Package org.eclipse.orion.internal.server.servlets.file

Source Code of org.eclipse.orion.internal.server.servlets.file.DirectoryHandlerV1

/*******************************************************************************
* Copyright (c) 2010, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.internal.server.servlets.file;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.orion.internal.server.servlets.ServletResourceHandler;
import org.eclipse.orion.internal.server.servlets.Slug;
import org.eclipse.orion.server.core.LogHelper;
import org.eclipse.orion.server.core.OrionConfiguration;
import org.eclipse.orion.server.core.ProtocolConstants;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.orion.server.servlets.OrionServlet;
import org.eclipse.osgi.util.NLS;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
* Handles HTTP requests against directories for eclipse web protocol version
* 1.0.
*/
public class DirectoryHandlerV1 extends ServletResourceHandler<IFileStore> {
  static final int CREATE_COPY = 0x1;
  static final int CREATE_MOVE = 0x2;
  static final int CREATE_NO_OVERWRITE = 0x4;

  private final ServletResourceHandler<IStatus> statusHandler;

  public DirectoryHandlerV1(ServletResourceHandler<IStatus> statusHandler) {
    this.statusHandler = statusHandler;
  }

  private boolean handleGet(HttpServletRequest request, HttpServletResponse response, IFileStore dir) throws IOException, CoreException {
    URI location = getURI(request);
    JSONObject result = ServletFileStoreHandler.toJSON(dir, dir.fetchInfo(EFS.NONE, null), location);
    String depthString = request.getParameter(ProtocolConstants.PARM_DEPTH);
    int depth = 0;
    if (depthString != null) {
      try {
        depth = Integer.parseInt(depthString);
      } catch (NumberFormatException e) {
        // ignore
      }
    }
    encodeChildren(dir, location, result, depth);
    OrionServlet.writeJSONResponse(request, response, result);
    return true;
  }

  private void encodeChildren(IFileStore dir, URI location, JSONObject result, int depth) throws CoreException {
    if (depth <= 0)
      return;
    JSONArray children = new JSONArray();
    //more efficient to ask for child information in bulk for certain file systems
    IFileInfo[] childInfos = dir.childInfos(EFS.NONE, null);
    for (IFileInfo childInfo : childInfos) {
      IFileStore childStore = dir.getChild(childInfo.getName());
      String name = childInfo.getName();
      if (childInfo.isDirectory())
        name += "/"; //$NON-NLS-1$
      URI childLocation = URIUtil.append(location, name);
      JSONObject childResult = ServletFileStoreHandler.toJSON(childStore, childInfo, childLocation);
      if (childInfo.isDirectory())
        encodeChildren(childStore, childLocation, childResult, depth - 1);
      children.put(childResult);
    }
    try {
      result.put(ProtocolConstants.KEY_CHILDREN, children);
    } catch (JSONException e) {
      // cannot happen
      throw new RuntimeException(e);
    }
  }

  private boolean handlePost(HttpServletRequest request, HttpServletResponse response, IFileStore dir) throws JSONException, CoreException, ServletException, IOException {
    //setup and precondition checks
    JSONObject requestObject = OrionServlet.readJSONRequest(request);
    String name = computeName(request, requestObject);
    if (name.length() == 0)
      return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "File name not specified.", null));
    IFileStore toCreate = dir.getChild(name);
    if (!name.equals(toCreate.getName()) || name.contains(":"))
      return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "Bad file name: " + name, null));
    int options = getCreateOptions(request);
    boolean destinationExists = toCreate.fetchInfo(EFS.NONE, null).exists();
    if (!validateOptions(request, response, toCreate, destinationExists, options))
      return true;
    //perform the operation
    if (performPost(request, response, requestObject, toCreate, options)) {
      //write the response
      URI location = URIUtil.append(getURI(request), name);
      JSONObject result = ServletFileStoreHandler.toJSON(toCreate, toCreate.fetchInfo(EFS.NONE, null), location);
      result.append("FileEncoding", System.getProperty("file.encoding"));
      OrionServlet.writeJSONResponse(request, response, result);
      response.setHeader(ProtocolConstants.HEADER_LOCATION, ServletResourceHandler.resovleOrionURI(request, location).toString());
      //response code should indicate if a new resource was actually created or not
      response.setStatus(destinationExists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED);
    }
    return true;
  }

  /**
   * Performs the actual modification corresponding to a POST request. All preconditions
   * are assumed to be satisfied.
   * @return <code>true</code> if the operation was successful, and <code>false</code> otherwise.
   */
  private boolean performPost(HttpServletRequest request, HttpServletResponse response, JSONObject requestObject, IFileStore toCreate, int options) throws CoreException, IOException, ServletException {
    boolean isCopy = (options & CREATE_COPY) != 0;
    boolean isMove = (options & CREATE_MOVE) != 0;
    try {
      if (isCopy || isMove)
        return performCopyMove(request, response, requestObject, toCreate, isCopy, options);
      if (requestObject.optBoolean(ProtocolConstants.KEY_DIRECTORY))
        toCreate.mkdir(EFS.NONE, null);
      else
        toCreate.openOutputStream(EFS.NONE, null).close();
    } catch (CoreException e) {
      IStatus status = e.getStatus();
      if (status != null && status.getCode() == EFS.ERROR_WRITE) {
        // Sanitize message, as it might contain the filepath.
        statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create: " + toCreate.getName(), null));
        return false;
      }
      throw e;
    }
    return true;
  }

  /**
   * Perform a copy or move as specified by the request.
   * @return <code>true</code> if the operation was successful, and <code>false</code> otherwise.
   */
  private boolean performCopyMove(HttpServletRequest request, HttpServletResponse response, JSONObject requestObject, IFileStore toCreate, boolean isCopy, int options) throws ServletException, CoreException {
    String locationString = requestObject.optString(ProtocolConstants.KEY_LOCATION, null);
    if (locationString == null) {
      statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "Copy or move request must specify source location", null));
      return false;
    }
    try {
      IFileStore source = resolveSourceLocation(request, locationString);
      if (source == null) {
        statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, NLS.bind("Source does not exist: ", locationString), null));
        return false;
      } else if (source.isParentOf(toCreate)) {
        statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "The destination cannot be a descendent of the source location", null));
        return false;
      }
      boolean allowOverwrite = (options & CREATE_NO_OVERWRITE) == 0;
      int efsOptions = allowOverwrite ? EFS.OVERWRITE : EFS.NONE;
      try {
        if (isCopy) {
          source.copy(toCreate, efsOptions, null);
        } else {
          source.move(toCreate, efsOptions, null);
          // Path format is /file/workspaceId/projectId/[location to folder]
          Path path = new Path(locationString);
          if (path.segmentCount() == 3 && path.segment(0).equals("file")) {
            // The folder is a project, remove the metadata
            OrionConfiguration.getMetaStore().deleteProject(path.segment(1), source.getName());
          }
        }
      } catch (CoreException e) {
        if (!source.fetchInfo(EFS.NONE, null).exists()) {
          statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_NOT_FOUND, NLS.bind("Source does not exist: ", locationString), e));
          return false;
        }
        if (e.getStatus().getCode() == EFS.ERROR_EXISTS) {
          statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_PRECONDITION_FAILED, "A file or folder with the same name already exists at this location", null));
          return false;
        }
        //just rethrow if we can't do something more specific
        throw e;
      }
    } catch (URISyntaxException e) {
      statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, NLS.bind("Bad source location in request: ", locationString), e));
      return false;
    }
    return true;
  }

  /**
   * Computes the name of the resource to be created by a POST operation. Returns
   * an empty string if the name was not specified.
   */
  private String computeName(HttpServletRequest request, JSONObject requestObject) {
    String name = Slug.decode(request.getHeader(ProtocolConstants.HEADER_SLUG));

    //next comes the source location for a copy/move
    if (name == null || name.length() == 0) {
      String location = requestObject.optString(ProtocolConstants.KEY_LOCATION);
      int lastSlash = location.lastIndexOf('/');
      if (lastSlash >= 0)
        name = location.substring(lastSlash + 1);
    }
    //finally use the name attribute from the request body
    if (name == null || name.length() == 0)
      name = requestObject.optString(ProtocolConstants.KEY_NAME);
    return name;
  }

  /**
   * Asserts that request options are valid. If options are not valid then this method handles the request response and return false. If the options
   * are valid this method return true.
   */
  private boolean validateOptions(HttpServletRequest request, HttpServletResponse response, IFileStore toCreate, boolean destinationExists, int options) throws ServletException {
    //operation cannot be both copy and move
    int copyMove = CREATE_COPY | CREATE_MOVE;
    if ((options & copyMove) == copyMove) {
      statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "Syntax error in request", null));
      return false;
    }
    //if overwrite is disallowed make sure destination does not exist yet
    boolean noOverwrite = (options & CREATE_NO_OVERWRITE) != 0;
    //for copy/move case, let the implementation check for overwrite because pre-validating is complicated
    if ((options & copyMove) == 0 && noOverwrite && destinationExists) {
      statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_PRECONDITION_FAILED, "A file or folder with the same name already exists at this location", null));
      return false;
    }
    return true;
  }

  /**
   * Returns a bit-mask of create options as specified by the request.
   */
  private int getCreateOptions(HttpServletRequest request) {
    int result = 0;
    String optionString = request.getHeader(ProtocolConstants.HEADER_CREATE_OPTIONS);
    if (optionString != null) {
      for (String option : optionString.split(",")) { //$NON-NLS-1$
        if (ProtocolConstants.OPTION_COPY.equalsIgnoreCase(option))
          result |= CREATE_COPY;
        else if (ProtocolConstants.OPTION_MOVE.equalsIgnoreCase(option))
          result |= CREATE_MOVE;
        else if (ProtocolConstants.OPTION_NO_OVERWRITE.equalsIgnoreCase(option))
          result |= CREATE_NO_OVERWRITE;
      }
    }
    return result;
  }

  private boolean handleDelete(HttpServletRequest request, HttpServletResponse response, IFileStore dir) throws JSONException, CoreException, ServletException, IOException {
    dir.delete(EFS.NONE, null);
    return true;
  }

  private boolean handlePut(HttpServletRequest request, HttpServletResponse response, IFileStore dir) throws JSONException, IOException, CoreException {
    IFileInfo info = ServletFileStoreHandler.fromJSON(request);
    dir.putInfo(info, EFS.NONE, null);
    return true;
  }

  @Override
  public boolean handleRequest(HttpServletRequest request, HttpServletResponse response, IFileStore dir) throws ServletException {
    try {
      switch (getMethod(request)) {
        case GET :
          return handleGet(request, response, dir);
        case PUT :
          return handlePut(request, response, dir);
        case POST :
          return handlePost(request, response, dir);
        case DELETE :
          return handleDelete(request, response, dir);
        default :
          return false;
      }
    } catch (JSONException e) {
      return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, "Syntax error in request", e));
    } catch (CoreException e) {
      //core exception messages are designed for end user consumption, so use message directly
      return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), e));
    } catch (Exception e) {
      if (handleAuthFailure(request, response, e))
        return true;
      //the exception message is probably not appropriate for end user consumption
      LogHelper.log(e);
      return statusHandler.handleRequest(request, response, new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An unknown failure occurred. Consult your server log or contact your system administrator.", e));
    }
  }
}
TOP

Related Classes of org.eclipse.orion.internal.server.servlets.file.DirectoryHandlerV1

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.