/**
   * A DIME request is really a SOAP request that we are dealing with, and so its authentication is
   * the SOAP authentication approach. Since Axis2 does not handle DIME messages we deal with them
   * here.
   *
   * @param request
   * @param response
   */
  private void processDimeRequest(HttpServletRequest request, HttpServletResponse response) {
    S3PutObjectRequest putRequest = null;
    S3PutObjectResponse putResponse = null;
    int bytesRead = 0;

    S3Engine engine = new S3Engine();

    try {
      logRequest(request);

      MultiPartDimeInputStream ds = new MultiPartDimeInputStream(request.getInputStream());

      // -> the first stream MUST be the SOAP party
      if (ds.nextInputStream()) {
        // logger.debug( "DIME msg [" + ds.getStreamType() + "," + ds.getStreamTypeFormat() + "," +
        // ds.getStreamId() + "]" );
        byte[] buffer = new byte[8192];
        bytesRead = ds.read(buffer, 0, 8192);
        // logger.debug( "DIME SOAP Bytes read: " + bytesRead );
        ByteArrayInputStream bis = new ByteArrayInputStream(buffer, 0, bytesRead);
        putRequest = toEnginePutObjectRequest(bis);
      }

      // -> we only need to support a DIME message with two bodyparts
      if (null != putRequest && ds.nextInputStream()) {
        InputStream is = ds.getInputStream();
        putRequest.setData(is);
      }

      // -> need to do SOAP level auth here, on failure return the SOAP fault
      StringBuffer xml = new StringBuffer();
      String AWSAccessKey = putRequest.getAccessKey();
      UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey);
      try {
        S3SoapAuth.verifySignature(
            putRequest.getSignature(),
            "PutObject",
            putRequest.getRawTimestamp(),
            AWSAccessKey,
            info.getSecretKey());

      } catch (AxisFault e) {
        String reason = e.toString();
        int start = reason.indexOf(".AxisFault:");
        if (-1 != start) reason = reason.substring(start + 11);

        xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append(
            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" >\n");
        xml.append("<soap:Body>\n");
        xml.append("<soap:Fault>\n");
        xml.append("<faultcode>").append(e.getFaultCode().toString()).append("</faultcode>\n");
        xml.append("<faultstring>").append(reason).append("</faultstring>\n");
        xml.append("</soap:Fault>\n");
        xml.append("</soap:Body></soap:Envelope>");

        endResponse(response, xml.toString());
        return;
      }

      // -> PutObject S3 Bucket Policy would be done in the engine.handleRequest() call
      UserContext.current()
          .initContext(AWSAccessKey, info.getSecretKey(), AWSAccessKey, "S3 DIME request", request);
      putResponse = engine.handleRequest(putRequest);

      xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
      xml.append(
          "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:tns=\"http://s3.amazonaws.com/doc/2006-03-01/\">");
      xml.append("<soap:Body>");
      xml.append("<tns:PutObjectResponse>");
      xml.append("<tns:PutObjectResponse>");
      xml.append("<tns:ETag>\"").append(putResponse.getETag()).append("\"</tns:ETag>");
      xml.append("<tns:LastModified>")
          .append(DatatypeConverter.printDateTime(putResponse.getLastModified()))
          .append("</tns:LastModified>");
      xml.append("</tns:PutObjectResponse></tns:PutObjectResponse>");
      xml.append("</soap:Body></soap:Envelope>");

      endResponse(response, xml.toString());
    } catch (PermissionDeniedException e) {
      logger.error("Unexpected exception " + e.getMessage(), e);
      response.setStatus(403);
      endResponse(response, "Access denied");
    } catch (Throwable e) {
      logger.error("Unexpected exception " + e.getMessage(), e);
    } finally {
    }
  }
  private ServletAction routeRequest(HttpServletRequest request) {
    //  URL routing for S3 REST calls.
    String pathInfo = request.getPathInfo();
    String bucketName = null;
    String key = null;

    String serviceEndpoint = ServiceProvider.getInstance().getServiceEndpoint();
    String host = request.getHeader("Host");

    // Check for unrecognized forms of URI information in request

    if ((pathInfo == null) || (pathInfo.indexOf('/') != 0))
      if ("POST".equalsIgnoreCase(request.getMethod()))
      // Case where request is POST operation with no pathinfo
      // This is the POST alternative to PUT described at s3.amazonaws.com API doc page 141
      {
        return routePlainPostRequest(request);
      }

    // Irrespective of whether the requester is using subdomain or full host naming of path
    // expressions
    // to buckets, wherever the request is made up of a service endpoint followed by a /, in AWS S3
    // this always
    // conveys a ListAllMyBuckets command

    if ((serviceEndpoint.equalsIgnoreCase(host)) && (pathInfo.equalsIgnoreCase("/"))) {
      request.setAttribute(S3Constants.BUCKET_ATTR_KEY, "/");
      return new S3BucketAction(); // for ListAllMyBuckets
    }

    // Because there is a leading / at position 0 of pathInfo, now subtract this to process the
    // remainder
    pathInfo = pathInfo.substring(1);

    if (ServiceProvider.getInstance().getUseSubDomain()) {

      // -> verify the format of the bucket name
      int endPos = host.indexOf(ServiceProvider.getInstance().getMasterDomain());
      if (endPos > 0) {
        bucketName = host.substring(0, endPos);
        S3Engine.verifyBucketName(bucketName, false);
        request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName);
      } else request.setAttribute(S3Constants.BUCKET_ATTR_KEY, "");

      if (pathInfo == null || pathInfo.equalsIgnoreCase("/")) {
        return new S3BucketAction();
      } else {
        String objectKey = pathInfo.substring(1);
        request.setAttribute(S3Constants.OBJECT_ATTR_KEY, objectKey);
        return new S3ObjectAction();
      }
    } else {

      int endPos = pathInfo.indexOf('/'); // Subsequent / character?

      if (endPos < 1) {
        bucketName = pathInfo;
        S3Engine.verifyBucketName(bucketName, false);
        request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName);
        return new S3BucketAction();
      } else {
        bucketName = pathInfo.substring(0, endPos);
        key = pathInfo.substring(endPos + 1);
        S3Engine.verifyBucketName(bucketName, false);

        if (!key.isEmpty()) {
          request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName);
          request.setAttribute(S3Constants.OBJECT_ATTR_KEY, pathInfo.substring(endPos + 1));
          return new S3ObjectAction();
        } else {
          request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName);
          return new S3BucketAction();
        }
      }
    }
  }