/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/PropFindMethod.java,v 1.35 2001/08/17 14:39:12 cmlenz Exp $
* $Revision: 1.35 $
* $Date: 2001/08/17 14:39:12 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.slide.webdav.method;
import java.security.Principal;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.apache.util.XMLPrinter;
import org.apache.util.WebdavStatus;
import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.*;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.webdav.*;
import org.apache.slide.structure.*;
import org.apache.slide.lock.*;
import org.apache.slide.content.*;
import org.apache.slide.security.NodePermission;
/**
* PROPFIND method.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
*/
public class PropFindMethod extends WebdavMethod {
// -------------------------------------------------------------- Constants
/**
* Root node URI.
*/
protected static final String DIRECTORY = "/";
/**
* Default depth is infite.
*/
protected static final int INFINITY = Integer.MAX_VALUE;
/**
* Specify a property mask.
*/
protected static final int FIND_BY_PROPERTY = 0;
/**
* Display all properties.
*/
protected static final int FIND_ALL_PROP = 1;
/**
* Return property names.
*/
protected static final int FIND_PROPERTY_NAMES = 2;
/**
* Supported locks property value.
*/
protected static final String SUPPORTED_LOCK = "<lockentry>"
+ "<lockscope><exclusive/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>"
+ "<lockentry>"
+ "<lockscope><shared/></lockscope>"
+ "<locktype><write/></locktype>"
+ "</lockentry>";
/**
* Supported privilege set.
*/
protected static final String SUPPORTED_PRIVILEGE_SET =
"supported-privilege-set";
/**
* Supported privileges.
*/
protected static final String SUPPORTED_PRIVILEGES =
"<supported-privilege>"
+ "<privilege><all/></privilege>"
+ "<abstract/>"
+ "<description>Any operation</description>"
+ "<supported-privilege>"
+ "<privilege><read/></privilege>"
+ "<description>Read any object</description>"
+ "</supported-privilege>"
+ "<supported-privilege>"
+ "<privilege><write/></privilege>"
+ "<description>Write any object</description>"
+ "</supported-privilege>"
+ "<supported-privilege>"
+ "<privilege><read-acl/></privilege>"
+ "<description>Read the ACL</description>"
+ "</supported-privilege>"
+ "<supported-privilege>"
+ "<privilege><write-acl/></privilege>"
+ "<description>Write the ACL</description>"
+ "</supported-privilege>"
+ "</supported-privilege>";
/**
* ACL semantics.
*/
protected static final String SLIDE_ACL_SEMANTICS =
"<acl-sem>"
+ "<ace-combination><no-deny/></ace-combination>"
+ "</acl-sem>"
+ "<acl-sem>"
+ "<required-principal><href/></required-principal>"
+ "</acl-sem>"
+ "<acl-sem>"
+ "<required-principal><self/></required-principal>"
+ "</acl-sem>"
+ "<acl-sem>"
+ "<required-principal><all/></required-principal>"
+ "</acl-sem>";
/**
* Current user privilege set.
*/
protected static final String CURRENT_USER_PRIVILEGE_SET =
"current-user-privilege-set";
/**
* Principal collection set.
*/
protected static final String PRINCIPAL_COLLECTION_SET =
"principal-collection-set";
/**
* ACL semantics.
*/
protected static final String ACL_SEMANTICS = "acl-semantics";
/**
* ACL.
*/
protected static final String ACL = "acl";
/**
* ACE.
*/
protected static final String ACE = "ace";
/**
* GRANT.
*/
protected static final String GRANT = "grant";
/**
* DENY.
*/
protected static final String DENY = "deny";
/**
* PRINCIPAL.
*/
protected static final String PRINCIPAL = "principal";
/**
* PRIVILEGE.
*/
protected static final String PRIVILEGE = "privilege";
/**
* INHERITED.
*/
protected static final String INHERITED = "inherited";
// ----------------------------------------------------- Instance Variables
/**
* Depth.
*/
protected int depth;
/**
* Type of the PROPFIND method.
*/
protected int propFindType;
/**
* Properties to display
*/
protected Vector propertyVector;
/**
* Current namespace number used to generate namespace abbreviations.
*/
protected int namespaceNumber = 0;
/**
* Namespaces list. Keyed by namespace names.
*/
protected HashMap namespaces;
// ----------------------------------------------------------- Constructors
/**
* PROPFIND method constructor.
*
* @param token Namespace access token
* @param req HTTP request
* @param resp HTTP response
*/
public PropFindMethod(NamespaceAccessToken token, HttpServletRequest req,
HttpServletResponse resp,
WebdavServletConfig config) {
super(token, req, resp, config);
readRequestContent();
namespaces = new HashMap();
namespaces.put(SLIDE_NAMESPACE, SLIDE_NAMESPACE_ABBREV);
depth = INFINITY;
propFindType = FIND_ALL_PROP;
}
// ------------------------------------------------------ Protected Methods
/**
* Parse the request.
*
* @exception WebdavException Bad request
*/
protected void parseRequest()
throws WebdavException {
// parse Depth header
String depthStr = req.getHeader("Depth");
if (depthStr == null) {
depth = INFINITY;
} else if (depthStr.equals("0")) {
depth = 0;
} else if (depthStr.equals("1")) {
depth = 1;
} else if (depthStr.equalsIgnoreCase("infinity")) {
depth = INFINITY;
} else {
try {
depth = Integer.parseInt(depthStr);
if (depth < 0) {
depth = 0;
}
} catch (NumberFormatException ex) {
depth = INFINITY;
}
}
// limit tree browsing a bit
if (depth > getConfig().getDepthLimit()) {
depth = getConfig().getDepthLimit();
}
if (req.getContentLength() != 0) {
Node propNode = null;
try {
Document document = parseRequestContent();
// Get the root element of the document
Element rootElement = document.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("prop")) {
propFindType = FIND_BY_PROPERTY;
propNode = currentNode;
}
if (currentNode.getNodeName().endsWith("propname")) {
propFindType = FIND_PROPERTY_NAMES;
}
if (currentNode.getNodeName().endsWith("allprop")) {
propFindType = FIND_ALL_PROP;
}
break;
}
}
} catch (SAXException e) {
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
throw new WebdavException(WebdavStatus.SC_BAD_REQUEST);
} catch (ParserConfigurationException e) {
System.err.println(e.getMessage());
resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
throw new WebdavException
(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
} catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
throw new WebdavException
(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
}
if (propFindType == FIND_BY_PROPERTY) {
propertyVector = new Vector();
NodeList childList = propNode.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:
Property property = getProperty(currentNode);
propertyVector.addElement(property);
break;
}
}
}
}
}
/**
* Parse the namespace info of a node name.
*
* @param node The DOM node to parse
* @return The corresponding Property object
*/
protected Property getProperty(Node node) {
Property property = new Property();
property.name = node.getLocalName();
property.namespace = node.getNamespaceURI();
generateNamespaceAbbreviation(property.namespace);
return property;
}
/**
* Execute the request.
*
* @exception WebdavException
*/
protected void executeRequest()
throws IOException, WebdavException {
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
// Loads the associated object from the store.
String resourceUri = requestUri;
// Get the object from Data.
ObjectNode resource = null;
try {
resource = structure.retrieve(slideToken, resourceUri);
} catch (StructureException e) {
try {
resp.sendError
(WebdavStatus.SC_NOT_FOUND,
WebdavStatus.getStatusText(WebdavStatus.SC_NOT_FOUND));
} catch(IOException ex) {
ex.printStackTrace();
}
throw new WebdavException(WebdavStatus.SC_NOT_FOUND);
} catch (Exception e) {
resp.setStatus(getErrorCode(e)); // no special handling needed
throw new WebdavException(WebdavStatus.SC_ACCEPTED, false); // abort the TA
}
resp.setContentType("text/xml; charset=UTF-8");
// Create multistatus object
XMLPrinter generatedXML = new XMLPrinter(resp.getWriter());
generatedXML.writeXMLHeader();
generatedXML.writeElement(null, "multistatus"
+ generateNamespaceDeclarations(),
XMLPrinter.OPENING);
if (resource != null) {
if (depth == 0) {
parsePropertiesOfObject(resource, generatedXML);
} else {
// The stack always contains the object of the current level
Stack stack = new Stack();
stack.push(resource);
// Stack of the objects one level below
Stack stackBelow = new Stack();
while ((!stack.isEmpty()) && (depth >= 0)) {
// Send partial response
generatedXML.sendData();
ObjectNode cur = (ObjectNode) stack.pop();
parsePropertiesOfObject(cur, generatedXML);
if (depth > 0) {
Enumeration enum = null;
try {
enum = structure.getChildren(slideToken, cur);
} catch (Exception e) {
resp.setStatus(getErrorCode(e)); // no special handling needed
throw new WebdavException(WebdavStatus.SC_ACCEPTED, false); // abort the TA
}
while (enum.hasMoreElements()) {
stackBelow.push(enum.nextElement());
}
}
if (stack.isEmpty()) {
depth--;
stack = stackBelow;
stackBelow = new Stack();
}
}
}
}
generatedXML.writeElement(null, "multistatus",
XMLPrinter.CLOSING);
// Send remaining data
generatedXML.sendData();
}
/**
* Parse properties of an object.
*
* @param object
* @param multistatus
* @exception WebdavException
*/
protected void parsePropertiesOfObject(ObjectNode object,
XMLPrinter generatedXML)
throws WebdavException {
generatedXML.writeElement(null, "response", XMLPrinter.OPENING);
String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
+ WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
NodeRevisionDescriptors revisionDescriptors = null;
NodeRevisionDescriptor revisionDescriptor = null;
boolean isCollection = false;
NodeLock objectLockToken = null;
try {
generatedXML.writeElement(null, "href", XMLPrinter.OPENING);
revisionDescriptors =
content.retrieve(slideToken, object.getUri());
try {
revisionDescriptor = content.retrieve(slideToken,
revisionDescriptors);
isCollection = WebdavUtils.isCollection(revisionDescriptor);
String path = object.getUri();
String absoluteUri =
WebdavUtils.decodeURL(req.getRequestURI());
String relativePath = requestUri;
String toAppend = "";
if (relativePath.length() <= path.length()) {
toAppend = path.substring(relativePath.length());
if ((!absoluteUri.endsWith("/")) &&
(!toAppend.startsWith("/"))) {
toAppend = "/" + toAppend;
}
if (toAppend.equals("/")) {
toAppend = "";
}
}
generatedXML.writeText(
WebdavUtils.encodeURL(absoluteUri + toAppend));
} catch (RevisionDescriptorNotFoundException e) {
// The object doesn't have any revision, we create a dummy
// NodeRevisionDescriptor object
isCollection = true;
revisionDescriptor = new NodeRevisionDescriptor(0);
String resourceName = object.getUri();
int lastSlash = resourceName.lastIndexOf('/');
if (lastSlash != -1)
resourceName = resourceName.substring(lastSlash + 1);
revisionDescriptor.setName(resourceName);
String path = object.getUri();
String absoluteUri =
WebdavUtils.decodeURL(req.getRequestURI());
String relativePath = requestUri;
String toAppend = "";
if (relativePath.length() <= path.length()) {
toAppend = path.substring(relativePath.length());
if ((!absoluteUri.endsWith("/"))
&& (!toAppend.startsWith("/"))) {
toAppend = "/" + toAppend;
}
}
generatedXML.writeText(
WebdavUtils.encodeURL(absoluteUri + toAppend));
}
generatedXML.writeElement(null, "href", XMLPrinter.CLOSING);
Enumeration lockTokens = lock.enumerateLocks(slideToken,
object.getUri());
if (lockTokens.hasMoreElements()) {
objectLockToken = (NodeLock) lockTokens.nextElement();
}
} catch (AccessDeniedException e) {
if (revisionDescriptor == null) {
revisionDescriptor = new NodeRevisionDescriptor(0);
}
} catch (Exception e) {
resp.setStatus(getErrorCode(e)); // no special handling needed
throw new WebdavException(WebdavStatus.SC_INTERNAL_SERVER_ERROR); // abort the TA
}
Vector propertiesParser = null;
switch (propFindType) {
case FIND_ALL_PROP :
// Show properties / values for current object.
generatedXML.writeElement(null, "propstat",
XMLPrinter.OPENING);
generatedXML.writeElement(null, "prop", XMLPrinter.OPENING);
status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
// Lock information, which is dynamically generated
if (objectLockToken != null) {
showLockDiscoveryInfo(objectLockToken, generatedXML);
}
// Supported locks
generatedXML.writeElement(null, "supportedlock",
XMLPrinter.OPENING);
generatedXML.writeText(SUPPORTED_LOCK);
generatedXML.writeElement(null, "supportedlock",
XMLPrinter.CLOSING);
// WebDAV ACL
if( org.apache.slide.util.Configuration.useIntegratedSecurity() ) {
showSupportedPrivilegeSet(generatedXML);
showCurrentUserPrivilegeSet(object, generatedXML);
showAcl(object, generatedXML);
showAclSemantics(generatedXML);
showPrincipalCollectionSet(generatedXML);
}
// Other properties
Enumeration propertyList =
revisionDescriptor.enumerateProperties();
while (propertyList.hasMoreElements()) {
NodeProperty currentProperty =
(NodeProperty) propertyList.nextElement();
if (currentProperty != null) {
String namespace = currentProperty.getNamespace();
generateNamespaceAbbreviation(namespace);
String namespaceAbbrev =
(String) namespaces.get(namespace);
Object currentPropertyValue = currentProperty.getValue();
if ((currentPropertyValue == null) ||
(currentPropertyValue.toString().equals(""))) {
generatedXML.writeElement
(namespaceAbbrev, namespace,
currentProperty.getName(),
XMLPrinter.NO_CONTENT);
} else {
generatedXML.writeElement
(namespaceAbbrev, namespace,
currentProperty.getName(),
XMLPrinter.OPENING);
generatedXML.writeText
(currentPropertyValue.toString());
generatedXML.writeElement
(namespaceAbbrev, namespace,
currentProperty.getName(),
XMLPrinter.CLOSING);
}
}
}
generatedXML.writeElement(null, "prop", XMLPrinter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat",
XMLPrinter.CLOSING);
break;
case FIND_PROPERTY_NAMES :
// Show properties for current object.
status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
generatedXML.writeElement(null, "propstat", XMLPrinter.OPENING);
generatedXML.writeElement(null, "prop", XMLPrinter.OPENING);
// Lock information
generatedXML.writeElement(null, "lockdiscovery",
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "supportedlock",
XMLPrinter.NO_CONTENT);
// WebDAV ACL
if( org.apache.slide.util.Configuration.useIntegratedSecurity() ) {
generatedXML.writeElement(null, "owner", XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "supported-privilege-set",
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "current-user-privilege-set",
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "acl",
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, ACL_SEMANTICS,
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, PRINCIPAL_COLLECTION_SET,
XMLPrinter.NO_CONTENT);
}
// Then, add this resource's custom properties
propertyList = revisionDescriptor.enumerateProperties();
while (propertyList.hasMoreElements()) {
NodeProperty currentProperty =
(NodeProperty) propertyList.nextElement();
if (currentProperty != null) {
String namespace = currentProperty.getNamespace();
generateNamespaceAbbreviation(namespace);
String namespaceAbbrev =
(String) namespaces.get(namespace);
generatedXML.writeElement
(namespaceAbbrev, namespace,
currentProperty.getName(),
XMLPrinter.NO_CONTENT);
}
}
generatedXML.writeElement(null, "prop", XMLPrinter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat",
XMLPrinter.CLOSING);
break;
case FIND_BY_PROPERTY :
// Show requested properties value.
propertyList = propertyVector.elements();
generatedXML.writeElement(null, "propstat",
XMLPrinter.OPENING);
generatedXML.writeElement(null, "prop", XMLPrinter.OPENING);
status = new String("HTTP/1.1 " + WebdavStatus.SC_OK
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_OK));
Vector propertiesNotFoundVector = new Vector();
while (propertyList.hasMoreElements()) {
Property property = (Property) propertyList.nextElement();
String propertyName = property.name;
String propertyNamespace = property.namespace;
// Lock information, dynamically generated
if ((propertyName.equals("lockdiscovery")) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
// Return list of active locks ...
if (objectLockToken != null) {
showLockDiscoveryInfo(objectLockToken, generatedXML);
}
} else if ((propertyName.equals("supportedlock")) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
// Supported locks
generatedXML.writeElement(null, "supportedlock",
XMLPrinter.OPENING);
generatedXML.writeText(SUPPORTED_LOCK);
generatedXML.writeElement(null, "supportedlock",
XMLPrinter.CLOSING);
} else if ((propertyName.equals(SUPPORTED_PRIVILEGE_SET)) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
// WebDAV ACL
if( org.apache.slide.util.Configuration.useIntegratedSecurity() )
showSupportedPrivilegeSet(generatedXML);
} else if ((propertyName.equals(CURRENT_USER_PRIVILEGE_SET)) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
if( org.apache.slide.util.Configuration.useIntegratedSecurity() )
showCurrentUserPrivilegeSet(object, generatedXML);
} else if ((propertyName.equals("acl")) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
if( org.apache.slide.util.Configuration.useIntegratedSecurity() )
showAcl(object, generatedXML);
} else if ((propertyName.equals(ACL_SEMANTICS)) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
if( org.apache.slide.util.Configuration.useIntegratedSecurity() )
showAclSemantics(generatedXML);
} else if ((propertyName.equals(PRINCIPAL_COLLECTION_SET)) &&
(propertyNamespace.equals
(NodeProperty.DEFAULT_NAMESPACE))) {
if( org.apache.slide.util.Configuration.useIntegratedSecurity() )
showPrincipalCollectionSet(generatedXML);
} else {
// Search in the custom properties
NodeProperty currentProperty =
revisionDescriptor.getProperty(propertyName,
propertyNamespace);
if (currentProperty != null) {
String namespace = currentProperty.getNamespace();
String namespaceAbbrev =
(String) namespaces.get(namespace);
Object currentPropertyValue =
currentProperty.getValue();
if ((currentPropertyValue == null) ||
(currentPropertyValue.toString().equals(""))) {
generatedXML.writeElement
(namespaceAbbrev, null,
currentProperty.getName(),
XMLPrinter.NO_CONTENT);
} else {
generatedXML.writeElement
(namespaceAbbrev, null,
currentProperty.getName(),
XMLPrinter.OPENING);
generatedXML.writeText
(currentPropertyValue.toString());
generatedXML.writeElement
(namespaceAbbrev, null,
currentProperty.getName(),
XMLPrinter.CLOSING);
}
} else {
propertiesNotFoundVector.addElement(property);
}
}
}
generatedXML.writeElement(null, "prop", XMLPrinter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat",
XMLPrinter.CLOSING);
Enumeration propertiesNotFoundList =
propertiesNotFoundVector.elements();
if (propertiesNotFoundList.hasMoreElements()) {
status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
+ " " + WebdavStatus.getStatusText
(WebdavStatus.SC_NOT_FOUND));
generatedXML.writeElement(null, "propstat",
XMLPrinter.OPENING);
generatedXML.writeElement(null, "prop",
XMLPrinter.OPENING);
while (propertiesNotFoundList.hasMoreElements()) {
Property propertyNotFound =
(Property) propertiesNotFoundList.nextElement();
String name = propertyNotFound.name;
String namespace = propertyNotFound.namespace;
String namespaceAbbrev =
(String) namespaces.get(namespace);
generatedXML.writeElement
(namespaceAbbrev, null, name, XMLPrinter.NO_CONTENT);
}
generatedXML.writeElement(null, "prop",
XMLPrinter.CLOSING);
generatedXML.writeProperty(null, "status", status);
generatedXML.writeElement(null, "propstat",
XMLPrinter.CLOSING);
}
break;
}
generatedXML.writeElement(null, "response", XMLPrinter.CLOSING);
}
// ------------------------------------------------------ Protected Methods
/**
* Generate a namespace abbreviation for the given namespace.
*
* @param namespaceName Name of the namespace
*/
protected void generateNamespaceAbbreviation(String namespaceName) {
if (namespaces.get(namespaceName) != null)
return;
if (namespaceName.equals(NodeProperty.DEFAULT_NAMESPACE))
return;
String namespaceAbbrev = "ns" + namespaceNumber++;
namespaces.put(namespaceName, namespaceAbbrev);
}
/**
* Generate namespace declaration attributes.
*
* @return String namespace attributes
*/
protected String generateNamespaceDeclarations() {
StringBuffer result = new StringBuffer();
result.append(" xmlns=\"").append(NodeProperty.DEFAULT_NAMESPACE)
.append("\" ");
Iterator namespaceList = namespaces.keySet().iterator();
while (namespaceList.hasNext()) {
String namespace = (String) namespaceList.next();
String abbrev = (String) namespaces.get(namespace);
result.append("xmlns:").append(abbrev).append("=\"")
.append(namespace).append("\" ");
}
return result.toString();
}
/**
* Show lockdiscovery info.
*
* @exception WebdavException Something is wrong with the servlet container
*/
protected void showLockDiscoveryInfo(NodeLock token,
XMLPrinter generatedXML)
throws WebdavException {
generatedXML.writeElement(null, "lockdiscovery", XMLPrinter.OPENING);
generatedXML.writeElement(null, "activelock", XMLPrinter.OPENING);
generatedXML.writeElement(null, "locktype", XMLPrinter.OPENING);
generatedXML.writeElement(null, "write", XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "locktype", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "lockscope", XMLPrinter.OPENING);
if (token.isExclusive()) {
generatedXML.writeElement(null, "exclusive",
XMLPrinter.NO_CONTENT);
} else {
generatedXML.writeElement(null, "shared", XMLPrinter.NO_CONTENT);
}
generatedXML.writeElement(null, "lockscope", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "depth", XMLPrinter.OPENING);
if (token.isInheritable()) {
generatedXML.writeText("Infinity");
} else {
generatedXML.writeText("0");
}
generatedXML.writeElement(null, "depth", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "owner", XMLPrinter.OPENING);
/* We need to write this as data (i.e. a CDATA section) because
* we don't know what the subjectUri (i.e. username) might
* contain. The webdav RFC leaves us completely free to
* put whatever we want inside the owner element.
*/
generatedXML.writeData(token.getSubjectUri());
generatedXML.writeElement(null, "owner", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "timeout", XMLPrinter.OPENING);
generatedXML.writeText("Second-"
+ (new Long((token.getExpirationDate().getTime()
- (new Date()).getTime())/1000))
.toString());
generatedXML.writeElement(null, "timeout", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "locktoken", XMLPrinter.OPENING);
generatedXML.writeElement(null, "href", XMLPrinter.OPENING);
// Put here the token Id
boolean isOwner = false;
try {
String subjectUri = security.getPrincipal(slideToken).getUri();
if (token.getSubjectUri().startsWith(subjectUri))
isOwner = true;
} catch (Exception e) {
}
if (isOwner || slideToken.checkLockToken(token.getLockId())) {
generatedXML.writeText("opaquelocktoken:" + token.getLockId());
} else {
generatedXML.writeText("opaquelocktoken:faketoken");
}
generatedXML.writeElement(null, "href", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "locktoken", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "activelock", XMLPrinter.CLOSING);
generatedXML.writeElement(null, "lockdiscovery", XMLPrinter.CLOSING);
}
/**
* WebDAV ACL DAV:supported-privilege-set property.
*/
protected void showSupportedPrivilegeSet(XMLPrinter generatedXML)
throws WebdavException {
generatedXML.writeElement(null, SUPPORTED_PRIVILEGE_SET,
XMLPrinter.OPENING);
// FIXME : Make the genaration dynamic (although it needs only to be
// generated once). I suggest waiting for the DAV ACL spec final
// version before implementing that.
generatedXML.writeText(SUPPORTED_PRIVILEGES);
generatedXML.writeElement(null, SUPPORTED_PRIVILEGE_SET,
XMLPrinter.CLOSING);
}
/**
* WebDAV ACL DAV:current-user-privilege-set property.
*/
protected void showCurrentUserPrivilegeSet
(ObjectNode object, XMLPrinter generatedXML)
throws WebdavException {
NamespaceConfig config = token.getNamespaceConfig();
SubjectNode principalNode = null;
try {
principalNode = (SubjectNode) security.getPrincipal(slideToken);
} catch (SlideException e) {
return;
} catch (ClassCastException e) {
return;
}
try {
boolean readObject =
security.hasPermission(object, principalNode,
config.getReadObjectAction());
boolean createObject =
security.hasPermission(object, principalNode,
config.getCreateObjectAction());
boolean removeObject =
security.hasPermission(object, principalNode,
config.getRemoveObjectAction());
boolean grantPermission =
security.hasPermission(object, principalNode,
config.getGrantPermissionAction());
boolean revokePermission =
security.hasPermission(object, principalNode,
config.getRevokePermissionAction());
boolean readPermissions =
security.hasPermission(object, principalNode,
config.getReadPermissionsAction());
boolean lockObject =
security.hasPermission(object, principalNode,
config.getLockObjectAction());
boolean killLock =
security.hasPermission(object, principalNode,
config.getKillLockAction());
boolean readLocks =
security.hasPermission(object, principalNode,
config.getReadLocksAction());
boolean readRevisionMetadata =
security.hasPermission(object, principalNode,
config.getReadRevisionMetadataAction());
boolean createRevisionMetadata = security.hasPermission
(object, principalNode,
config.getCreateRevisionMetadataAction());
boolean modifyRevisionMetadata = security.hasPermission
(object, principalNode,
config.getModifyRevisionMetadataAction());
boolean removeRevisionMetadata = security.hasPermission
(object, principalNode,
config.getRemoveRevisionMetadataAction());
boolean readRevisionContent =
security.hasPermission(object, principalNode,
config.getReadRevisionContentAction());
boolean createRevisionContent = security.hasPermission
(object, principalNode,
config.getCreateRevisionContentAction());
boolean modifyRevisionContent = security.hasPermission
(object, principalNode,
config.getModifyRevisionContentAction());
boolean removeRevisionContent = security.hasPermission
(object, principalNode,
config.getRemoveRevisionContentAction());
boolean canRead = readObject && readRevisionMetadata
&& readRevisionContent;
boolean canWrite = createObject && removeObject && lockObject
&& readLocks && createRevisionMetadata
&& modifyRevisionMetadata && removeRevisionMetadata
&& createRevisionContent && modifyRevisionContent
&& removeRevisionContent;
boolean canReadAcl = readPermissions;
boolean canWriteAcl = grantPermission && revokePermission;
boolean canAll = canRead && canWrite && canReadAcl && canWriteAcl
&& killLock;
generatedXML.writeElement(null, CURRENT_USER_PRIVILEGE_SET,
XMLPrinter.OPENING);
if (canRead) {
writePrivilege(generatedXML, "read", null);
}
if (canWrite) {
writePrivilege(generatedXML, "write", null);
}
if (canReadAcl) {
writePrivilege(generatedXML, "read-acl", null);
}
if (canWriteAcl) {
writePrivilege(generatedXML, "write-acl", null);
}
if (canAll) {
writePrivilege(generatedXML, "all", null);
}
generatedXML.writeElement(null, CURRENT_USER_PRIVILEGE_SET,
XMLPrinter.CLOSING);
} catch (SlideException e) {
return;
}
}
/**
* Write a privilege.
*/
protected void writePrivilege(XMLPrinter generatedXML, String privilege,
String namespace) {
generatedXML.writeElement(null, "privilege", XMLPrinter.OPENING);
generatedXML.writeElement(namespace, privilege, XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, "privilege", XMLPrinter.CLOSING);
}
/**
* WebDAV ACL DAV:acl property.
*/
protected void showAcl(ObjectNode object, XMLPrinter generatedXML)
throws WebdavException {
generatedXML.writeElement(null, ACL, XMLPrinter.OPENING);
NamespaceConfig config = token.getNamespaceConfig();
// read
String readObjectUri = config.getReadObjectAction().getUri();
String readRevisionMetadataUri =
config.getReadRevisionMetadataAction().getUri();
String readRevisionContentUri =
config.getReadRevisionContentAction().getUri();
// write
String createObjectUri = config.getCreateObjectAction().getUri();
String removeObjectUri = config.getRemoveObjectAction().getUri();
String lockObjectUri = config.getLockObjectAction().getUri();
String readLocksUri = config.getReadLocksAction().getUri();
String createRevisionMetadataUri =
config.getCreateRevisionMetadataAction().getUri();
String modifyRevisionMetadataUri =
config.getModifyRevisionMetadataAction().getUri();
String removeRevisionMetadataUri =
config.getRemoveRevisionMetadataAction().getUri();
String createRevisionContentUri =
config.getCreateRevisionContentAction().getUri();
String modifyRevisionContentUri =
config.getModifyRevisionContentAction().getUri();
String removeRevisionContentUri =
config.getRemoveRevisionContentAction().getUri();
// read-acl
String readPermissionsUri = config.getReadPermissionsAction().getUri();
// write-acl
String grantPermissionUri = config.getGrantPermissionAction().getUri();
String revokePermissionUri =
config.getRevokePermissionAction().getUri();
ObjectNode current = object;
boolean inheritedPermissions = false;
Vector permissions = new Vector();
while (current != null) {
try {
// put all permissions in a list
permissions.clear();
Enumeration aclList = security.enumeratePermissions(slideToken, current);
while (aclList.hasMoreElements()) {
NodePermission permission = (NodePermission) aclList.nextElement();
// if we are processing inheritedPermissions (from parent and up)
// then the permission should be inheritable
if (inheritedPermissions && !permission.isInheritable()) {
// continue with next permission
continue;
}
permissions.add(permission);
}
// start combining and writing the permissions
while (permissions.size()>0) {
NodePermission permission = (NodePermission) permissions.get(0);
permissions.remove(0);
String principal = permission.getSubjectUri();
boolean negative = permission.isNegative();
String action = permission.getActionUri();
// read
boolean isReadObject = readObjectUri.startsWith(action);
boolean isReadLocks = readLocksUri.startsWith(action);
boolean isReadRevisionMetadata = readRevisionMetadataUri.startsWith(action);
boolean isReadRevisionContent = readRevisionContentUri.startsWith(action);
// write
boolean isCreateObject = createObjectUri.startsWith(action);
boolean isRemoveObject = removeObjectUri.startsWith(action);
boolean isLockObject = lockObjectUri.startsWith(action);
boolean isCreateRevisionMetadata = createRevisionMetadataUri.startsWith(action);
boolean isModifyRevisionMetadata = modifyRevisionMetadataUri.startsWith(action);
boolean isRemoveRevisionMetadata = removeRevisionMetadataUri.startsWith(action);
boolean isCreateRevisionContent = createRevisionContentUri.startsWith(action);
boolean isModifyRevisionContent = modifyRevisionContentUri.startsWith(action);
boolean isRemoveRevisionContent = removeRevisionContentUri.startsWith(action);
//read-acl
boolean isReadPermissions = readPermissionsUri.startsWith(action);
// write-acl
boolean isGrantPermission = grantPermissionUri.startsWith(action);
boolean isRevokePermission = revokePermissionUri.startsWith(action);
// check the other permissions to combine them
// (if they are for the same principal/negative)
for (int i=0; i<permissions.size() ; i++)
{
NodePermission otherPermission = (NodePermission) permissions.get(i);
if (principal.equals(otherPermission.getSubjectUri()) && (negative==otherPermission.isNegative()))
{
permissions.remove(i);
i--; // because we removed the current one
action = otherPermission.getActionUri();
// read
isReadObject |= readObjectUri.startsWith(action);
isReadLocks |= readLocksUri.startsWith(action);
isReadRevisionMetadata |= readRevisionMetadataUri.startsWith(action);
isReadRevisionContent |= readRevisionContentUri.startsWith(action);
// write
isCreateObject |= createObjectUri.startsWith(action);
isRemoveObject |= removeObjectUri.startsWith(action);
isLockObject |= lockObjectUri.startsWith(action);
isCreateRevisionMetadata |= createRevisionMetadataUri.startsWith(action);
isModifyRevisionMetadata |= modifyRevisionMetadataUri.startsWith(action);
isRemoveRevisionMetadata |= removeRevisionMetadataUri.startsWith(action);
isCreateRevisionContent |= createRevisionContentUri.startsWith(action);
isModifyRevisionContent |= modifyRevisionContentUri.startsWith(action);
isRemoveRevisionContent |= removeRevisionContentUri.startsWith(action);
//read-acl
isReadPermissions |= readPermissionsUri.startsWith(action);
// write-acl
isGrantPermission |= grantPermissionUri.startsWith(action);
isRevokePermission |= revokePermissionUri.startsWith(action);
}
}
// WebDAV privileges
boolean isRead = isReadObject && isReadLocks &&
isReadRevisionMetadata && isReadRevisionContent;
boolean isWrite = isCreateObject && isRemoveObject && isLockObject &&
isCreateRevisionMetadata && isModifyRevisionMetadata &&
isRemoveRevisionMetadata &&
isCreateRevisionContent && isModifyRevisionContent &&
isRemoveRevisionContent;
boolean isReadAcl = isReadPermissions;
boolean isWriteAcl = isGrantPermission && isRevokePermission;
boolean isAll = isRead && isWrite && isReadAcl && isWriteAcl;
// start generating XML
generatedXML.writeElement(null, ACE, XMLPrinter.OPENING);
writePrincipal(generatedXML, principal);
if (permission.isNegative()) {
generatedXML.writeElement(null, DENY, XMLPrinter.OPENING);
} else {
generatedXML.writeElement(null, GRANT, XMLPrinter.OPENING);
}
if (isAll) {
writePrivilege(generatedXML, "all", true);
} else {
if (isRead) {
writePrivilege(generatedXML, "read", true);
} else {
if (isReadObject)
writePrivilege(generatedXML,
"read-object", false);
if (isReadLocks)
writePrivilege(generatedXML,
"read-locks", false);
if (isReadRevisionMetadata)
writePrivilege(generatedXML,
"read-revision-metadata",
false);
if (isReadRevisionContent)
writePrivilege(generatedXML,
"read-revision-content", false);
}
if (isWrite) {
writePrivilege(generatedXML, "write", true);
} else {
if (isCreateObject)
writePrivilege(generatedXML,
"create-object", false);
if (isRemoveObject)
writePrivilege(generatedXML,
"remove-object", false);
if (isLockObject)
writePrivilege(generatedXML,
"lock-object", false);
if (isCreateRevisionMetadata)
writePrivilege(generatedXML,
"create-revision-metadata",
false);
if (isModifyRevisionMetadata)
writePrivilege(generatedXML,
"modify-revision-metadata",
false);
if (isRemoveRevisionMetadata)
writePrivilege(generatedXML,
"remove-revision-metadata",
false);
if (isCreateRevisionContent)
writePrivilege(generatedXML,
"create-revision-content",
false);
if (isModifyRevisionContent)
writePrivilege(generatedXML,
"modify-revision-content",
false);
if (isRemoveRevisionContent)
writePrivilege(generatedXML,
"remove-revision-content",
false);
}
if (isReadAcl) {
writePrivilege(generatedXML, "read-acl", true);
}
if (isWriteAcl) {
writePrivilege(generatedXML, "write-acl", true);
} else {
if (isGrantPermission)
writePrivilege(generatedXML,
"grant-permission", false);
if (isRevokePermission)
writePrivilege(generatedXML,
"revoke-permission", false);
}
}
if (permission.isNegative()) {
generatedXML.writeElement(null, DENY,
XMLPrinter.CLOSING);
} else {
generatedXML.writeElement(null, GRANT,
XMLPrinter.CLOSING);
}
if (inheritedPermissions) {
generatedXML.writeElement(null, "protected",
XMLPrinter.NO_CONTENT);
generatedXML.writeElement(null, INHERITED,
XMLPrinter.OPENING);
generatedXML.writeElement(null, "href",
XMLPrinter.OPENING);
generatedXML.writeText(getFullPath(current.getUri()));
generatedXML.writeElement(null, "href",
XMLPrinter.CLOSING);
generatedXML.writeElement(null, INHERITED,
XMLPrinter.CLOSING);
}
generatedXML.writeElement(null, ACE, XMLPrinter.CLOSING);
}
} catch (SlideException e) {
}
inheritedPermissions = true;
try {
current = structure.getParent(slideToken, current);
} catch (SlideException e) {
break;
}
}
generatedXML.writeElement(null, ACL, XMLPrinter.CLOSING);
}
/**
* Write principal.
*/
protected void writePrincipal(XMLPrinter generatedXML, String principal) {
generatedXML.writeElement(null, PRINCIPAL, XMLPrinter.OPENING);
// FIXME: Apparently, there are or will be some other cases, but it
// isn't very clear in the spec
if (principal.equals("~")) {
generatedXML.writeElement(null, "self", XMLPrinter.NO_CONTENT);
} if (principal.equals("nobody")) {
generatedXML.writeElement(null, "all",
XMLPrinter.NO_CONTENT);
} else {
generatedXML.writeElement(null, "href", XMLPrinter.OPENING);
generatedXML.writeText(getFullPath(principal));
generatedXML.writeElement(null, "href", XMLPrinter.CLOSING);
}
generatedXML.writeElement(null, PRINCIPAL, XMLPrinter.CLOSING);
}
/**
* Write grant / deny.
*/
protected void writePrivilege(XMLPrinter generatedXML, String name,
boolean standard) {
generatedXML.writeElement(null, PRIVILEGE, XMLPrinter.OPENING);
if (standard) {
generatedXML.writeElement(null, name, XMLPrinter.NO_CONTENT);
} else {
generatedXML.writeElement(SLIDE_NAMESPACE_ABBREV, name,
XMLPrinter.NO_CONTENT);
}
generatedXML.writeElement(null, PRIVILEGE, XMLPrinter.CLOSING);
}
/**
* WebDAV ACL DAV:acl-semantics property.
*/
protected void showAclSemantics(XMLPrinter generatedXML) {
generatedXML.writeElement(null, ACL_SEMANTICS, XMLPrinter.OPENING);
generatedXML.writeText(SLIDE_ACL_SEMANTICS);
generatedXML.writeElement(null, ACL_SEMANTICS, XMLPrinter.CLOSING);
}
/**
* WebDAV ACL DAV:principal-collection-set property.
*/
protected void showPrincipalCollectionSet(XMLPrinter generatedXML) {
NamespaceConfig config = token.getNamespaceConfig();
generatedXML.writeElement(null, PRINCIPAL_COLLECTION_SET,
XMLPrinter.OPENING);
generatedXML.writeElement(null, "href", XMLPrinter.OPENING);
generatedXML.writeText(getFullPath(config.getUsersPath()));
generatedXML.writeElement(null, "href", XMLPrinter.CLOSING);
generatedXML.writeElement(null, PRINCIPAL_COLLECTION_SET,
XMLPrinter.CLOSING);
}
// --------------------------------------------------- Property Inner Class
protected class Property {
public String name;
public String value;
public String namespace;
public String namespaceAbbrev;
public int status = WebdavStatus.SC_OK;
}
}