/* * ==================================================================== * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.server.dav.handlers; import java.util.Iterator; import java.util.LinkedList; import java.util.logging.Level; import javax.servlet.http.HttpServletResponse; import org.tmatesoft.svn.core.internal.server.dav.DAVException; import org.tmatesoft.svn.core.internal.server.dav.DAVIFHeader; import org.tmatesoft.svn.core.internal.server.dav.DAVIFState; import org.tmatesoft.svn.core.internal.server.dav.DAVIFStateType; import org.tmatesoft.svn.core.internal.server.dav.DAVLock; import org.tmatesoft.svn.core.internal.server.dav.DAVLockScope; import org.tmatesoft.svn.core.internal.server.dav.DAVPathUtil; import org.tmatesoft.svn.core.internal.server.dav.DAVResource; import org.tmatesoft.svn.core.internal.server.dav.DAVServlet; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.2.0 * @author TMate Software Ltd. */ public class DAVValidateWalker implements IDAVResourceWalkHandler { public DAVResponse handleResource(DAVResponse response, DAVResource resource, DAVLockInfoProvider lockInfoProvider, LinkedList ifHeaders, int flags, DAVLockScope lockScope, CallType callType) throws DAVException { DAVException exception = null; try { validateResourceState(ifHeaders, resource, lockInfoProvider, lockScope, flags); return response; } catch (DAVException e) { exception = e; } //TODO: I'm not sure what resources we should compare here if (DAVServlet.isHTTPServerError(exception.getResponseCode())) { throw exception; } DAVResponse resp = new DAVResponse(null, resource.getResourceURI().getRequestURI(), response, null, exception.getResponseCode()); return resp; } public void validateResourceState(LinkedList ifHeaders, DAVResource resource, DAVLockInfoProvider provider, DAVLockScope lockScope, int flags) throws DAVException { DAVLock lock = null; if (provider != null) { try { lock = provider.getLock(resource); } catch (DAVException dave) { throw new DAVException("The locks could not be queried for verification against a possible \"If:\" header.", null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, SVNLogType.NETWORK, Level.FINE, dave, null, null, 0, null); } } boolean seenLockToken = false; if (lockScope == DAVLockScope.EXCLUSIVE) { if (lock != null) { throw new DAVException("Existing lock(s) on the requested resource prevent an exclusive lock.", ServletDAVHandler.SC_HTTP_LOCKED, 0); } seenLockToken = true; } else if (lockScope == DAVLockScope.SHARED) { if (lock.getScope() == DAVLockScope.EXCLUSIVE) { throw new DAVException("The requested resource is already locked exclusively.", ServletDAVHandler.SC_HTTP_LOCKED, 0); } seenLockToken = true; } else { seenLockToken = lock == null; } if (ifHeaders == null || ifHeaders.isEmpty()) { if (seenLockToken) { return; } throw new DAVException("This resource is locked and an \"If:\" header was not supplied to allow access to the resource.", ServletDAVHandler.SC_HTTP_LOCKED, 0); } DAVIFHeader ifHeader = (DAVIFHeader) ifHeaders.getFirst(); if (lock == null && ifHeader.isDummyHeader()) { if ((flags & ServletDAVHandler.DAV_VALIDATE_IS_PARENT) != 0) { return; } throw new DAVException("The locktoken specified in the \"Lock-Token:\" header is invalid because this resource has no outstanding locks.", HttpServletResponse.SC_BAD_REQUEST, 0); } String eTag = resource.getETag(); String uri = DAVPathUtil.dropTraillingSlash(resource.getResourceURI().getRequestURI()); int numThatAppy = 0; String reason = null; Iterator ifHeadersIter = ifHeaders.iterator(); for (;ifHeadersIter.hasNext();) { ifHeader = (DAVIFHeader) ifHeadersIter.next(); if (ifHeader.getURI() != null && !uri.equals(ifHeader.getURI())) { continue; } ++numThatAppy; LinkedList stateList = ifHeader.getStateList(); boolean doContinue = false; for (Iterator stateListIter = stateList.iterator(); stateListIter.hasNext();) { DAVIFState state = (DAVIFState) stateListIter.next(); if (state.getType() == DAVIFStateType.IF_ETAG) { String currentETag = null; String givenETag = null; String stateETag = state.getETag(); if (stateETag.startsWith("W/")) { givenETag = stateETag.substring(2); } else { givenETag = stateETag; } if (eTag.startsWith("W/")) { currentETag = eTag.substring(2); } else { currentETag = eTag; } boolean eTagsDoNotMatch = !givenETag.equals(currentETag); if (state.getCondition() == DAVIFState.IF_CONDITION_NORMAL && eTagsDoNotMatch) { reason = "an entity-tag was specified, but the resource's actual ETag does not match."; doContinue = true; break; } else if (state.getCondition() == DAVIFState.IF_CONDITION_NOT && !eTagsDoNotMatch) { reason = "an entity-tag was specified using the \"Not\" form, but the resource's actual ETag matches the provided entity-tag."; doContinue = true; break; } } else if (state.getType() == DAVIFStateType.IF_OPAQUE_LOCK) { if (provider == null) { if (state.getCondition() == DAVIFState.IF_CONDITION_NOT) { continue; } reason = "a State-token was supplied, but a lock database is not available for to provide the required lock."; doContinue = true; break; } boolean matched = false; if (lock != null) { if (!lock.getLockToken().equals(state.getLockToken())) { continue; } seenLockToken = true; if (state.getCondition() == DAVIFState.IF_CONDITION_NOT) { reason = "a State-token was supplied, which used a \"Not\" condition. The State-token was found in the locks on this resource"; doContinue = true; break; } String lockAuthUser = lock.getAuthUser(); String requestUser = resource.getUserName(); if (lockAuthUser != null && (requestUser == null || !lockAuthUser.equals(requestUser))) { throw new DAVException("User \"{0}\" submitted a locktoken created by user \"{1}\".", new Object[] { requestUser, lockAuthUser }, HttpServletResponse.SC_FORBIDDEN, 0); } matched = true; } if (!matched && state.getCondition() == DAVIFState.IF_CONDITION_NORMAL) { reason = "a State-token was supplied, but it was not found in the locks on this resource."; doContinue = true; break; } } else if (state.getType() == DAVIFStateType.IF_UNKNOWN) { if (state.getCondition() == DAVIFState.IF_CONDITION_NORMAL) { reason = "an unknown state token was supplied"; doContinue = true; break; } } } if (doContinue) { continue; } if (seenLockToken) { return; } break; } if (!ifHeadersIter.hasNext()) { if (numThatAppy == 0) { if (seenLockToken) { return; } if (findSubmittedLockToken(ifHeaders, lock)) { return; } throw new DAVException("This resource is locked and the \"If:\" header did not specify one of the locktokens for this resource's lock(s).", ServletDAVHandler.SC_HTTP_LOCKED, 0); } ifHeader = (DAVIFHeader) ifHeaders.getFirst(); if (ifHeader.isDummyHeader()) { throw new DAVException("The locktoken specified in the \"Lock-Token:\" header did not specify one of this resource's locktoken(s).", HttpServletResponse.SC_BAD_REQUEST, 0); } if (reason == null) { throw new DAVException("The preconditions specified by the \"If:\" header did not match this resource.", HttpServletResponse.SC_PRECONDITION_FAILED, 0); } throw new DAVException("The precondition(s) specified by the \"If:\" header did not match this resource. At least one failure is because: {0}", new Object[] { reason }, HttpServletResponse.SC_PRECONDITION_FAILED, 0); } if (findSubmittedLockToken(ifHeaders, lock)) { return; } if (ifHeader.isDummyHeader()) { throw new DAVException("The locktoken specified in the \"Lock-Token:\" header did not specify one of this resource's locktoken(s).", HttpServletResponse.SC_BAD_REQUEST, 0); } throw new DAVException("This resource is locked and the \"If:\" header did not specify one of the locktokens for this resource's lock(s).", ServletDAVHandler.SC_HTTP_LOCKED, 1); } private boolean findSubmittedLockToken(LinkedList ifHeaders, DAVLock lock) { for (Iterator ifHeadersIter = ifHeaders.iterator(); ifHeadersIter.hasNext();) { DAVIFHeader ifHeader = (DAVIFHeader) ifHeadersIter.next(); LinkedList ifStates = ifHeader.getStateList(); for (Iterator ifStatesIter = ifStates.iterator(); ifStatesIter.hasNext();) { DAVIFState ifState = (DAVIFState) ifStatesIter.next(); if (ifState.getType() == DAVIFStateType.IF_OPAQUE_LOCK) { String lockToken = lock.getLockToken(); String stateLockToken = ifState.getLockToken(); if (lockToken.equals(stateLockToken)) { return true; } } } } return false; } }