# -*- coding: UTF-8 -*-


# Expression
# By: Frederic Peters <fpeters@entrouvert.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


"""WebDAV Module"""


import libxml2

import elements
import faults
import namespaces
import logs


class Multistatus(elements.Element):
    pass


class Prop(elements.Element):
    def generateWebDavResponse(self, specimen, multistatus, depth):
        multistatusNode = multistatus.node
        responseNode = multistatusNode.newTextChild(None, "response", None)
        # The href URI is quoted, because Nautilus requires that the returned href is the same
        # as the URL in the PROPFIND command. Otherwise it can not open "untitled folder", because
        # it expects "untitled%20folder".
        responseNode.newTextChild(None, "href", specimen.getAbsoluteUri(quote = True))

        def addForbiddenPropNodeChild(responseNode, propNode, node):
            if propNode is None:
                propstatNode = responseNode.newTextChild(None, "propstat", None)
                propNode = propstatNode.newTextChild(None, "prop", None)
                propstatNode.newTextChild(None, "status", "HTTP/1.1 403 Access Forbidden")
            try:
                nodeNamespaceUri = node.ns().content
            except libxml2.treeError:
                # The libxml2 Python binding raises this exception when
                # xmlNs returns None.
                raise Exception("Missing namespace")
            if nodeNamespaceUri == namespaces.dav.uri:
                nodeNamespace = None
                useDefaultNamespace = True
            else:
                try:
                    nodeNamespace = propNode.searchNsByHref(responseNode.doc, nodeNamespaceUri)
                except libxml2.treeError:
                    # The libxml2 Python binding raises this exception when
                    # xmlSearchNsByHref returns None.
                    try:
                        nodeNamespacePrefix = namespaces.getName(nodeNamespaceUri)
                    except KeyError:
                        nodeNamespace = None
                        useDefaultNamespace = False
                    else:
                        nodeNamespace = propNode.newNs(nodeNamespaceUri, nodeNamespacePrefix)
            child = propNode.newTextChild(nodeNamespace, node.name, None)
            if nodeNamespace is None and not useDefaultNamespace:
                nodeNamespace = child.newNs(nodeNamespaceUri, node.ns().name)
                child.setNs(nodeNamespace)
            return propNode

        def addNotFoundPropNodeChild(responseNode, propNode, node):
            if propNode is None:
                propstatNode = responseNode.newTextChild(None, "propstat", None)
                propNode = propstatNode.newTextChild(None, "prop", None)
                propstatNode.newTextChild(None, "status", "HTTP/1.1 404 Not Found")
            try:
                nodeNamespaceUri = node.ns().content
            except libxml2.treeError:
                # The libxml2 Python binding raises this exception when xmlNs returns None.
                raise Exception("Missing namespace")
            if nodeNamespaceUri == namespaces.dav.uri:
                nodeNamespace = None
                useDefaultNamespace = True
            else:
                try:
                    nodeNamespace = propNode.searchNsByHref(responseNode.doc, nodeNamespaceUri)
                except libxml2.treeError:
                    # The libxml2 Python binding raises this exception when
                    # xmlSearchNsByHref returns None.
                    try:
                        nodeNamespacePrefix = namespaces.getName(nodeNamespaceUri)
                    except KeyError:
                        nodeNamespace = None
                        useDefaultNamespace = False
                    else:
                        nodeNamespace = propNode.newNs(nodeNamespaceUri, nodeNamespacePrefix)
            child = propNode.newTextChild(nodeNamespace, node.name, None)
            if nodeNamespace is None and not useDefaultNamespace:
                nodeNamespace = child.newNs(nodeNamespaceUri, node.ns().name)
                child.setNs(nodeNamespace)
            return propNode

        def addOkPropNodeChild(responseNode, propNode, node, nodeContent = None):
            if propNode is None:
                propstatNode = responseNode.newTextChild(None, "propstat", None)
                propNode = propstatNode.newTextChild(None, "prop", None)
                propstatNode.newTextChild(None, "status", "HTTP/1.1 200 OK")
            try:
                nodeNamespaceUri = node.ns().content
            except libxml2.treeError:
                # The libxml2 Python binding raises this exception when
                # xmlNs returns None.
                raise Exception("Missing namespace")
            if nodeNamespaceUri == namespaces.dav.uri:
                nodeNamespace = None
                useDefaultNamespace = True
            else:
                try:
                    nodeNamespace = propNode.searchNsByHref(responseNode.doc, nodeNamespaceUri)
                except libxml2.treeError:
                    # The libxml2 Python binding raises this exception when
                    # xmlSearchNsByHref returns None.
                    try:
                        nodeNamespacePrefix = namespaces.getName(nodeNamespaceUri)
                    except KeyError:
                        nodeNamespace = None
                        useDefaultNamespace = False
                    else:
                        nodeNamespace = propNode.newNs(nodeNamespaceUri, nodeNamespacePrefix)
            child = propNode.newTextChild(nodeNamespace, node.name, nodeContent)
            if nodeNamespace is None and not useDefaultNamespace:
                nodeNamespace = child.newNs(nodeNamespaceUri, node.ns().name)
                child.setNs(nodeNamespace)
            return propNode, child

        def addUnauthorizedPropNodeChild(responseNode, propNode, node):
            if propNode is None:
                propstatNode = responseNode.newTextChild(None, "propstat", None)
                propNode = propstatNode.newTextChild(None, "prop", None)
                propstatNode.newTextChild(None, "status", "HTTP/1.1 401 Access Unauthorized")
            try:
                nodeNamespaceUri = node.ns().content
            except libxml2.treeError:
                # The libxml2 Python binding raises this exception when
                # xmlNs returns None.
                raise Exception("Missing namespace")
            if nodeNamespaceUri == namespaces.dav.uri:
                nodeNamespace = None
                useDefaultNamespace = True
            else:
                try:
                    nodeNamespace = propNode.searchNsByHref(responseNode.doc, nodeNamespaceUri)
                except libxml2.treeError:
                    # The libxml2 Python binding raises this exception when
                    # xmlSearchNsByHref returns None.
                    try:
                        nodeNamespacePrefix = namespaces.getName(nodeNamespaceUri)
                    except KeyError:
                        nodeNamespace = None
                        useDefaultNamespace = False
                    else:
                        nodeNamespace = propNode.newNs(nodeNamespaceUri, nodeNamespacePrefix)
            child = propNode.newTextChild(nodeNamespace, node.name, None)
            if nodeNamespace is None and not useDefaultNamespace:
                nodeNamespace = child.newNs(nodeNamespaceUri, node.ns().name)
                child.setNs(nodeNamespace)
            return propNode

        okPropNode = None
        forbiddenPropNode = None
        notFoundPropNode = None
        unauthorizedPropNode = None
        nodes = self.evaluateXpath("*")
        for node in nodes:
            nodeName = node.name
            try:
                nodeNamespaceUri = node.ns().content
            except libxml2.treeError:
                # The libxml2 Python binding raises this exception when
                # xmlNs returns None.
                nodeNamespaceUri = None
            if nodeNamespaceUri == namespaces.dav.uri:
                if nodeName == "creationdate":
                    try:
                        creationDate = specimen.getWebDavCreationDate()
                    except faults.PathForbidden:
                        forbiddenPropNode = addForbiddenPropNodeChild(
                            responseNode, forbiddenPropNode, node)
                    except faults.PathNotFound:
                        notFoundPropNode = addNotFoundPropNodeChild(
                            responseNode, notFoundPropNode, node)
                    except faults.PathUnauthorized:
                        unauthorizedPropNode = addUnauthorizedPropNodeChild(
                            responseNode, unauthorizedPropNode, node)
                    else:
                        if creationDate is None:
                            notFoundPropNode = addNotFoundPropNodeChild(
                                responseNode, notFoundPropNode, node)
                        else:
                            okPropNode, node = addOkPropNodeChild(
                                responseNode, okPropNode, node, creationDate)
                elif nodeName == "getcontentlength":
                    try:
                        contentLength = specimen.getWebDavContentLength()
                    except faults.PathForbidden:
                        forbiddenPropNode = addForbiddenPropNodeChild(
                            responseNode, forbiddenPropNode, node)
                    except faults.PathNotFound:
                        notFoundPropNode = addNotFoundPropNodeChild(
                            responseNode, notFoundPropNode, node)
                    except faults.PathUnauthorized:
                        unauthorizedPropNode = addUnauthorizedPropNodeChild(
                            responseNode, unauthorizedPropNode, node)
                    else:
                        if contentLength is None:
                            notFoundPropNode = addNotFoundPropNodeChild(
                                responseNode, notFoundPropNode, node)
                        else:
                            okPropNode, node = addOkPropNodeChild(
                                responseNode, okPropNode, node,
                                str(contentLength))
                elif nodeName == "getcontenttype":
                    try:
                        contentType = specimen.getWebDavContentType()
                    except faults.PathForbidden:
                        forbiddenPropNode = addForbiddenPropNodeChild(
                            responseNode, forbiddenPropNode, node)
                    except faults.PathNotFound:
                        notFoundPropNode = addNotFoundPropNodeChild(
                            responseNode, notFoundPropNode, node)
                    except faults.PathUnauthorized:
                        unauthorizedPropNode = addUnauthorizedPropNodeChild(
                            responseNode, unauthorizedPropNode, node)
                    else:
                        if contentType is None:
                            notFoundPropNode = addNotFoundPropNodeChild(
                                responseNode, notFoundPropNode, node)
                        else:
                            okPropNode, node = addOkPropNodeChild(
                                responseNode, okPropNode, node, contentType)
                elif nodeName == "getlastmodified":
                    try:
                        lastModified = specimen.getWebDavLastModified()
                    except faults.PathForbidden:
                        forbiddenPropNode = addForbiddenPropNodeChild(
                            responseNode, forbiddenPropNode, node)
                    except faults.PathNotFound:
                        notFoundPropNode = addNotFoundPropNodeChild(
                            responseNode, notFoundPropNode, node)
                    except faults.PathUnauthorized:
                        unauthorizedPropNode = addUnauthorizedPropNodeChild(
                            responseNode, unauthorizedPropNode, node)
                    else:
                        if lastModified is None:
                            notFoundPropNode = addNotFoundPropNodeChild(
                                responseNode, notFoundPropNode, node)
                        else:
                            okPropNode, node = addOkPropNodeChild(
                                responseNode, okPropNode, node, lastModified)
                elif nodeName == "resourcetype":
                    try:
                        isCollection = specimen.isWebDavCollection()
                    except faults.PathForbidden:
                        forbiddenPropNode = addForbiddenPropNodeChild(
                            responseNode, forbiddenPropNode, node)
                    except faults.PathNotFound:
                        notFoundPropNode = addNotFoundPropNodeChild(
                            responseNode, notFoundPropNode, node)
                    except faults.PathUnauthorized:
                        unauthorizedPropNode = addUnauthorizedPropNodeChild(
                            responseNode, unauthorizedPropNode, node)
                    else:
                        okPropNode, node = addOkPropNodeChild(
                            responseNode, okPropNode, node)
                        if isCollection:
                            node.newTextChild(None, "collection", None)
                else:
                    notFoundPropNode = addNotFoundPropNodeChild(
                        responseNode, notFoundPropNode, node)
            else:
                notFoundPropNode = addNotFoundPropNodeChild(
                    responseNode, notFoundPropNode, node)

        if depth != 0:
            try:
                isCollection = specimen.isWebDavCollection()
            except (faults.PathForbidden, faults.PathNotFound, faults.PathUnauthorized):
                isCollection = False
            if isCollection:
                if depth != "infinity":
                    depth -= 1
                for item in specimen.getWebDavCollectionItems():
                    logs.info('item: %r' % item)
                    self.generateWebDavResponse(item, multistatus, depth)
            

class Propfind(elements.Element):
    def generateWebDavResponse(self, specimen, multistatus, depth):
        prop = self.prop
        if self.prop is not None:
            self.prop.generateWebDavResponse(specimen, multistatus, depth)

    def getProp(self):
        propNodes = self.evaluateXpath("dav:prop")
        if not propNodes:
            return None
        return elements.newElement(propNodes[0], previous = self, owner = self)

    prop = property(getProp)


elements.registerElement(namespaces.dav.uri, "multistatus", Multistatus)
elements.registerElement(namespaces.dav.uri, "prop", Prop)
elements.registerElement(namespaces.dav.uri, "propfind", Propfind)
