/*
 * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/properties/AclProperty.java,v 1.4 2005/02/03 21:03:32 ozeigermann Exp $
 * $Revision: 1.4 $
 * $Date: 2005/02/03 21:03:32 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 The Apache Software Foundation 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.apache.slide.webdav.util.properties;

import java.util.Enumeration;

import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.PropertyName;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.Uri;
import org.apache.slide.common.UriPath;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.event.VetoException;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.security.NodePermission;
import org.apache.slide.security.Security;
import org.apache.slide.store.Store;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.LinkedObjectNotFoundException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.Structure;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.XMLValue;
import org.apache.slide.webdav.util.AclConstants;
import org.apache.slide.webdav.util.WebdavConstants;
import org.apache.slide.webdav.util.WebdavContext;
import org.apache.slide.webdav.util.resourcekind.ResourceKind;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.apache.slide.content.NodeProperty.NamespaceCache;

/**
 * Computes the ACL <code>acl</code> property.
 */
public class AclProperty extends AbstractComputedProperty
{
    public PropertyName getPropertyName() {
        return AclConstants.PN_ACL;
    }
    
    public Object computeValue(
            NamespaceAccessToken nsaToken,
            NodeRevisionDescriptors revisionDescriptors, 
            NodeRevisionDescriptor revisionDescriptor, 
            ResourceKind resourceKind, WebdavContext context)
        throws RevisionDescriptorNotFoundException, 
            ServiceAccessException, LinkedObjectNotFoundException, 
            AccessDeniedException, ObjectNotFoundException, 
            VetoException 
    {
        XMLValue xmlValue = new XMLValue();
        
        NamespaceConfig config = nsaToken.getNamespaceConfig();
        Structure structure = nsaToken.getStructureHelper();
        ObjectNode objectNode =  structure.retrieve(context.getSlideToken(), 
                revisionDescriptors.getUri());
        
        // check read-acl permission
        Security security = nsaToken.getSecurityHelper();
        security.checkCredentials(context.getSlideToken(), objectNode, 
                config.getReadPermissionsAction());
        
        String currentSubjectUri = "";
        boolean currentNegative = false;
        String currentInheritedFrom = "";
        Element currentAceElm = null;
        Element currentGrantDenyElm = null;
        Enumeration permissions = security.enumeratePermissions(
                context.getSlideToken(), objectNode, true);
        while (permissions.hasMoreElements()) {
            NodePermission perm = (NodePermission)permissions.nextElement();
            if (!perm.getSubjectUri().equals(currentSubjectUri)
                || (perm.isNegative() && !currentNegative || !perm.isNegative() && currentNegative)
                || (!currentInheritedFrom.equals(perm.getInheritedFrom()))) {
                
                // save previous ACE
                if (currentAceElm != null) {
                    xmlValue.add(currentAceElm);
                }
                
                // new ACE
                currentAceElm = new Element(AclConstants.E_ACE, DNSP);
                
                // principal
                if (perm.isInvert()) {
                    Element invertElm = new Element(AclConstants.E_INVERT, DNSP);
                    invertElm.addContent(createPrincipalElement(perm.getSubjectUri()));
                    currentAceElm.addContent(invertElm);
                }
                else {
                    currentAceElm.addContent(createPrincipalElement(perm.getSubjectUri()));
                }
                
                // grant/deny
                currentGrantDenyElm = perm.isNegative()
                    ? new Element(AclConstants.E_DENY, DNSP)
                    : new Element(AclConstants.E_GRANT, DNSP);
                currentAceElm.addContent(currentGrantDenyElm);
                
                // inherited
                String inheritedFrom = perm.getInheritedFrom();
                if (inheritedFrom != null && inheritedFrom.length() > 0) {
                    currentAceElm.addContent(createInheritedElement(inheritedFrom));
                }

                // inheritable
                if (perm.isInheritable() == false) {
                    currentAceElm.addContent(new Element("non-inheritable",
                            NamespaceCache.SLIDE_NAMESPACE));
                }
                
                // protected
                if (perm.isProtected()) {
                    currentAceElm.addContent(new Element(AclConstants.E_PROTECTED, 
                            DNSP));
                }
                
                currentSubjectUri = perm.getSubjectUri();
                currentNegative = perm.isNegative();
                currentInheritedFrom = inheritedFrom != null ? inheritedFrom : "";
            }
            currentGrantDenyElm.addContent(createPrivilegeElement(nsaToken, 
                    context.getSlideToken(), perm.getActionUri()));
        }
        
        // save last ACE
        if (currentAceElm != null) {
            xmlValue.add(currentAceElm);
        }
        
        return xmlValue;
    }
    
    
    
    private Element createPrincipalElement(String subjectUri) {
        Element principalElm = new Element(AclConstants.E_PRINCIPAL, DNSP);
        if (subjectUri.equals(SubjectNode.ALL_URI)) {
            Element allElm = new Element(AclConstants.E_ALL, DNSP);
            principalElm.addContent(allElm);
        }
        else if (subjectUri.equals(SubjectNode.AUTHENTICATED_URI)) {
            Element authenticatedElm = new Element(AclConstants.E_AUTHENTICATED, DNSP);
            principalElm.addContent(authenticatedElm);
        }
        else if (subjectUri.equals(SubjectNode.UNAUTHENTICATED_URI)) {
            Element unauthenticatedElm = new Element(AclConstants.E_UNAUTHENTICATED, DNSP);
            principalElm.addContent(unauthenticatedElm);
        }
        else if (subjectUri.equals(SubjectNode.OWNER_URI)) {
            Element propertyElm = new Element(AclConstants.E_PROPERTY, DNSP);
            propertyElm.addContent(new Element(AclConstants.P_OWNER, DNSP));
            principalElm.addContent(propertyElm);
        }
        else if (subjectUri.equals(SubjectNode.SELF_URI)) {
            Element selfElm = new Element(AclConstants.E_SELF, DNSP);
            principalElm.addContent(selfElm);
        }
        else {
            Element hrefElm = new Element(AclConstants.E_HREF, DNSP);
            hrefElm.addContent(new Text(subjectUri));
            principalElm.addContent(hrefElm);
        }
        return principalElm;
    }
    
    private Element createPrivilegeElement(NamespaceAccessToken nsaToken, 
            SlideToken sToken, String actionUriAsString) 
        throws RevisionDescriptorNotFoundException, ServiceAccessException 
    {
        Element privilegeElm = new Element(AclConstants.E_PRIVILEGE, DNSP);
        if (actionUriAsString.equals(ActionNode.ALL_URI)) {
            Element allElm = new Element(AclConstants.E_ALL, DNSP);
            privilegeElm.addContent(allElm);
        }
        else {
            Uri actionUri = nsaToken.getUri(sToken, actionUriAsString);
            Store store = actionUri.getStore();
            NodeRevisionNumber latestRevisionNumber = store.retrieveRevisionDescriptors(actionUri).getLatestRevision();
            NodeProperty privilegeNamespace = store.retrieveRevisionDescriptor(actionUri, latestRevisionNumber).getProperty(AclConstants.PN_PRIVILEGE_NAMESPACE);
            Namespace actionNamespace = null;
            if (privilegeNamespace != null && privilegeNamespace.getValue() instanceof String) {
                actionNamespace = Namespace.getNamespace((String) privilegeNamespace.getValue());
            }
            else {
                actionNamespace = DNSP;
            }

            Element actionElm = new Element(UriPath.getLastSegment(actionUriAsString), actionNamespace);
            privilegeElm.addContent(actionElm);
        }
        return privilegeElm;
    }
    
    private Element createInheritedElement(String inheritedFrom) {
        Element inheritedElm = new Element(AclConstants.E_INHERITED, DNSP);
        Element hrefElm = new Element(WebdavConstants.E_HREF, DNSP);
        hrefElm.addContent(new Text(inheritedFrom));
        inheritedElm.addContent(hrefElm);
        return inheritedElm;
    }
}
