/* * eXist Open Source Native XML Database * Copyright (C) 2001-06 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.http.webdav.methods; import org.exist.EXistException; import org.exist.collections.Collection; import org.exist.dom.DocumentImpl; import org.exist.security.PermissionDeniedException; import org.exist.security.User; import org.exist.storage.BrokerPool; import org.exist.storage.DBBroker; import org.exist.storage.lock.Lock; import org.exist.storage.txn.TransactionException; import org.exist.storage.txn.TransactionManager; import org.exist.storage.txn.Txn; import org.exist.util.LockException; import org.exist.xmldb.XmldbURI; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; /** * Implements the WebDAV move method. * @author wolf */ public class Move extends AbstractWebDAVMethod { public Move(BrokerPool pool) { super(pool); } /* (non-Javadoc) * @see org.exist.http.webdav.WebDAVMethod#process(org.exist.security.User, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.exist.collections.Collection, org.exist.dom.DocumentImpl) */ public void process(User user, HttpServletRequest request, HttpServletResponse response, XmldbURI path) throws ServletException, IOException { DBBroker broker = null; Collection collection = null; DocumentImpl resource = null; try { broker = pool.get(user); collection = broker.openCollection(path, Lock.WRITE_LOCK); if(collection == null) { XmldbURI docUri = path.lastSegment(); XmldbURI collUri = path.removeLastSegment(); collection = broker.openCollection(collUri, Lock.WRITE_LOCK); if(collection == null) { LOG.debug("No resource or collection found for path: " + path); response.sendError(HttpServletResponse.SC_NOT_FOUND, NOT_FOUND_ERR); return; } resource = collection.getDocumentWithLock(broker, docUri, Lock.WRITE_LOCK); if(resource == null) { LOG.debug("No resource found for path: " + path); response.sendError(HttpServletResponse.SC_NOT_FOUND, NOT_FOUND_ERR); return; } } //TODO : release collection lock here ? String destination = request.getHeader("Destination"); XmldbURI destPath = null; try { URI uri = new URI(destination); String host = uri.getHost(); int port = uri.getPort(); if(!(host.equals(request.getServerName()) && port == request.getServerPort())) { response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "Copying to a different server is not yet implemented"); return; } //TODO: use XmldbURI for this stuff too String tempDestPath = uri.getPath(); if(tempDestPath.startsWith(request.getContextPath())) tempDestPath = tempDestPath.substring(request.getContextPath().length()); if(tempDestPath.startsWith(request.getServletPath())) tempDestPath = tempDestPath.substring(request.getServletPath().length()); destPath = XmldbURI.create(tempDestPath); } catch (URISyntaxException e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Malformed URL in destination header"); } if(resource != null) moveResource(user, broker, request, response, resource, destPath); else moveCollection(user, broker, request, response, collection, destPath); } catch (EXistException e) { throw new ServletException("Failed to copy: " + e.getMessage(), e); } catch (LockException e) { throw new ServletException("Failed to copy: " + e.getMessage(), e); } finally { if(collection != null) collection.release(Lock.WRITE_LOCK); if(resource != null) resource.getUpdateLock().release(Lock.WRITE_LOCK); pool.release(broker); } } private void moveCollection(User user, DBBroker broker, HttpServletRequest request, HttpServletResponse response, Collection collection, XmldbURI destination) throws ServletException, IOException { if(collection.getURI().equals(destination)) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Source and destination are the same"); return; } if(destination.lastSegment()==null) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad destination: " + destination); return; } Collection destCollection = null; TransactionManager transact = pool.getTransactionManager(); try { Txn txn = transact.beginTransaction(); try { boolean replaced = false; destCollection = broker.openCollection(destination, Lock.WRITE_LOCK); if(destCollection != null) { boolean overwrite = overwrite(request); if(!overwrite) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, "Destination collection exists and overwrite is not allowed"); return; } broker.removeCollection(txn, destCollection); replaced = true; } XmldbURI parentURI = destination.removeLastSegment(); XmldbURI newCollURI = destination.lastSegment(); LOG.debug("parent = " + parentURI + "; new name = " + newCollURI); destCollection = broker.openCollection(parentURI, Lock.WRITE_LOCK); if(destCollection == null) { response.sendError(HttpServletResponse.SC_CONFLICT, "No parent collection: " + parentURI); return; } broker.moveCollection(txn, collection, destCollection, newCollURI); if(replaced) response.setStatus(HttpServletResponse.SC_NO_CONTENT); else response.setStatus(HttpServletResponse.SC_CREATED); } catch (PermissionDeniedException e) { response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); } catch (LockException e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } finally { if(destCollection != null) destCollection.release(Lock.WRITE_LOCK); transact.commit(txn); } } catch (TransactionException e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } private void moveResource(User user, DBBroker broker, HttpServletRequest request, HttpServletResponse response, DocumentImpl resource, XmldbURI destination) throws ServletException, IOException { XmldbURI docUri = destination.lastSegment(); XmldbURI collUri = destination.removeLastSegment(); if(docUri == null) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad destination: " + destination); return; } boolean replaced = false; Collection destCollection = null; TransactionManager transact = broker.getBrokerPool().getTransactionManager(); Txn transaction = transact.beginTransaction(); try { destCollection = broker.openCollection(collUri, Lock.WRITE_LOCK); if(destCollection == null) { transact.abort(transaction); response.sendError(HttpServletResponse.SC_CONFLICT, "Destination collection not found"); return; } DocumentImpl oldDoc = destCollection.getDocument(broker, docUri); //TODO : release collection lock here ? if(oldDoc != null) { boolean overwrite = overwrite(request); if(!overwrite) { transact.abort(transaction); response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED, "Destination resource exists and overwrite is not allowed"); return; } replaced = true; } broker.moveResource(transaction, resource, destCollection, docUri); // [1509776] After a webDAV MOVE all locks need to be removed. resource.setUserLock(null); resource.getMetadata().setLockToken(null); transact.commit(transaction); if(replaced) response.setStatus(HttpServletResponse.SC_NO_CONTENT); else response.setStatus(HttpServletResponse.SC_CREATED); } catch (PermissionDeniedException e) { transact.abort(transaction); response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); } catch (LockException e) { transact.abort(transaction); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } catch (TransactionException e) { transact.abort(transaction); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } finally { if(destCollection != null) destCollection.release(Lock.WRITE_LOCK); } } private boolean overwrite(HttpServletRequest request) { String header = request.getHeader("Overwrite"); if(header == null) return false; return header.equals("T"); } }