Merged DEV_TEMPORARY to HEAD

18288: ENH-678: alfresco webdav does not pass litmus webdav test suite

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18320 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2010-01-27 12:56:18 +00:00
parent 3dfdf48af4
commit e2f0615f86
14 changed files with 1742 additions and 711 deletions

View File

@@ -95,6 +95,10 @@ public abstract class AbstractMoveOrCopyMethod extends HierarchicalMethod
FileInfo destParentInfo = null; FileInfo destParentInfo = null;
try try
{ {
if (destPath.endsWith(WebDAVHelper.PathSeperator))
{
destPath = destPath.substring(0, destPath.length() - 1);
}
destParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, destPath, servletPath); destParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, destPath, servletPath);
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
@@ -103,7 +107,7 @@ public abstract class AbstractMoveOrCopyMethod extends HierarchicalMethod
{ {
logger.debug("Destination parent folder doesn't exist: " + destPath); logger.debug("Destination parent folder doesn't exist: " + destPath);
} }
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND); throw new WebDAVServerException(HttpServletResponse.SC_CONFLICT);
} }
// check for the existence of the destination node // check for the existence of the destination node
@@ -123,6 +127,8 @@ public abstract class AbstractMoveOrCopyMethod extends HierarchicalMethod
// delete the destination node if it is not the same as the source node // delete the destination node if it is not the same as the source node
if (!destInfo.getNodeRef().equals(sourceInfo.getNodeRef())) if (!destInfo.getNodeRef().equals(sourceInfo.getNodeRef()))
{ {
checkNode(destInfo);
// attempting to move or copy onto another node // attempting to move or copy onto another node
fileFolderService.delete(destInfo.getNodeRef()); fileFolderService.delete(destInfo.getNodeRef());
} }
@@ -138,7 +144,9 @@ public abstract class AbstractMoveOrCopyMethod extends HierarchicalMethod
NodeRef sourceNodeRef = sourceInfo.getNodeRef(); NodeRef sourceNodeRef = sourceInfo.getNodeRef();
NodeRef destParentNodeRef = destParentInfo.getNodeRef(); NodeRef destParentNodeRef = destParentInfo.getNodeRef();
String name = getDAVHelper().splitPath(destPath)[1]; String name = getDAVHelper().splitPath(destPath)[1];
moveOrCopy(fileFolderService, sourceNodeRef, destParentNodeRef, name); moveOrCopy(fileFolderService, sourceNodeRef, destParentNodeRef, name);
// Set the response status // Set the response status

View File

@@ -99,6 +99,9 @@ public class DeleteMethod extends WebDAVMethod
} }
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND); throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
} }
checkNode(fileInfo);
// delete it // delete it
fileFolderService.delete(fileInfo.getNodeRef()); fileFolderService.delete(fileInfo.getNodeRef());
} }

View File

@@ -293,7 +293,7 @@ public class GetMethod extends WebDAVMethod
* @param strETagHeader The header to parse * @param strETagHeader The header to parse
* @return A list of ETags * @return A list of ETags
*/ */
private ArrayList parseETags(String strETagHeader) private ArrayList<String> parseETags(String strETagHeader)
{ {
ArrayList<String> list = new ArrayList<String>(); ArrayList<String> list = new ArrayList<String>();

View File

@@ -0,0 +1,280 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.webdav;
import java.util.LinkedList;
/**
* Class to represent a WebDAV lock info
*
* @author Ivan Rybnikov
*
*/
public class LockInfo
{
// Exclusive lock token
private String token = null;
// Lock scope
private String scope = null;
// Lock depth
private String depth = null;
// If lock is shared
private boolean shared = false;
// Shared lock tokens
private LinkedList<String> sharedLockTokens = null;
// Shared lock token separator
private static final String SHARED_LOCK_TOKEN_SEPARATOR = ",";
/**
* Default constructor
*
*/
public LockInfo()
{
}
/**
* Constructor
*
* @param token Exclusive lock token
* @param scope Lock scope (shared/exclusive)
* @param depth Lock depth (0/infinity)
*/
public LockInfo(String token, String scope, String depth)
{
this.token = token;
this.scope = scope;
this.depth = depth;
}
/**
* Returns true if node has shared or exclusive locks
*
* @return boolean
*/
public boolean isLocked()
{
if (token != null || (sharedLockTokens != null && !sharedLockTokens.isEmpty()))
{
return true;
}
return false;
}
/**
* Setter for exclusive lock token
*
* @param token Lock token
*/
public void setToken(String token)
{
this.token = token;
}
/**
* Getter for exclusive lock token
* @return
*/
public String getToken()
{
return token;
}
/**
* Setter for lock scope.
*
* @param scope
*/
public void setScope(String scope)
{
this.scope = scope;
}
/**
* Returns lock scope
*
* @return lock scope
*/
public String getScope()
{
return scope == null ? WebDAV.XML_EXCLUSIVE : scope;
}
/**
* Setter for lock depth
*
* @param depth lock depth
*/
public void setDepth(String depth)
{
this.depth = depth;
}
/**
* Returns lock depth
*
* @return lock depth
*/
public String getDepth()
{
return depth;
}
/**
* Transforms shared lock tokens string to list.
*
* @param sharedLockTokens String contains all node's shared lock tokens
* divided with SHARED_LOCK_TOKEN_SEPARATOR value.
* @return List of shared lock tokens
*/
public static LinkedList<String> parseSharedLockTokens(String sharedLockTokens)
{
if (sharedLockTokens == null)
{
return null;
}
LinkedList<String> result = new LinkedList<String>();
String[] sl = sharedLockTokens.split(SHARED_LOCK_TOKEN_SEPARATOR);
for (int i = 0; i < sl.length; i++)
{
result.add(sl[i]);
}
return result;
}
/**
* Getter for sharedLockTokens list
*
* @return LinkedList<String>
*/
public LinkedList<String> getSharedLockTokens()
{
return sharedLockTokens;
}
/**
* Setter for sharedLockTokens list
*
* @param sharedLockTokens
*/
public void setSharedLockTokens(LinkedList<String> sharedLockTokens)
{
this.sharedLockTokens = sharedLockTokens;
}
/**
* Adds new shared lock token to sharedLockTokens list
*
* @param token new token
*/
public void addSharedLockToken(String token)
{
if (sharedLockTokens == null)
{
sharedLockTokens = new LinkedList<String>();
}
sharedLockTokens.add(token);
}
/**
* Transforms list of shared locks to string.
* Lock tokens separated with SHARED_LOCK_TOKEN_SEPARATOR value.
*
* @param lockTokens list of shared locks
* @return String
*/
public static String makeSharedLockTokensString(LinkedList<String> lockTokens)
{
StringBuilder str = new StringBuilder();
boolean first = true;
for (String token : lockTokens)
{
if (!first)
{
str.append(SHARED_LOCK_TOKEN_SEPARATOR);
}
else
{
first = false;
}
str.append(token);
}
return str.toString();
}
/**
* Setter for shared property
*
* @param shared
*/
public void setShared(boolean shared)
{
this.shared = shared;
}
/**
* Returns true is lock is shared
*
* @return boolean
*/
public boolean isShared()
{
return shared;
}
/**
* Return the lock info as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("[");
str.append("token=");
str.append(getToken());
str.append(",scope=");
str.append(getScope());
str.append(",depth=");
str.append(getDepth());
str.append(",shared locks=");
str.append(getSharedLockTokens());
str.append("]");
return str.toString();
}
}

View File

@@ -32,9 +32,9 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.WebDAVModel;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
@@ -43,6 +43,10 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
/** /**
@@ -54,8 +58,13 @@ public class LockMethod extends WebDAVMethod
{ {
public static final String EMPTY_NS = ""; public static final String EMPTY_NS = "";
private String m_strLockToken = null; private int m_timeoutDuration = WebDAV.TIMEOUT_INFINITY;
private int m_timeoutDuration = WebDAV.DEPTH_INFINITY;
private LockInfo lockInfo = new LockInfo();
private String m_scope = null;
private String lockToken= null;
/** /**
* Default constructor * Default constructor
@@ -65,23 +74,23 @@ public class LockMethod extends WebDAVMethod
} }
/** /**
* Check if the lock token is valid * Returns true if request has lock token in the If header
* *
* @return boolean * @return boolean
*/ */
protected final boolean hasLockToken() protected final boolean hasLockToken()
{ {
return m_strLockToken != null ? true : false; if (m_conditions != null)
}
/**
* Return the lock token of an existing lock
*
* @return String
*/
protected final String getLockToken()
{ {
return m_strLockToken; for (Condition condition : m_conditions)
{
if (!condition.getLockTokensMatch().isEmpty())
{
return true;
}
}
}
return false;
} }
/** /**
@@ -101,9 +110,20 @@ public class LockMethod extends WebDAVMethod
*/ */
protected void parseRequestHeaders() throws WebDAVServerException protected void parseRequestHeaders() throws WebDAVServerException
{ {
// Get the lock token, if any // Get the depth
m_strLockToken = parseIfHeader(); parseDepthHeader();
// According to the specification: "Values other than 0 or infinity MUST NOT be used with the Depth header on a LOCK method.".
// The specification does not specify the error code for this case - so we use HttpServletResponse.SC_INTERNAL_SERVER_ERROR.
if (m_depth != WebDAV.DEPTH_0 && m_depth != WebDAV.DEPTH_INFINITY)
{
throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// Parse Lock tokens and ETags, if any
parseIfHeader();
// Get the lock timeout value // Get the lock timeout value
@@ -147,7 +167,7 @@ public class LockMethod extends WebDAVMethod
// DEBUG // DEBUG
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Lock lockToken=" + getLockToken() + ", timeout=" + getLockTimeout()); logger.debug("Timeout=" + getLockTimeout() + ", depth=" + getDepth());
} }
/** /**
@@ -157,13 +177,50 @@ public class LockMethod extends WebDAVMethod
*/ */
protected void parseRequestBody() throws WebDAVServerException protected void parseRequestBody() throws WebDAVServerException
{ {
// NOTE: There is a body for lock requests which contain the if (m_request.getContentLength() == -1)
// type of lock to apply and the lock owner but we will {
// ignore these settings so don't bother reading the body return;
}
Document body = getRequestBodyAsDocument();
if (body != null)
{
Element rootElement = body.getDocumentElement();
NodeList childList = rootElement.getChildNodes();
for (int i = 0; i < childList.getLength(); i++)
{
Node currentNode = childList.item(i);
switch (currentNode.getNodeType())
{
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
if (currentNode.getNodeName().endsWith(WebDAV.XML_LOCK_SCOPE))
{
NodeList propertiesList = currentNode.getChildNodes();
for (int j = 0; j < propertiesList.getLength(); j++)
{
Node propertiesNode = propertiesList.item(j);
switch (propertiesNode.getNodeType())
{
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
m_scope = propertiesNode.getNodeName();
break;
}
}
}
break;
}
}
}
} }
/** /**
* Exceute the request * Execute the request
* *
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
@@ -225,21 +282,27 @@ public class LockMethod extends WebDAVMethod
" path: " + path + "\n" + " path: " + path + "\n" +
" node: " + lockNodeInfo); " node: " + lockNodeInfo);
} }
m_response.setStatus(HttpServletResponse.SC_CREATED);
} }
// Check if this is a new lock or a refresh
// Check if this is a new lock or a lock refresh
if (hasLockToken()) if (hasLockToken())
{ {
this.lockInfo = checkNode(lockNodeInfo);
// Refresh an existing lock // Refresh an existing lock
refreshLock(lockNodeInfo.getNodeRef(), userName); refreshLock(lockNodeInfo, userName);
} }
else else
{ {
this.lockInfo = checkNode(lockNodeInfo, true, WebDAV.XML_EXCLUSIVE.equals(m_scope));
// Create a new lock // Create a new lock
createLock(lockNodeInfo.getNodeRef(), userName); createLock(lockNodeInfo, userName);
} }
m_response.setHeader(WebDAV.HEADER_LOCK_TOKEN, "<" + WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), userName) + ">"); m_response.setHeader(WebDAV.HEADER_LOCK_TOKEN, "<" + WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), userName) + ">");
m_response.setHeader(WebDAV.HEADER_CONTENT_TYPE, WebDAV.XML_CONTENT_TYPE); m_response.setHeader(WebDAV.HEADER_CONTENT_TYPE, WebDAV.XML_CONTENT_TYPE);
@@ -254,25 +317,35 @@ public class LockMethod extends WebDAVMethod
* @param userName String * @param userName String
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
private final void createLock(NodeRef lockNode, String userName) throws WebDAVServerException private final void createLock(FileInfo lockNode, String userName) throws WebDAVServerException
{ {
LockService lockService = getLockService(); LockService lockService = getLockService();
// Check the lock status of the node // Create Lock token
LockStatus lockSts = lockService.getLockStatus(lockNode); lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);
// DEBUG if (WebDAV.XML_EXCLUSIVE.equals(m_scope))
if (logger.isDebugEnabled())
logger.debug("Create lock status=" + lockSts);
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
{ {
// Indicate that the resource is already locked // Lock the node
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED); lockService.lock(lockNode.getNodeRef(), LockType.WRITE_LOCK, getLockTimeout());
//this.lockInfo.setToken(lockToken);
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_OPAQUE_LOCK_TOKEN, lockToken);
}
else
{
this.lockInfo.addSharedLockToken(lockToken);
String sharedLockTokens = LockInfo.makeSharedLockTokensString(this.lockInfo.getSharedLockTokens());
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_SHARED_LOCK_TOKENS, sharedLockTokens);
} }
// Lock the node // Store lock depth
lockService.lock(lockNode, LockType.WRITE_LOCK, getLockTimeout()); getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_LOCK_DEPTH, WebDAV.getDepthName(m_depth));
// Store lock scope (shared/exclusive)
getNodeService().setProperty(lockNode.getNodeRef(), WebDAVModel.PROP_LOCK_SCOPE, m_scope);
} }
/** /**
@@ -282,25 +355,15 @@ public class LockMethod extends WebDAVMethod
* @param userName String * @param userName String
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
private final void refreshLock(NodeRef lockNode, String userName) throws WebDAVServerException private final void refreshLock(FileInfo lockNode, String userName) throws WebDAVServerException
{ {
LockService lockService = getLockService(); LockService lockService = getLockService();
// Check the lock status of the node if (WebDAV.XML_EXCLUSIVE.equals(m_scope))
LockStatus lockSts = lockService.getLockStatus(lockNode);
// DEBUG
if (logger.isDebugEnabled())
logger.debug("Refresh lock status=" + lockSts);
if (lockSts != LockStatus.LOCK_OWNER)
{ {
// Indicate that the resource is already locked
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
// Update the expiry for the lock // Update the expiry for the lock
lockService.lock(lockNode, LockType.WRITE_LOCK, getLockTimeout()); lockService.lock(lockNode.getNodeRef(), LockType.WRITE_LOCK, getLockTimeout());
}
} }
/** /**
@@ -356,16 +419,21 @@ public class LockMethod extends WebDAVMethod
xml.write(DocumentHelper.createElement(WebDAV.XML_WRITE)); xml.write(DocumentHelper.createElement(WebDAV.XML_WRITE));
xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_TYPE, WebDAV.XML_LOCK_TYPE); xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_TYPE, WebDAV.XML_LOCK_TYPE);
// NOTE: We only do exclusive lock tokens at the moment
xml.startElement(EMPTY_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_LOCK_SCOPE, nullAttr); xml.startElement(EMPTY_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_LOCK_SCOPE, nullAttr);
xml.write(DocumentHelper.createElement(WebDAV.XML_EXCLUSIVE)); if (lockToken != null)
{
// In case of lock creation take the scope from request header
xml.write(DocumentHelper.createElement(m_scope));
}
else
{
// In case of lock refreshing take the scope from previously stored lock
xml.write(DocumentHelper.createElement(this.lockInfo.getScope()));
}
xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_LOCK_SCOPE); xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_LOCK_SCOPE);
// NOTE: We only support one level of lock at the moment
xml.startElement(EMPTY_NS, WebDAV.XML_DEPTH, WebDAV.XML_DEPTH, nullAttr); xml.startElement(EMPTY_NS, WebDAV.XML_DEPTH, WebDAV.XML_DEPTH, nullAttr);
xml.write(WebDAV.ZERO); xml.write(WebDAV.getDepthName(m_depth));
xml.endElement(EMPTY_NS, WebDAV.XML_DEPTH, WebDAV.XML_DEPTH); xml.endElement(EMPTY_NS, WebDAV.XML_DEPTH, WebDAV.XML_DEPTH);
xml.startElement(EMPTY_NS, WebDAV.XML_OWNER, WebDAV.XML_OWNER, nullAttr); xml.startElement(EMPTY_NS, WebDAV.XML_OWNER, WebDAV.XML_OWNER, nullAttr);
@@ -389,9 +457,16 @@ public class LockMethod extends WebDAVMethod
xml.startElement(EMPTY_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_LOCK_TOKEN, nullAttr); xml.startElement(EMPTY_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_LOCK_TOKEN, nullAttr);
xml.startElement(EMPTY_NS, WebDAV.XML_HREF, WebDAV.XML_HREF, nullAttr); xml.startElement(EMPTY_NS, WebDAV.XML_HREF, WebDAV.XML_HREF, nullAttr);
if (lockToken != null)
xml.write(WebDAV.makeLockToken(lockNode, owner)); {
// Output created lock
xml.write(lockToken);
}
else
{
// Output refreshed lock
xml.write(this.lockInfo.getToken());
}
xml.endElement(EMPTY_NS, WebDAV.XML_HREF, WebDAV.XML_HREF); xml.endElement(EMPTY_NS, WebDAV.XML_HREF, WebDAV.XML_HREF);
xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_LOCK_TOKEN); xml.endElement(EMPTY_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_LOCK_TOKEN);
@@ -433,5 +508,4 @@ public class LockMethod extends WebDAVMethod
} }
} }

View File

@@ -31,7 +31,6 @@ import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.w3c.dom.Document;
/** /**
* Implements the WebDAV MKCOL method * Implements the WebDAV MKCOL method
@@ -66,16 +65,14 @@ public class MkcolMethod extends WebDAVMethod
{ {
// There should not be a body with the MKCOL request // There should not be a body with the MKCOL request
Document body = getRequestBodyAsDocument(); if (m_request.getContentLength() > 0)
if (body != null)
{ {
throw new WebDAVServerException(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); throw new WebDAVServerException(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
} }
} }
/** /**
* Exceute the request * Execute the request
* *
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
@@ -120,7 +117,7 @@ public class MkcolMethod extends WebDAVMethod
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
// parent path is missing // parent path is missing
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND); throw new WebDAVServerException(HttpServletResponse.SC_CONFLICT);
} }
} }
else else

View File

@@ -24,7 +24,13 @@
*/ */
package org.alfresco.repo.webdav; package org.alfresco.repo.webdav;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
/** /**
@@ -47,6 +53,27 @@ public class MoveMethod extends AbstractMoveOrCopyMethod
NodeRef destParentNodeRef, NodeRef destParentNodeRef,
String name) throws Exception String name) throws Exception
{ {
NodeRef rootNodeRef = getRootNodeRef();
String path = getPath();
List<String> pathElements = getDAVHelper().splitAllPaths(path);
FileInfo fileInfo = null;
try
{
// get the node to move
fileInfo = fileFolderService.resolveNamePath(rootNodeRef, pathElements);
}
catch (FileNotFoundException e)
{
if (logger.isDebugEnabled())
{
logger.debug("Node not found: " + getPath());
}
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
}
checkNode(fileInfo);
fileFolderService.move(sourceNodeRef, destParentNodeRef, name); fileFolderService.move(sourceNodeRef, destParentNodeRef, name);
} }
} }

View File

@@ -37,8 +37,6 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.SessionUser; import org.alfresco.repo.SessionUser;
import org.alfresco.repo.webdav.auth.AuthenticationFilter; import org.alfresco.repo.webdav.auth.AuthenticationFilter;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.model.FileNotFoundException;
@@ -68,8 +66,7 @@ public class PropFindMethod extends WebDAVMethod
protected static final int GET_NAMED_PROPS = 1; protected static final int GET_NAMED_PROPS = 1;
protected static final int FIND_PROPS = 2; protected static final int FIND_PROPS = 2;
// Find depth and request type // Find request type
private int m_depth = WebDAV.DEPTH_INFINITY;
protected int m_mode = GET_ALL_PROPS; protected int m_mode = GET_ALL_PROPS;
// Requested properties // Requested properties
@@ -86,16 +83,6 @@ public class PropFindMethod extends WebDAVMethod
m_namespaces = new HashMap<String, String>(); m_namespaces = new HashMap<String, String>();
} }
/**
* Return the property find depth
*
* @return int
*/
public final int getDepth()
{
return m_depth;
}
/** /**
* Return the find mode * Return the find mode
* *
@@ -115,22 +102,8 @@ public class PropFindMethod extends WebDAVMethod
{ {
// Store the Depth header as this is used by several WebDAV methods // Store the Depth header as this is used by several WebDAV methods
String strDepth = m_request.getHeader(WebDAV.HEADER_DEPTH); parseDepthHeader();
if (strDepth != null && strDepth.length() > 0)
{
if (strDepth.equals(WebDAV.ZERO))
{
m_depth = WebDAV.DEPTH_0;
}
else if (strDepth.equals(WebDAV.ONE))
{
m_depth = WebDAV.DEPTH_1;
}
else
{
m_depth = WebDAV.DEPTH_INFINITY;
}
}
} }
/** /**
@@ -195,7 +168,7 @@ public class PropFindMethod extends WebDAVMethod
} }
/** /**
* Exceute the main WebDAV request processing * Execute the main WebDAV request processing
* *
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
@@ -355,7 +328,7 @@ public class PropFindMethod extends WebDAVMethod
String strName = node.getLocalName(); String strName = node.getLocalName();
String strNamespaceUri = node.getNamespaceURI(); String strNamespaceUri = node.getNamespaceURI();
if (strNamespaceUri.equals(WebDAV.DEFAULT_NAMESPACE_URI)) if (WebDAV.DEFAULT_NAMESPACE_URI.equals(strNamespaceUri))
{ {
property = new WebDAVProperty(strName); property = new WebDAVProperty(strName);
} }
@@ -373,6 +346,10 @@ public class PropFindMethod extends WebDAVMethod
*/ */
private String getNamespaceName(String strNamespaceUri) private String getNamespaceName(String strNamespaceUri)
{ {
if (strNamespaceUri == null)
{
return null;
}
String strNamespaceName = m_namespaces.get(strNamespaceUri); String strNamespaceName = m_namespaces.get(strNamespaceUri);
if (strNamespaceName == null) if (strNamespaceName == null)
{ {
@@ -465,7 +442,7 @@ public class PropFindMethod extends WebDAVMethod
Object davValue = null; Object davValue = null;
if (propNamespaceUri.equals(WebDAV.DEFAULT_NAMESPACE_URI)) if (WebDAV.DEFAULT_NAMESPACE_URI.equals(propNamespaceUri))
{ {
// Check if the client is requesting lock information // Check if the client is requesting lock information
if (propName.equals(WebDAV.XML_LOCK_DISCOVERY)) // && metaData.isLocked()) if (propName.equals(WebDAV.XML_LOCK_DISCOVERY)) // && metaData.isLocked())
@@ -624,7 +601,28 @@ public class PropFindMethod extends WebDAVMethod
// TODO: Custom properties lookup // TODO: Custom properties lookup
// String qualifiedName = propNamespaceUri + WebDAV.NAMESPACE_SEPARATOR + propName; // String qualifiedName = propNamespaceUri + WebDAV.NAMESPACE_SEPARATOR + propName;
String value = (String) getNodeService().getProperty(nodeRef, property.createQName());
if (value == null)
{
propertiesNotFound.add(property); propertiesNotFound.add(property);
}
else
{
if (property.hasNamespaceName())
{
xml.startElement(property.getNamespaceName(), property.getName(), property.getNamespaceName() + WebDAV.NAMESPACE_SEPARATOR + property.getName(), nullAttr);
xml.write(value);
xml.endElement(property.getNamespaceName(), property.getName(), property.getNamespaceName() + WebDAV.NAMESPACE_SEPARATOR + property.getName());
}
else
{
xml.startElement("", property.getName(), property.getName(), nullAttr);
xml.write(value);
xml.endElement("", property.getName(), property.getName());
}
}
} }
} }
@@ -894,15 +892,13 @@ public class PropFindMethod extends WebDAVMethod
*/ */
protected void generateLockDiscoveryResponse(XMLWriter xml, NodeRef node, boolean isDir) throws Exception protected void generateLockDiscoveryResponse(XMLWriter xml, NodeRef node, boolean isDir) throws Exception
{ {
// Get the lock status for the node // Output the lock status response
LockService lockService = getLockService(); LockInfo lockInfo = getNodeLockInfo(node);
LockStatus lockSts = lockService.getLockStatus(node); if (lockInfo.isLocked())
{
// Output the lock status reponse generateLockDiscoveryXML(xml, node, lockInfo);
}
if (lockSts != LockStatus.NO_LOCK)
generateLockDiscoveryXML(xml, node);
} }
/** /**

View File

@@ -31,7 +31,6 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@@ -69,6 +68,8 @@ public class PropPatchMethod extends PropFindMethod
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND); throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
} }
checkNode(pathNodeInfo);
// Set the response content type // Set the response content type
m_response.setContentType(WebDAV.XML_CONTENT_TYPE); m_response.setContentType(WebDAV.XML_CONTENT_TYPE);
@@ -353,14 +354,29 @@ public class PropPatchMethod extends PropFindMethod
} }
/**
* Stores information about PROPPATCH action(set or remove) an according property.
*
* @author Ivan Rybnikov
*/
private class PropertyAction private class PropertyAction
{ {
protected static final int SET = 0; protected static final int SET = 0;
protected static final int REMOVE = 1; protected static final int REMOVE = 1;
// Property on which action should be performed
private WebDAVProperty property; private WebDAVProperty property;
// Action
private int action; private int action;
/**
* Constructor
*
* @param action
* @param property
*/
public PropertyAction(int action, WebDAVProperty property) public PropertyAction(int action, WebDAVProperty property)
{ {
this.action = action; this.action = action;

View File

@@ -76,9 +76,9 @@ public class PutMethod extends WebDAVMethod
m_expectHeaderPresent = true; m_expectHeaderPresent = true;
} }
// Get the lock token, if any // Parse Lock tokens and ETags, if any
m_strLockToken = parseIfHeader(); parseIfHeader();
} }
/** /**
@@ -112,6 +112,9 @@ public class PutMethod extends WebDAVMethod
{ {
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST); throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST);
} }
checkNode(contentNodeInfo);
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {

View File

@@ -24,12 +24,16 @@
*/ */
package org.alfresco.repo.webdav; package org.alfresco.repo.webdav;
import java.util.LinkedList;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.WebDAVModel;
import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeService;
/** /**
* Implements the WebDAV UNLOCK method * Implements the WebDAV UNLOCK method
@@ -104,7 +108,7 @@ public class UnlockMethod extends WebDAVMethod
} }
/** /**
* Exceute the request * Execute the request
* *
* @exception WebDAVServerException * @exception WebDAVServerException
*/ */
@@ -135,6 +139,7 @@ public class UnlockMethod extends WebDAVMethod
// Get the lock status for the node // Get the lock status for the node
LockService lockService = getDAVHelper().getLockService(); LockService lockService = getDAVHelper().getLockService();
NodeService nodeService = getNodeService();
// String nodeId = lockInfo[0]; // String nodeId = lockInfo[0];
// String userName = lockInfo[1]; // String userName = lockInfo[1];
@@ -143,6 +148,9 @@ public class UnlockMethod extends WebDAVMethod
{ {
// Unlock the node // Unlock the node
lockService.unlock(lockNodeInfo.getNodeRef()); lockService.unlock(lockNodeInfo.getNodeRef());
nodeService.removeProperty(lockNodeInfo.getNodeRef(), WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
nodeService.removeProperty(lockNodeInfo.getNodeRef(), WebDAVModel.PROP_LOCK_DEPTH);
nodeService.removeProperty(lockNodeInfo.getNodeRef(), WebDAVModel.PROP_LOCK_SCOPE);
// Indicate that the unlock was successful // Indicate that the unlock was successful
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT); m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
@@ -154,6 +162,28 @@ public class UnlockMethod extends WebDAVMethod
} }
} }
else if (lockSts == LockStatus.NO_LOCK) else if (lockSts == LockStatus.NO_LOCK)
{
String sharedLocks = (String) nodeService.getProperty(lockNodeInfo.getNodeRef(), WebDAVModel.PROP_SHARED_LOCK_TOKENS);
if (sharedLocks != null)
{
LinkedList<String> locks = LockInfo.parseSharedLockTokens(sharedLocks);
if (locks != null && locks.contains(m_strLockToken))
{
locks.remove(m_strLockToken);
nodeService.setProperty(lockNodeInfo.getNodeRef(), WebDAVModel.PROP_SHARED_LOCK_TOKENS, LockInfo.makeSharedLockTokensString(locks));
// Indicate that the unlock was successful
m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
// DEBUG
if (logger.isDebugEnabled())
{
logger.debug("Unlock token=" + getLockToken() + " Successful");
}
}
}
else
{ {
// DEBUG // DEBUG
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
@@ -162,6 +192,9 @@ public class UnlockMethod extends WebDAVMethod
// Node is not locked // Node is not locked
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED); throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
} }
}
else if (lockSts == LockStatus.LOCKED) else if (lockSts == LockStatus.LOCKED)
{ {
// DEBUG // DEBUG

View File

@@ -60,7 +60,7 @@ public class WebDAV
public static final String DAV_NS = "D"; public static final String DAV_NS = "D";
public static final String DAV_NS_PREFIX = DAV_NS + ":"; public static final String DAV_NS_PREFIX = DAV_NS + ":";
// PROPFIND depth // PROPFIND, LOCK depth
public static final int DEPTH_0 = 0; public static final int DEPTH_0 = 0;
public static final int DEPTH_1 = 1; public static final int DEPTH_1 = 1;
@@ -83,6 +83,7 @@ public class WebDAV
public static final String SC_NOT_FOUND_DESC = "Not Found"; public static final String SC_NOT_FOUND_DESC = "Not Found";
public static final String SC_FORBIDDEN_DESC = "Forbidden"; public static final String SC_FORBIDDEN_DESC = "Forbidden";
// HTTP methods // HTTP methods
public static final String METHOD_PUT = "PUT"; public static final String METHOD_PUT = "PUT";
@@ -124,6 +125,10 @@ public class WebDAV
public static final String HEADER_IF_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; public static final String HEADER_IF_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
// If header keyword
public static final String HEADER_KEY_NOT = "Not";
// General string constants // General string constants
public static final String ASTERISK = "*"; public static final String ASTERISK = "*";
@@ -214,6 +219,7 @@ public class WebDAV
public static final String XML_NS_ERROR = DAV_NS_PREFIX + "error"; public static final String XML_NS_ERROR = DAV_NS_PREFIX + "error";
public static final String XML_NS_CANNOT_MODIFY_PROTECTED_PROPERTY = DAV_NS_PREFIX + "cannot-modify-protected-property"; public static final String XML_NS_CANNOT_MODIFY_PROTECTED_PROPERTY = DAV_NS_PREFIX + "cannot-modify-protected-property";
public static final String XML_CONTENT_TYPE = "text/xml; charset=UTF-8"; public static final String XML_CONTENT_TYPE = "text/xml; charset=UTF-8";
// Alfresco specific properties // Alfresco specific properties
@@ -590,6 +596,30 @@ public class WebDAV
return tokens; return tokens;
} }
/**
* Returns string representation of the depth
*
* @param depth
* @return String
*/
public static final String getDepthName(int depth)
{
switch (depth)
{
case DEPTH_0:
return ZERO;
case DEPTH_1:
return ONE;
case DEPTH_INFINITY:
return INFINITY;
default:
throw new IllegalArgumentException("Unknown depth:" + depth);
}
}
/** /**
* Static initializer * Static initializer
*/ */

View File

@@ -28,6 +28,8 @@ import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -36,16 +38,23 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.WebDAVModel;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -57,6 +66,7 @@ import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* Abstract base class for all the WebDAV method handling classes * Abstract base class for all the WebDAV method handling classes
* *
@@ -89,6 +99,19 @@ public abstract class WebDAVMethod
protected String m_strPath = null; protected String m_strPath = null;
// If header conditions
protected LinkedList<Condition> m_conditions = null;
// If header resource-tag
protected String m_resourceTag = null;
// Depth header
protected int m_depth = WebDAV.DEPTH_INFINITY;
/** /**
* Default constructor * Default constructor
*/ */
@@ -125,6 +148,16 @@ public abstract class WebDAVMethod
return false; return false;
} }
/**
* Return the property find depth
*
* @return int
*/
public final int getDepth()
{
return m_depth;
}
/** /**
* Executes the method, wrapping the call to {@link #executeImpl()} in an appropriate transaction * Executes the method, wrapping the call to {@link #executeImpl()} in an appropriate transaction
* and handling the error conditions. * and handling the error conditions.
@@ -233,13 +266,42 @@ public abstract class WebDAVMethod
} }
/** /**
* Returns the lock token present in the If header * Parses "Depth" request header
* *
* @return The lock token present in the If header * @throws WebDAVServerException
*/ */
protected String parseIfHeader() throws WebDAVServerException protected void parseDepthHeader() throws WebDAVServerException
{ {
String strLockToken = null; // Store the Depth header as this is used by several WebDAV methods
String strDepth = m_request.getHeader(WebDAV.HEADER_DEPTH);
if (strDepth != null && strDepth.length() > 0)
{
if (strDepth.equals(WebDAV.ZERO))
{
m_depth = WebDAV.DEPTH_0;
}
else if (strDepth.equals(WebDAV.ONE))
{
m_depth = WebDAV.DEPTH_1;
}
else
{
m_depth = WebDAV.DEPTH_INFINITY;
}
}
}
/**
* Parses "If" header of the request.
* Stores conditions that should be checked.
* Parses both No-tag-list and Tagged-list formats
* See "10.4.2 Syntax" paragraph of the WebDAV specification for "If" header format.
*
*/
protected void parseIfHeader() throws WebDAVServerException
{
//String strLockToken = null;
String strIf = m_request.getHeader(WebDAV.HEADER_IF); String strIf = m_request.getHeader(WebDAV.HEADER_IF);
@@ -248,45 +310,78 @@ public abstract class WebDAVMethod
if (strIf != null && strIf.length() > 0) if (strIf != null && strIf.length() > 0)
{ {
if (strIf.startsWith("(<")) if (strIf.startsWith("<"))
{ {
// Parse the tokens (only get the first one though) m_resourceTag = strIf.substring(1, strIf.indexOf(">"));
strIf = strIf.substring(m_resourceTag.length() + 3);
}
int idx = strIf.indexOf(">"); m_conditions = new LinkedList<Condition>();
if (idx != -1) String[] parts = strIf.split("\\) \\(");
for (int i = 0; i < parts.length; i++)
{
String partString = parts[i].replaceAll("\\(", "").replaceAll("\\)", "");
Condition c = new Condition();
String[] conditions = partString.split(" ");
for (int j = 0; j < conditions.length; j++)
{
boolean fNot = false;
String eTag = null;
String lockToken = null;
if (WebDAV.HEADER_KEY_NOT.equals(conditions[j]))
{
// Check if Not keyword followed by State-token or entity-tag
if (j == (conditions.length - 1))
{
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
}
fNot = true;
j++;
}
// read State-token
int index = conditions[j].indexOf('<');
if (index != -1)
{ {
try try
{ {
strLockToken = strIf.substring(WebDAV.OPAQUE_LOCK_TOKEN.length() + 2, idx); String s = conditions[j].substring(index + 1, conditions[j].indexOf(">"));
} if (!s.startsWith(WebDAV.OPAQUE_LOCK_TOKEN))
catch (IndexOutOfBoundsException e)
{ {
logger.warn("Failed to parse If header: " + strIf); if(!fNot)
{
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
} }
} }
else else
{ {
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST); lockToken = s;
c.addLockTocken(lockToken, fNot);
} }
}
// Print a warning if there are other tokens detected catch (IndexOutOfBoundsException e)
if (strIf.length() > idx + 2)
{ {
logger.warn("The If header contained more than one lock token, only one is supported"); throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
}
}
else if (strIf.startsWith("<"))
{
logger.warn("Tagged lists in the If header are not supported");
}
else if (strIf.startsWith("(["))
{
logger.warn("ETags in the If header are not supported");
} }
} }
return strLockToken; // read entity-tag
index = conditions[j].indexOf("[\"");
if (index != -1)
{
// TODO: implement parsing of weak ETags: W/"123..".
eTag = conditions[j].substring(index + 1, conditions[j].indexOf("]"));
c.addETag(eTag, fNot);
}
}
m_conditions.add(c);
}
}
} }
/** /**
@@ -329,6 +424,26 @@ public abstract class WebDAVMethod
return m_davHelper.getNodeService(); return m_davHelper.getNodeService();
} }
/**
* Convenience method to return the search service
*
* @return SearchService
*/
protected final SearchService getSearchService()
{
return m_davHelper.getSearchService();
}
/**
* Convenience method to return the namespace service
*
* @return NamespaceService
*/
protected final NamespaceService getNamespaceService()
{
return m_davHelper.getNamespaceService();
}
/** /**
* @return Returns the general file/folder manipulation service * @return Returns the general file/folder manipulation service
*/ */
@@ -437,7 +552,7 @@ public abstract class WebDAVMethod
* @param xml XMLWriter * @param xml XMLWriter
* @param lockNode NodeRef * @param lockNode NodeRef
*/ */
protected void generateLockDiscoveryXML(XMLWriter xml, NodeRef lockNode) throws Exception protected void generateLockDiscoveryXML(XMLWriter xml, NodeRef lockNode, LockInfo lockInfo) throws Exception
{ {
Attributes nullAttr= getDAVHelper().getNullAttributes(); Attributes nullAttr= getDAVHelper().getNullAttributes();
@@ -463,13 +578,13 @@ public abstract class WebDAVMethod
// NOTE: We only do exclusive lock tokens at the moment // NOTE: We only do exclusive lock tokens at the moment
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_NS_LOCK_SCOPE, nullAttr); xml.startElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_NS_LOCK_SCOPE, nullAttr);
xml.write(DocumentHelper.createElement(WebDAV.XML_NS_EXCLUSIVE)); xml.write(DocumentHelper.createElement(lockInfo.getScope()));
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_NS_LOCK_SCOPE); xml.endElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_SCOPE, WebDAV.XML_NS_LOCK_SCOPE);
// NOTE: We only support one level of lock at the moment // NOTE: We only support one level of lock at the moment
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_DEPTH, WebDAV.XML_NS_DEPTH, nullAttr); xml.startElement(WebDAV.DAV_NS, WebDAV.XML_DEPTH, WebDAV.XML_NS_DEPTH, nullAttr);
xml.write(WebDAV.ZERO); xml.write(lockInfo.getDepth());
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_DEPTH, WebDAV.XML_NS_DEPTH); xml.endElement(WebDAV.DAV_NS, WebDAV.XML_DEPTH, WebDAV.XML_NS_DEPTH);
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_OWNER, WebDAV.XML_NS_OWNER, nullAttr); xml.startElement(WebDAV.DAV_NS, WebDAV.XML_OWNER, WebDAV.XML_NS_OWNER, nullAttr);
@@ -531,7 +646,7 @@ public abstract class WebDAVMethod
String strNamespaceName = nameSpaces.get(strNamespaceUri); String strNamespaceName = nameSpaces.get(strNamespaceUri);
ns.append(" ").append(WebDAV.XML_NS).append(":").append(strNamespaceName).append("=\""); ns.append(" ").append(WebDAV.XML_NS).append(":").append(strNamespaceName).append("=\"");
ns.append(strNamespaceUri).append("\" "); ns.append(strNamespaceUri == null ? "" : strNamespaceUri).append("\" ");
} }
} }
@@ -539,4 +654,451 @@ public abstract class WebDAVMethod
} }
/**
* Checks if write operation can be performed on node.
*
* @param fileInfo - node's file info
* @param ignoreShared - if true ignores shared locks
* @param lockMethod - must be true if used from lock method
* @return node's lock info
* @throws WebDAVServerException if node has shared or exclusive lock
* or If header preconditions failed
*/
protected LockInfo checkNode(FileInfo fileInfo, boolean ignoreShared, boolean lockMethod) throws WebDAVServerException
{
LockInfo nodeLockInfo = getNodeLockInfo(fileInfo.getNodeRef());
String nodeETag = getDAVHelper().makeQuotedETag(fileInfo.getNodeRef());
if (m_conditions == null)
{
if (nodeLockInfo.getToken() == null)
{
if (nodeLockInfo.getSharedLockTokens() == null)
{
return nodeLockInfo;
}
if (!ignoreShared)
{
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
}
else
{
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
}
// Checking of the If tag consists of two checks:
// 1. If the node is locked we need to check it's Lock token independently of conditions check result.
// For example "(<wrong token>) (Not <DAV:no-lock>)" if always true,
// but request must fail with 423 Locked response because node is locked.
// 2. Check if ANY of the conditions in If header true.
checkLockToken(nodeLockInfo, ignoreShared, lockMethod);
checkConditions(nodeLockInfo.getToken(), nodeETag);
return nodeLockInfo;
}
/**
* Checks if write operation can be performed on node.
*
* @param fileInfo
* @return
* @throws WebDAVServerException if node has shared or exclusive lock
* or If header preconditions failed
*/
protected LockInfo checkNode(FileInfo fileInfo) throws WebDAVServerException
{
return checkNode(fileInfo, false, true);
}
/**
* Checks if node can be accessed with WebDAV operation
*
* @param nodeLockToken - token to check
* @param lockInfo - node's lock info
* @param ignoreShared - if true - ignores shared lock tokens
* @param lockMethod - must be true if used from lock method
* @throws WebDAVServerException if node has no appropriate lock token
*/
private void checkLockToken(LockInfo lockInfo, boolean ignoreShared, boolean lockMethod) throws WebDAVServerException
{
String nodeLockToken = lockInfo.getToken();
LinkedList<String> sharedLockTokens = lockInfo.getSharedLockTokens();
if (m_conditions != null)
{
// Request has conditions to check
if (lockInfo.isShared())
{
// Node has shared lock. Check if conditions contains lock token of the node.
// If not throw exception
if (sharedLockTokens != null)
{
if (!ignoreShared)
{
for (Condition condition : m_conditions)
{
for (String sharedLockToken : sharedLockTokens)
{
if (condition.getLockTokensMatch().contains(sharedLockToken))
{
return;
}
}
}
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
return;
}
}
else
{
// Node has exclusive lock. Check if conditions contains lock token of the node
// If not throw exception
for (Condition condition : m_conditions)
{
if (nodeLockToken != null)
{
if (condition.getLockTokensMatch().contains(nodeLockToken))
{
return;
}
}
}
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
}
else
{
// Request has no conditions
if (lockInfo.isShared())
{
// If lock is shared and check was called not from LOCK method return
if (!lockMethod)
{
return;
}
// Throw exception - we can't set lock on node with shared lock
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
}
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
}
/**
* Checks If header conditions. Throws WebDAVServerException with 412(Precondition failed)
* if none of the conditions success.
*
* @param nodeLockToken - node's lock token
* @param nodeETag - node's ETag
* @throws WebDAVServerException if conditions fail
*/
private void checkConditions(String nodeLockToken, String nodeETag) throws WebDAVServerException
{
// Checks If header conditions.
// Each condition can contain check of ETag and check of Lock token.
if (m_conditions == null)
{
// No conditions were provided with "If" request header, so check successful
return;
}
// Check the list of "If" header's conditions.
// If any condition conforms then check is successful
for (Condition condition : m_conditions)
{
// Flag for ETag conditions
boolean fMatchETag = true;
// Flag for Lock token conditions
boolean fMatchLockToken = true;
// Check ETags that should match
if (condition.getETagsMatch() != null)
{
fMatchETag = condition.getETagsMatch().contains(nodeETag) ? true : false;
}
// Check ETags that shouldn't match
if (condition.getETagsNotMatch() != null)
{
fMatchETag = condition.getETagsNotMatch().contains(nodeETag) ? false : true;
}
// Check lock tokens that should match
if (condition.getLockTokensMatch() != null)
{
fMatchLockToken = condition.getLockTokensMatch().contains(nodeLockToken) ? true : false;
}
// Check lock tokens that shouldn't match
if (condition.getLockTokensNotMatch() != null)
{
fMatchLockToken = condition.getLockTokensNotMatch().contains(nodeLockToken) ? false : true;
}
if (fMatchETag && fMatchLockToken)
{
// Condition conforms
return;
}
}
// None of the conditions successful
throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
}
/**
* Returns node Lock token in consideration of WebDav lock depth.
*
* @param fileInfo node
* @return String Lock token
*/
protected LockInfo getNodeLockInfo(NodeRef nodeRef)
{
LockInfo lockInfo = new LockInfo();
NodeService nodeService = getNodeService();
LockService lockService = getLockService();
// Check if node is locked directly.
LockStatus lockSts = lockService.getLockStatus(nodeRef);
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
{
String propOpaqueLockToken = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
if (propOpaqueLockToken != null)
{
// Get lock depth
String depth = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_DEPTH);
//Get lock scope
String scope = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_SCOPE);
// Get shared lock tokens
String sharedLocks = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
// Node has it's own Lock token.
// Store lock information to the lockInfo object
lockInfo.setToken(propOpaqueLockToken);
lockInfo.setDepth(depth);
lockInfo.setScope(scope);
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
return lockInfo;
}
}
else
{
// No has no exclusive lock but can be locked with shared lock
String sharedLocks = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
if (sharedLocks != null)
{
// Get lock depth
String depth = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_DEPTH);
//Get lock scope
String scope = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_SCOPE);
// Node has it's own Lock token.
// Store lock information to the lockInfo object
lockInfo.setDepth(depth);
lockInfo.setScope(scope);
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
lockInfo.setShared(true);
return lockInfo;
}
}
// Node isn't locked directly and has no it's own Lock token.
// Try to search indirect lock.
NodeRef node = nodeRef;
while (true)
{
List<ChildAssociationRef> assocs = nodeService.getParentAssocs(node, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
if (assocs.isEmpty())
{
// Node has no lock and Lock token
return new LockInfo();
}
NodeRef parent = assocs.get(0).getParentRef();
lockSts = lockService.getLockStatus(parent);
if (lockSts == LockStatus.LOCKED || lockSts == LockStatus.LOCK_OWNER)
{
// Check node lock depth.
// If depth is WebDAV.INFINITY then return this node's Lock token.
String depth = (String) nodeService.getProperty(parent, WebDAVModel.PROP_LOCK_DEPTH);
if (WebDAV.INFINITY.equals(depth))
{
// In this case node is locked indirectly.
//Get lock scope
String scope = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_SCOPE);
// Get shared lock tokens
String sharedLocks = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
// Store lock information to the lockInfo object
// Get lock token of the locked node - this is indirect lock token.
String propOpaqueLockToken = (String) nodeService.getProperty(parent, WebDAVModel.PROP_OPAQUE_LOCK_TOKEN);
lockInfo.setToken(propOpaqueLockToken);
lockInfo.setDepth(depth);
lockInfo.setScope(scope);
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
return lockInfo;
}
}
else
{
// No has no exclusive lock but can be locked with shared lock
String sharedLocks = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_SHARED_LOCK_TOKENS);
if (sharedLocks != null)
{
// Check node lock depth.
// If depth is WebDAV.INFINITY then return this node's Lock token.
String depth = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_DEPTH);
if (WebDAV.INFINITY.equals(depth))
{
// In this case node is locked indirectly.
//Get lock scope
String scope = (String) nodeService.getProperty(nodeRef, WebDAVModel.PROP_LOCK_SCOPE);
// Node has it's own Lock token.
lockInfo.setDepth(depth);
lockInfo.setScope(scope);
lockInfo.setSharedLockTokens(LockInfo.parseSharedLockTokens(sharedLocks));
lockInfo.setShared(true);
return lockInfo;
}
}
}
node = parent;
}
}
/**
* Class used for storing conditions which comes with "If" header of the request
*
* @author ivanry
*
*/
protected class Condition
{
// These tokens will be checked on equivalence against node's lock token
private LinkedList<String> lockTokensMatch = new LinkedList<String>();
// These tokens will be checked on non-equivalence against node's lock token
private LinkedList<String> lockTokensNotMatch = new LinkedList<String>();
// These ETags will be checked on equivalence against node's ETags
private LinkedList<String> eTagsMatch;
// These ETags will be checked on non-equivalence against node's ETags
private LinkedList<String> eTagsNotMatch;
/**
* Default constructor
*
*/
public Condition()
{
}
/**
* Returns the list of lock tokens that should be checked against node's lock token on equivalence.
*
* @return lock tokens
*/
public LinkedList<String> getLockTokensMatch()
{
return this.lockTokensMatch;
}
/**
* Returns the list of lock tokens that should be checked against node's lock token on non-equivalence.
*
* @return lock tokens
*/
public LinkedList<String> getLockTokensNotMatch()
{
return this.lockTokensNotMatch;
}
/**
* Returns the list of ETags that should be checked against node's ETag on equivalence.
*
* @return ETags list
*/
public LinkedList<String> getETagsMatch()
{
return this.eTagsMatch;
}
/**
* Returns the list of ETags that should be checked against node's ETag on non-equivalence.
*
* @return ETags list
*/
public LinkedList<String> getETagsNotMatch()
{
return this.eTagsNotMatch;
}
/**
* Adds lock token to check
*
* @param lockToken String
* @param notMatch true is lock token should be added to the list matched tokens.
* false if should be added to the list of non-matches.
*/
public void addLockTocken(String lockToken, boolean notMatch)
{
if (notMatch)
{
this.lockTokensNotMatch.add(lockToken);
}
else
{
this.lockTokensMatch.add(lockToken);
}
}
/**
* Add ETag to check
*
* @param eTag String
* @param notMatch true is ETag should be added to the list matched ETags.
* false if should be added to the list of non-matches.
*/
public void addETag(String eTag, boolean notMatch)
{
if (notMatch)
{
if (eTagsNotMatch == null)
{
eTagsNotMatch = new LinkedList<String>();
}
this.eTagsNotMatch.add(eTag);
}
else
{
if (eTagsMatch == null)
{
eTagsMatch = new LinkedList<String>();
}
this.eTagsMatch.add(eTag);
}
}
}
} }

View File

@@ -138,6 +138,7 @@ public class WebDAVProperty
m_strValue = strValue; m_strValue = strValue;
} }
/** /**
* Creates QName of the property * Creates QName of the property
* *
@@ -163,6 +164,7 @@ public class WebDAVProperty
WebDAV.XML_SUPPORTED_LOCK.equals(m_strName); WebDAV.XML_SUPPORTED_LOCK.equals(m_strName);
} }
/** /**
* Return the property as a string * Return the property as a string
* *