mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
521 lines
16 KiB
Java
521 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* 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 org.alfresco.repo.webdav;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
|
import org.alfresco.repo.transaction.TransactionUtil;
|
|
import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
|
|
import org.alfresco.service.ServiceRegistry;
|
|
import org.alfresco.service.cmr.lock.LockService;
|
|
import org.alfresco.service.cmr.model.FileFolderService;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.repository.MimetypeService;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.cmr.security.AuthenticationService;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.dom4j.DocumentHelper;
|
|
import org.dom4j.io.OutputFormat;
|
|
import org.dom4j.io.XMLWriter;
|
|
import org.w3c.dom.Document;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
|
|
/**
|
|
* Abstract base class for all the WebDAV method handling classes
|
|
*
|
|
* @author gavinc
|
|
*/
|
|
public abstract class WebDAVMethod
|
|
{
|
|
// Log output
|
|
|
|
protected static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol");
|
|
|
|
// Output formatted XML in the response
|
|
|
|
private static final boolean XMLPrettyPrint = true;
|
|
|
|
// Servlet request/response
|
|
|
|
protected HttpServletRequest m_request;
|
|
protected HttpServletResponse m_response;
|
|
|
|
// WebDAV helper
|
|
|
|
protected WebDAVHelper m_davHelper;
|
|
|
|
// Root node
|
|
|
|
protected NodeRef m_rootNodeRef;
|
|
|
|
// Repository path
|
|
|
|
protected String m_strPath = null;
|
|
|
|
/**
|
|
* Default constructor
|
|
*/
|
|
public WebDAVMethod()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Set the request/response details
|
|
*
|
|
* @param req HttpServletRequest
|
|
* @param resp HttpServletResponse
|
|
* @param registry ServiceRegistry
|
|
* @param rootNode NodeRef
|
|
*/
|
|
public void setDetails(HttpServletRequest req, HttpServletResponse resp, WebDAVHelper davHelper, NodeRef rootNode)
|
|
{
|
|
m_request = req;
|
|
m_response = resp;
|
|
m_davHelper = davHelper;
|
|
m_rootNodeRef = rootNode;
|
|
|
|
m_strPath = WebDAV.getRepositoryPath(req);
|
|
}
|
|
|
|
/**
|
|
* Executes the method
|
|
*/
|
|
public void execute() throws WebDAVServerException
|
|
{
|
|
// Parse the HTTP headers
|
|
parseRequestHeaders();
|
|
|
|
// Parse the HTTP body
|
|
parseRequestBody();
|
|
|
|
TransactionWork<WebDAVServerException> executeWork = new TransactionWork<WebDAVServerException>()
|
|
{
|
|
public WebDAVServerException doWork() throws Exception
|
|
{
|
|
executeImpl();
|
|
return null;
|
|
}
|
|
};
|
|
try
|
|
{
|
|
// Execute the method
|
|
TransactionService transactionService = getTransactionService();
|
|
TransactionUtil.executeInUserTransaction(transactionService, executeWork);
|
|
}
|
|
catch (AccessDeniedException e)
|
|
{
|
|
// Return a forbidden status
|
|
throw new WebDAVServerException(HttpServletResponse.SC_UNAUTHORIZED, e);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
Throwable cause = e.getCause();
|
|
if (cause instanceof WebDAVServerException)
|
|
{
|
|
throw (WebDAVServerException) cause;
|
|
}
|
|
else
|
|
{
|
|
// Convert error to a server error
|
|
throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Access the content repository to satisfy the request and generates the appropriate WebDAV
|
|
* response.
|
|
*
|
|
* @throws WebDAVServerException a general server exception
|
|
* @throws Exception any unhandled exception
|
|
*/
|
|
protected abstract void executeImpl() throws WebDAVServerException, Exception;
|
|
|
|
/**
|
|
* Parses the given request body represented as an XML document and sets any necessary context
|
|
* ready for execution.
|
|
*/
|
|
protected abstract void parseRequestBody() throws WebDAVServerException;
|
|
|
|
/**
|
|
* Parses the HTTP headers of the request and sets any necessary context ready for execution.
|
|
*/
|
|
protected abstract void parseRequestHeaders() throws WebDAVServerException;
|
|
|
|
/**
|
|
* Retrieves the request body as an XML document
|
|
*
|
|
* @return The body of the request as an XML document or null if there isn't a body
|
|
*/
|
|
protected Document getRequestBodyAsDocument() throws WebDAVServerException
|
|
{
|
|
Document body = null;
|
|
|
|
if (m_request.getContentLength() > 0)
|
|
{
|
|
// TODO: Do we need to do anything for chunking support?
|
|
|
|
try
|
|
{
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
factory.setNamespaceAware(true);
|
|
|
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
body = builder.parse(new InputSource(m_request.getReader()));
|
|
}
|
|
catch (ParserConfigurationException e)
|
|
{
|
|
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST, e);
|
|
}
|
|
catch (SAXException e)
|
|
{
|
|
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST, e);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST, e);
|
|
}
|
|
}
|
|
|
|
return body;
|
|
}
|
|
|
|
/**
|
|
* Returns the lock token present in the If header
|
|
*
|
|
* @return The lock token present in the If header
|
|
*/
|
|
protected String parseIfHeader() throws WebDAVServerException
|
|
{
|
|
String strLockToken = null;
|
|
|
|
String strIf = m_request.getHeader(WebDAV.HEADER_IF);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Parsing If header: " + strIf);
|
|
|
|
if (strIf != null && strIf.length() > 0)
|
|
{
|
|
if (strIf.startsWith("(<"))
|
|
{
|
|
// Parse the tokens (only get the first one though)
|
|
|
|
int idx = strIf.indexOf(">");
|
|
if (idx != -1)
|
|
{
|
|
try
|
|
{
|
|
strLockToken = strIf.substring(WebDAV.OPAQUE_LOCK_TOKEN.length() + 2, idx);
|
|
}
|
|
catch (IndexOutOfBoundsException e)
|
|
{
|
|
logger.warn("Failed to parse If header: " + strIf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new WebDAVServerException(HttpServletResponse.SC_BAD_REQUEST);
|
|
}
|
|
|
|
// Print a warning if there are other tokens detected
|
|
|
|
if (strIf.length() > idx + 2)
|
|
{
|
|
logger.warn("The If header contained more than one lock token, only one is supported");
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Return the WebDAV protocol helper
|
|
*
|
|
* @return WebDAVHelper
|
|
*/
|
|
protected final WebDAVHelper getDAVHelper()
|
|
{
|
|
return m_davHelper;
|
|
}
|
|
|
|
/**
|
|
* Return the service registry
|
|
*
|
|
* @return ServiceRegistry
|
|
*/
|
|
protected final ServiceRegistry getServiceRegistry()
|
|
{
|
|
return m_davHelper.getServiceRegistry();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the transaction service
|
|
*
|
|
* @return TransactionService
|
|
*/
|
|
protected final TransactionService getTransactionService()
|
|
{
|
|
return m_davHelper.getServiceRegistry().getTransactionService();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the node service
|
|
*
|
|
* @return NodeService
|
|
*/
|
|
protected final NodeService getNodeService()
|
|
{
|
|
return m_davHelper.getNodeService();
|
|
}
|
|
|
|
/**
|
|
* @return Returns the general file/folder manipulation service
|
|
*/
|
|
protected final FileFolderService getFileFolderService()
|
|
{
|
|
return m_davHelper.getFileFolderService();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the content service
|
|
*
|
|
* @return ContentService
|
|
*/
|
|
protected final ContentService getContentService()
|
|
{
|
|
return m_davHelper.getServiceRegistry().getContentService();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the mimetype service
|
|
*
|
|
* @return MimetypeService
|
|
*/
|
|
protected final MimetypeService getMimetypeService()
|
|
{
|
|
return m_davHelper.getMimetypeService();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the lock service
|
|
*
|
|
* @return LockService
|
|
*/
|
|
protected final LockService getLockService()
|
|
{
|
|
return m_davHelper.getLockService();
|
|
}
|
|
|
|
/**
|
|
* Convenience method to return the authentication service
|
|
*
|
|
* @return AuthenticationService
|
|
*/
|
|
protected final AuthenticationService getAuthenticationService()
|
|
{
|
|
return m_davHelper.getAuthenticationService();
|
|
}
|
|
|
|
/**
|
|
* @return Returns the path of the servlet
|
|
*/
|
|
protected final String getServletPath()
|
|
{
|
|
return m_request.getServletPath();
|
|
}
|
|
|
|
/**
|
|
* Return the root node
|
|
*
|
|
* @return NodeRef
|
|
*/
|
|
protected final NodeRef getRootNodeRef()
|
|
{
|
|
return m_rootNodeRef;
|
|
}
|
|
|
|
/**
|
|
* Return the relative path
|
|
*
|
|
* @return String
|
|
*/
|
|
protected final String getPath()
|
|
{
|
|
return m_strPath;
|
|
}
|
|
|
|
/**
|
|
* Create an XML writer for the response
|
|
*
|
|
* @return XMLWriter
|
|
* @exception IOException
|
|
*/
|
|
protected final XMLWriter createXMLWriter() throws IOException
|
|
{
|
|
// Check if debug output or XML pretty printing is enabled
|
|
|
|
XMLWriter writer = null;
|
|
|
|
if (XMLPrettyPrint == true || logger.isDebugEnabled())
|
|
{
|
|
writer = new XMLWriter(m_response.getWriter(), OutputFormat.createPrettyPrint());
|
|
}
|
|
else
|
|
{
|
|
writer = new XMLWriter(m_response.getWriter(), OutputFormat.createCompactFormat());
|
|
}
|
|
|
|
// Return the writer
|
|
|
|
return writer;
|
|
}
|
|
|
|
/**
|
|
* Generates the lock discovery XML response
|
|
*
|
|
* @param xml XMLWriter
|
|
* @param lockNode NodeRef
|
|
*/
|
|
protected void generateLockDiscoveryXML(XMLWriter xml, NodeRef lockNode) throws Exception
|
|
{
|
|
Attributes nullAttr= getDAVHelper().getNullAttributes();
|
|
|
|
if (lockNode != null)
|
|
{
|
|
|
|
// Get the lock details
|
|
|
|
NodeService nodeService = getNodeService();
|
|
|
|
String owner = (String) nodeService.getProperty(lockNode, ContentModel.PROP_LOCK_OWNER);
|
|
Date expiryDate = (Date) nodeService.getProperty(lockNode, ContentModel.PROP_EXPIRY_DATE);
|
|
|
|
// Output the XML response
|
|
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_DISCOVERY, WebDAV.XML_NS_LOCK_DISCOVERY, nullAttr);
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_ACTIVE_LOCK, WebDAV.XML_NS_ACTIVE_LOCK, nullAttr);
|
|
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_TYPE, WebDAV.XML_NS_LOCK_TYPE, nullAttr);
|
|
xml.write(DocumentHelper.createElement(WebDAV.XML_NS_WRITE));
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_TYPE, WebDAV.XML_NS_LOCK_TYPE);
|
|
|
|
// 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.write(DocumentHelper.createElement(WebDAV.XML_NS_EXCLUSIVE));
|
|
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
|
|
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_DEPTH, WebDAV.XML_NS_DEPTH, nullAttr);
|
|
xml.write(WebDAV.ZERO);
|
|
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.write(owner);
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_OWNER, WebDAV.XML_NS_OWNER);
|
|
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_TIMEOUT, WebDAV.XML_NS_TIMEOUT, nullAttr);
|
|
|
|
// Output the expiry time
|
|
|
|
String strTimeout = WebDAV.INFINITE;
|
|
if (expiryDate != null)
|
|
{
|
|
long timeoutRemaining = (expiryDate.getTime() - System.currentTimeMillis())/1000L;
|
|
|
|
strTimeout = WebDAV.SECOND + timeoutRemaining;
|
|
}
|
|
xml.write(strTimeout);
|
|
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_TIMEOUT, WebDAV.XML_NS_TIMEOUT);
|
|
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_NS_LOCK_TOKEN, nullAttr);
|
|
xml.startElement(WebDAV.DAV_NS, WebDAV.XML_HREF, WebDAV.XML_NS_HREF, nullAttr);
|
|
|
|
xml.write(WebDAV.makeLockToken(lockNode, owner));
|
|
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_HREF, WebDAV.XML_NS_HREF);
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_TOKEN, WebDAV.XML_NS_LOCK_TOKEN);
|
|
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_ACTIVE_LOCK, WebDAV.XML_NS_ACTIVE_LOCK);
|
|
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_LOCK_DISCOVERY, WebDAV.XML_NS_LOCK_DISCOVERY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a list of namespace declarations for the response
|
|
*/
|
|
protected String generateNamespaceDeclarations(HashMap<String,String> nameSpaces)
|
|
{
|
|
StringBuilder ns = new StringBuilder();
|
|
|
|
ns.append(" ");
|
|
ns.append(WebDAV.XML_NS);
|
|
ns.append(":");
|
|
ns.append(WebDAV.DAV_NS);
|
|
ns.append("=\"");
|
|
ns.append(WebDAV.DEFAULT_NAMESPACE_URI);
|
|
ns.append("\"");
|
|
|
|
// Add additional namespaces
|
|
|
|
if ( nameSpaces != null)
|
|
{
|
|
Iterator<String> namespaceList = nameSpaces.keySet().iterator();
|
|
|
|
while (namespaceList.hasNext())
|
|
{
|
|
String strNamespaceUri = namespaceList.next();
|
|
String strNamespaceName = nameSpaces.get(strNamespaceUri);
|
|
|
|
ns.append(" ").append(WebDAV.XML_NS).append(":").append(strNamespaceName).append("=\"");
|
|
ns.append(strNamespaceUri).append("\" ");
|
|
}
|
|
}
|
|
|
|
return ns.toString();
|
|
}
|
|
|
|
|
|
}
|