content: /*
   * Copyright 1999,2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package net.sf.webdav.methods;
  
  import java.io.IOException;
  import java.util.Hashtable;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  import net.sf.webdav.ITransaction;
  import net.sf.webdav.IWebdavStore;
  import net.sf.webdav.StoredObject;
  import net.sf.webdav.WebdavStatus;
  import net.sf.webdav.exceptions.AccessDeniedException;
  import net.sf.webdav.exceptions.LockFailedException;
  import net.sf.webdav.exceptions.ObjectAlreadyExistsException;
  import net.sf.webdav.exceptions.ObjectNotFoundException;
  import net.sf.webdav.exceptions.WebdavException;
  import net.sf.webdav.fromcatalina.RequestUtil;
  import net.sf.webdav.locking.ResourceLocks;
  
  public class DoCopy extends AbstractMethod {
  
      private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
              .getLogger(DoCopy.class);
  
      private IWebdavStore _store;
      private ResourceLocks _resourceLocks;
      private DoDelete _doDelete;
      private boolean _readOnly;
  
      public DoCopy(IWebdavStore store, ResourceLocks resourceLocks,
              DoDelete doDelete, boolean readOnly) {
          _store = store;
          _resourceLocks = resourceLocks;
          _doDelete = doDelete;
          _readOnly = readOnly;
      }
  
      public void execute(ITransaction transaction, HttpServletRequest req,
              HttpServletResponse resp) throws IOException, LockFailedException {
          LOG.trace("-- " + this.getClass().getName());
  
          String path = getRelativePath(req);
          if (!_readOnly) {
  
              String tempLockOwner = "doCopy" + System.currentTimeMillis()
                      + req.toString();
              if (_resourceLocks.lock(transaction, path, tempLockOwner, false, 0,
                      TEMP_TIMEOUT, TEMPORARY)) {
                  try {
                      if (!copyResource(transaction, req, resp))
                          return;
                  } catch (AccessDeniedException e) {
                      resp.sendError(WebdavStatus.SC_FORBIDDEN);
                  } catch (ObjectAlreadyExistsException e) {
                      resp.sendError(WebdavStatus.SC_CONFLICT, req
                              .getRequestURI());
                  } catch (ObjectNotFoundException e) {
                      resp.sendError(WebdavStatus.SC_NOT_FOUND, req
                              .getRequestURI());
                  } catch (WebdavException e) {
                      resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
                  } finally {
                      _resourceLocks.unlockTemporaryLockedObjects(transaction,
                              path, tempLockOwner);
                  }
              } else {
                  resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
              }
  
          } else {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
          }
  
      }
  
      /**
       * Copy a resource.
       * 
       * @param transaction
       *      indicates that the method is within the scope of a WebDAV
       *      transaction
       * @param req
       *      Servlet request
       * @param resp
       *      Servlet response
       * @return true if the copy is successful
       * @throws WebdavException
       *      if an error in the underlying store occurs
       * @throws IOException
       *      when an error occurs while sending the response
       * @throws LockFailedException
       */
      public boolean copyResource(ITransaction transaction,
              HttpServletRequest req, HttpServletResponse resp)
              throws WebdavException, IOException, LockFailedException {
  
          // Parsing destination header
          String destinationPath = parseDestinationHeader(req, resp);
  
          if (destinationPath == null)
              return false;
  
          String path = getRelativePath(req);
  
          if (path.equals(destinationPath)) {
              resp.sendError(WebdavStatus.SC_FORBIDDEN);
              return false;
          }
  
          Hashtable<String, Integer> errorList = new Hashtable<String, Integer>();
          String parentDestinationPath = getParentPath(getCleanPath(destinationPath));
  
          if (!checkLocks(transaction, req, resp, _resourceLocks,
                  parentDestinationPath)) {
              resp.setStatus(WebdavStatus.SC_LOCKED);
              return false; // parentDestination is locked
          }
  
          if (!checkLocks(transaction, req, resp, _resourceLocks, destinationPath)) {
              resp.setStatus(WebdavStatus.SC_LOCKED);
              return false; // destination is locked
          }
  
          // Parsing overwrite header
  
          boolean overwrite = true;
          String overwriteHeader = req.getHeader("Overwrite");
  
          if (overwriteHeader != null) {
              overwrite = overwriteHeader.equalsIgnoreCase("T");
          }
  
          // Overwriting the destination
          String lockOwner = "copyResource" + System.currentTimeMillis()
                  + req.toString();
  
          if (_resourceLocks.lock(transaction, destinationPath, lockOwner, false,
                  0, TEMP_TIMEOUT, TEMPORARY)) {
              StoredObject copySo, destinationSo = null;
              try {
                  copySo = _store.getStoredObject(transaction, path);
                  // Retrieve the resources
                  if (copySo == null) {
                      resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                      return false;
                  }
  
                  if (copySo.isNullResource()) {
                      String methodsAllowed = DeterminableMethod
                              .determineMethodsAllowed(copySo);
                      resp.addHeader("Allow", methodsAllowed);
                      resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
                      return false;
                  }
  
                  errorList = new Hashtable<String, Integer>();
  
                  destinationSo = _store.getStoredObject(transaction,
                          destinationPath);
  
                  if (overwrite) {
  
                      // Delete destination resource, if it exists
                      if (destinationSo != null) {
                          _doDelete.deleteResource(transaction, destinationPath,
                                  errorList, req, resp);
  
                      } else {
                          resp.setStatus(WebdavStatus.SC_CREATED);
                      }
                  } else {
  
                      // If the destination exists, then it's a conflict
                      if (destinationSo != null) {
                          resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                          return false;
                      } else {
                          resp.setStatus(WebdavStatus.SC_CREATED);
                      }
  
                  }
                  copy(transaction, path, destinationPath, errorList, req, resp);
  
                  if (!errorList.isEmpty()) {
                      sendReport(req, resp, errorList);
                  }
  
              } finally {
                  _resourceLocks.unlockTemporaryLockedObjects(transaction,
                          destinationPath, lockOwner);
              }
          } else {
              resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
              return false;
          }
          return true;
  
      }
  
      /**
       * copies the specified resource(s) to the specified destination.
       * preconditions must be handled by the caller. Standard status codes must
       * be handled by the caller. a multi status report in case of errors is
       * created here.
       * 
       * @param transaction
       *      indicates that the method is within the scope of a WebDAV
       *      transaction
       * @param sourcePath
       *      path from where to read
       * @param destinationPath
       *      path where to write
       * @param req
       *      HttpServletRequest
       * @param resp
       *      HttpServletResponse
       * @throws WebdavException
       *      if an error in the underlying store occurs
       * @throws IOException
       */
      private void copy(ITransaction transaction, String sourcePath,
              String destinationPath, Hashtable<String, Integer> errorList,
              HttpServletRequest req, HttpServletResponse resp)
              throws WebdavException, IOException {
  
          StoredObject sourceSo = _store.getStoredObject(transaction, sourcePath);
          if (sourceSo.isResource()) {
              _store.createResource(transaction, destinationPath);
              long resourceLength = _store.setResourceContent(transaction,
                      destinationPath, _store.getResourceContent(transaction,
                              sourcePath), null, null);
  
              if (resourceLength != -1) {
                  StoredObject destinationSo = _store.getStoredObject(
                          transaction, destinationPath);
                  destinationSo.setResourceLength(resourceLength);
              }
  
          } else {
  
              if (sourceSo.isFolder()) {
                  copyFolder(transaction, sourcePath, destinationPath, errorList,
                          req, resp);
              } else {
                  resp.sendError(WebdavStatus.SC_NOT_FOUND);
              }
          }
      }
  
      /**
       * helper method of copy() recursively copies the FOLDER at source path to
       * destination path
       * 
       * @param transaction
       *      indicates that the method is within the scope of a WebDAV
       *      transaction
       * @param sourcePath
       *      where to read
       * @param destinationPath
       *      where to write
       * @param errorList
       *      all errors that ocurred
       * @param req
       *      HttpServletRequest
       * @param resp
       *      HttpServletResponse
       * @throws WebdavException
       *      if an error in the underlying store occurs
       */
      private void copyFolder(ITransaction transaction, String sourcePath,
              String destinationPath, Hashtable<String, Integer> errorList,
              HttpServletRequest req, HttpServletResponse resp)
              throws WebdavException {
  
          _store.createFolder(transaction, destinationPath);
          boolean infiniteDepth = true;
          String depth = req.getHeader("Depth");
          if (depth != null) {
              if (depth.equals("0")) {
                  infiniteDepth = false;
              }
          }
          if (infiniteDepth) {
              String[] children = _store
                      .getChildrenNames(transaction, sourcePath);
              children = children == null ? new String[] {} : children;
  
              StoredObject childSo;
              for (int i = children.length - 1; i >= 0; i--) {
                  children[i] = "/" + children[i];
                  try {
                      childSo = _store.getStoredObject(transaction,
                              (sourcePath + children[i]));
                      if (childSo.isResource()) {
                          _store.createResource(transaction, destinationPath
                                  + children[i]);
                          long resourceLength = _store.setResourceContent(
                                  transaction, destinationPath + children[i],
                                  _store.getResourceContent(transaction,
                                          sourcePath + children[i]), null, null);
  
                          if (resourceLength != -1) {
                              StoredObject destinationSo = _store
                                      .getStoredObject(transaction,
                                              destinationPath + children[i]);
                              destinationSo.setResourceLength(resourceLength);
                          }
  
                      } else {
                          copyFolder(transaction, sourcePath + children[i],
                                  destinationPath + children[i], errorList, req,
                                  resp);
                      }
                  } catch (AccessDeniedException e) {
                      errorList.put(destinationPath + children[i], new Integer(
                              WebdavStatus.SC_FORBIDDEN));
                  } catch (ObjectNotFoundException e) {
                      errorList.put(destinationPath + children[i], new Integer(
                              WebdavStatus.SC_NOT_FOUND));
                  } catch (ObjectAlreadyExistsException e) {
                      errorList.put(destinationPath + children[i], new Integer(
                              WebdavStatus.SC_CONFLICT));
                  } catch (WebdavException e) {
                      errorList.put(destinationPath + children[i], new Integer(
                              WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                  }
              }
          }
      }
  
  
  }
  