Example #1
0
    def _createConnection(self):
        """ Overwrites template method for connection creation. """

        protocol = self._configuration.protocol
        hostname = self._configuration.hostname
        port = self._configuration.port
        connection = Connection(hostname, port, protocol=protocol)
        baseCollection = CollectionStorer(self._configuration.basePath, connection)
        try:
            try:
                baseCollection.validate()
            except AuthorizationError, error:
                username = self._configuration.username or ""
                password = self._configuration.password or ""
                if error.authType == "Basic":
                    realm = re.search('realm="([^"]+)"', error.authInfo)
                    if not realm is None:
                        realm = realm.group(1)
                    connection.addBasicAuthorization(username, password, realm)
                elif error.authType == "Digest":
                    authInfo = parseDigestAuthInfo(error.authInfo)
                    connection.addDigestAuthorization(username, password, 
                                                      realm=authInfo["realm"], qop=authInfo["qop"], nonce=authInfo["nonce"])
                else:
                    raise PersistenceError("Cannot create connection. Authentication type '%s' is not supported.")
        except (AttributeError, WebdavError), error:
            errorMessage = "Cannot create connection.\nReason:'%s'" % error.reason
            raise PersistenceError(errorMessage)
Example #2
0
    def __init__(self, url, connection=None, validateResourceNames=True):
        """
        Creates an instance for the given URL
        User must invoke validate() after construction to check the resource on the server.
        
        @param url: Unique resource location for this storer.
        @type  url: C{string}
        @param connection: this optional parameter contains a Connection object 
            for the host part of the given URL. Passing a connection saves 
            memory by sharing this connection. (defaults to None)
        @type  connection: L{webdav.Connection}
        @raise WebdavError: If validation of resource name path parts fails.
        """

        assert connection == None or isinstance(connection, Connection)
        parts = urlsplit(url, allow_fragments=False)
        self.path = parts[2]
        self.validateResourceNames = validateResourceNames

        # validate URL path
        for part in self.path.split('/'):
            if part != '' and not "ino:" in part:  # explicitly allowing this character sequence as a part of a path (Tamino 4.4)
                if self.validateResourceNames:
                    try:
                        validateResourceName(part)
                    except WrongNameError:
                        raise WebdavError("Found invalid resource name part.")
                self.name = part
        # was: filter(lambda part: part and validateResourceName(part), self.path.split('/'))
        # but filter is deprecated

        self.defaultNamespace = None  # default XML name space of properties
        if connection:
            self.connection = connection
        else:
            conn = parts[1].split(":")
            if len(conn) == 1:
                self.connection = Connection(
                    conn[0], protocol=parts[0])  # host and protocol
            else:
                self.connection = Connection(
                    conn[0], int(conn[1]),
                    protocol=parts[0])  # host and port and protocol
        self.versionHandler = VersionHandler(self.connection, self.path)
Example #3
0
    def __init__(self, url, connection=None, validateResourceNames=True):
        """
        Creates an instance for the given URL
        User must invoke validate() after construction to check the resource on the server.

        @param url: Unique resource location for this storer.
        @type  url: C{string}
        @param connection: this optional parameter contains a Connection object
            for the host part of the given URL. Passing a connection saves
            memory by sharing this connection. (defaults to None)
        @type  connection: L{webdav.Connection}
        @raise WebdavError: If validation of resource name path parts fails.
        """

        assert connection == None or isinstance(connection, Connection)
        parts = urlsplit(url, allow_fragments=False)
        self.path = parts[2]
        self.validateResourceNames = validateResourceNames

        # validate URL path
        for part in self.path.split('/'):
            if part != '' and not "ino:" in part: # explicitly allowing this character sequence as a part of a path (Tamino 4.4)
                if self.validateResourceNames:
                    try:
                        validateResourceName(part)
                    except WrongNameError:
                        raise WebdavError("Found invalid resource name part.")
                self.name = part
        # was: filter(lambda part: part and validateResourceName(part), self.path.split('/'))
        # but filter is deprecated

        self.defaultNamespace = None     # default XML name space of properties
        if connection:
            self.connection = connection
        else:
            conn = parts[1].split(":")
            if len(conn) == 1:
                self.connection = Connection(
                    conn[0], protocol=parts[0])  # host and protocol
            else:
                self.connection = Connection(
                    conn[0], int(conn[1]), protocol=parts[0])  # host and port and protocol
        self.versionHandler = VersionHandler(self.connection, self.path)
Example #4
0
    def _createConnection(self):
        """ Overwrites template method for connection creation. """

        protocol = self._configuration.protocol
        hostname = self._configuration.hostname
        port = self._configuration.port
        connection = Connection(hostname, port, protocol=protocol)
        baseCollection = CollectionStorer(self._configuration.basePath,
                                          connection)
        try:
            try:
                baseCollection.validate()
            except AuthorizationError, error:
                username = self._configuration.username or ""
                password = self._configuration.password or ""
                if error.authType == "Basic":
                    realm = re.search('realm="([^"]+)"', error.authInfo)
                    if not realm is None:
                        realm = realm.group(1)
                    connection.addBasicAuthorization(username, password, realm)
                elif error.authType == "Digest":
                    authInfo = parseDigestAuthInfo(error.authInfo)
                    connection.addDigestAuthorization(username,
                                                      password,
                                                      realm=authInfo["realm"],
                                                      qop=authInfo["qop"],
                                                      nonce=authInfo["nonce"])
                else:
                    raise PersistenceError(
                        "Cannot create connection. Authentication type '%s' is not supported."
                    )
        except (AttributeError, WebdavError), error:
            errorMessage = "Cannot create connection.\nReason:'%s'" % error.reason
            raise PersistenceError(errorMessage)
Example #5
0
class ResourceStorer(object):
    """
    This class provides client access to a WebDAV resource
    identified by an URI. It provides all WebDAV class 2 features which include
    uploading data, getting and setting properties qualified by a XML name space,
    locking and unlocking the resource. 
    This class does not cache resource data. This has to be performed by its clients.
    
    @author: Roland Betz
    """
        
    # Instance properties
    url = property(lambda self: str(self.connection) + self.path, None, None, "Resource's URL")

    def __init__(self, url, connection=None, validateResourceNames=True):
        """
        Creates an instance for the given URL
        User must invoke validate() after construction to check the resource on the server.
        
        @param url: Unique resource location for this storer.
        @type  url: C{string}
        @param connection: this optional parameter contains a Connection object 
            for the host part of the given URL. Passing a connection saves 
            memory by sharing this connection. (defaults to None)
        @type  connection: L{webdav.Connection}
        @raise WebdavError: If validation of resource name path parts fails.
        """

        assert connection == None or isinstance(connection, Connection)
        parts = urlsplit(url, allow_fragments=False)
        self.path = parts[2]
        self.validateResourceNames = validateResourceNames
        
        # validate URL path
        for part in self.path.split('/'):
            if part != '' and not "ino:" in part: # explicitly allowing this character sequence as a part of a path (Tamino 4.4)
                if self.validateResourceNames:
                    try:
                        validateResourceName(part)
                    except WrongNameError:
                        raise WebdavError("Found invalid resource name part.")
                self.name = part
        # was: filter(lambda part: part and validateResourceName(part), self.path.split('/'))
        # but filter is deprecated
        
        self.defaultNamespace = None     # default XML name space of properties
        if connection:
            self.connection = connection
        else:
            conn = parts[1].split(":")
            if len(conn) == 1:
                self.connection = Connection(conn[0], protocol = parts[0])  # host and protocol
            else:
                self.connection = Connection(conn[0], int(conn[1]), protocol = parts[0])  # host and port and protocol
        self.versionHandler = VersionHandler(self.connection, self.path)
        

    def validate(self):
        """
        Check whether URL contains a WebDAV resource
        Uses the WebDAV OPTIONS method.
        
        @raise WebdavError: L{WebdavError} if URL does not contain a WebDAV resource
        """
        #davHeader = response.getheader(HTTP_HEADER_DAV)
        davHeader = self.getSpecificOption(Constants.HTTP_HEADER_DAV)
        self.connection.logger.debug("HEADER DAV: %s" % davHeader)
        if not(davHeader) or davHeader.find("2") < 0:   # DAV class 2 supported ?
            raise WebdavError("URL does not support WebDAV", 0)

    def options(self):
        """
        Send an OPTIONS request to server and return all HTTP headers.
        
        @return: map of all HTTP headers returned by the OPTIONS method.
        """
        response = self.connection.options(self.path)
        result = {}
        result.update(response.msg)
        self.connection.logger.debug("OPTION returns: " + str(result.keys()))
        return result
        
    def _getAclSupportAvailable(self):
        """
        Returns True if the current connection has got ACL support.
        
        @return: ACL support (True / False)
        @rtype: C{bool}
        """
        options = self.getSpecificOption(Constants.HTTP_HEADER_DAV)
        if options.find(Constants.HTTP_HEADER_OPTION_ACL) >= 0:
            return True
        else:
            return False
        
    aclSupportAvailable = property(_getAclSupportAvailable)
        
    def _getDaslBasicsearchSupportAvailable(self):
        """
        Returns True if the current connection supports DASL basic search.
        
        @return: DASL basic search support (True / False)
        @rtype: C{bool}
        """
        options = self.getSpecificOption(Constants.HTTP_HEADER_DASL)
        if not options or \
           not options.find(Constants.HTTP_HEADER_OPTION_DAV_BASIC_SEARCH) >= 0:
            return False
        else:
            return True
    
    daslBasicsearchSupportAvailable = property(_getDaslBasicsearchSupportAvailable)
    
    def isConnectedToCatacombServer(self):
        """
        Returns True if connected to a Catacomb WebDav server.
        
        @return: if connected to Catacomb Webdav server (True / False)
        @rtype: C{bool}
        """
        if not self.connection.serverTypeChecked:
            options = self.getSpecificOption(Constants.HTTP_HEADER_SERVER)
            if options.find(Constants.HTTP_HEADER_SERVER_TAMINO) >= 0:
                self.connection.isConnectedToCatacomb = False
            else:
                self.connection.isConnectedToCatacomb = True
            self.connection.serverTypeChecked = True
        return self.connection.isConnectedToCatacomb
        
    def getSpecificOption(self, option):
        """
        Returns specified WebDav options.
        @param option: name of the option
        
        @return: String containing the value of the option.
        @rtype: C{string}
        """
        options = ''
        try:
            options = self.options().get(option)
        except KeyError:
            return options
        return options  
        
    ### delegate some method invocations    
    def __getattr__(self, name):
        """
        Build-in method:
        Forwards unknow lookups (methods) to delegate object 'versionHandler'.
        
        @param name: name of unknown attribute
        """
        # delegate Delta-V methods
        return getattr(self.versionHandler, name)
    
    def copy(self, toUrl, infinity=True):
        """
        Copies this resource.
        
        @param toUrl: target URI path
        @param infinity: Flag that indicates that the complete content of collection is copied. (default)  
        @type depth: C{boolean}
        """
        self.connection.logger.debug("Copy to " + repr(toUrl));
        _checkUrl(toUrl)
        if infinity:
            response = self.connection.copy(self.path, toUrl)
        else:
            response = self.connection.copy(self.path, toUrl, 0)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)        
        
    def delete(self, lockToken=None):
        """
        Deletes this resource.
        
        @param lockToken: String returned by last lock operation or null.
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        response = self.connection.delete(self.path, header)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)        

    def move(self, toUrl):
        """
        Moves this resource to the given path or renames it.
        
        @param toUrl: new (URI) path
        """
        self.connection.logger.debug("Move to " + repr(toUrl));
        _checkUrl(toUrl)
        response = self.connection.move(self.path, toUrl)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)        

 
    def lock(self, owner):
        """
        Locks this resource for exclusive write access. This means that for succeeding
        write operations the returned lock token has to be passed.
        If the methode does not throw an exception the lock has been granted.
        
        @param owner: describes the lock holder
        @return: lock token string (automatically generated)
        @rtype: L{LockToken}
        """
        response = self.connection.lock(self.path, owner)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)        
        return LockToken(self.url, response.locktoken)

    def unlock(self, lockToken):
        """
        Removes the lock from this resource.
        
        @param lockToken: which has been return by the lock() methode
        @type  lockToken: L{LockToken}
        """
        self.connection.unlock(self.path, lockToken.token)


    def deleteContent(self, lockToken=None):
        """
        Delete binary data at permanent storage.
        
        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        self.connection.put(self.path, "", extra_hdrs=header)

    def uploadContent(self, content, lockToken=None, extra_hdrs={}):
        """
        Write binary data to permanent storage.
        
        @param content: containing binary data
        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert not content or isinstance(content, types.UnicodeType) or\
                isinstance(content, types.StringType), "Content is not a string: " + content.__class__.__name__
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        response = None
        if not content is None:
            header["Content-length"] = str(len(content))
        else:
            header["Content-length"] = "0"
        header.update(extra_hdrs)
        try: 
            response = self.connection.put(self.path, content, extra_hdrs=header)
        finally:
            if response:        
                self.connection.logger.debug(response.read())
                response.close()

    def uploadFile(self, newFile, lockToken=None):
        """
        Write binary data to permanent storage.
        
        @param newFile: File containing binary data.
        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert isinstance(newFile, types.FileType), "Argument is no file: " + file.__class__.__name__
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        self.connection.putFile(self.path, newFile, header=header)

    def downloadContent(self, extra_hdrs={}):
        """
        Read binary data from permanent storage.
        """
        response = self.connection.get(self.path, extra_hdrs=extra_hdrs)
        # TODO: Other interface ? return self.connection.getfile()
        return response

    def downloadFile(self, localFileName):
        """
        Copy binary data from permanent storage to a local file.
        
        @param localFileName: file to write binary data to
        """
        localFile = open(localFileName, 'wb')
        remoteFile = self.downloadContent()
        _blockCopyFile(remoteFile, localFile, Connection.blockSize)
        remoteFile.close()
        localFile.close()

    def readProperties(self, *names, **kwargs):
        """
        Reads the given properties.
        
        @param names: a list of property names.
                      A property name is a (XmlNameSpace, propertyName) tuple.
        @param ignore404: a boolean flag.
                      Indicates if an error should be raised for missing properties.
        @return: a map from property names to DOM Element or String values.
        """
        assert names, "Property names are missing."
        ignore404 = kwargs.pop('ignore404', False)
        body = createFindBody(names, self.defaultNamespace)
        response = self.connection.propfind(self.path, body, depth=0)
        properties = response.msr.values()[0]
        if  not ignore404 and properties.errorCount > 0 :
            raise WebdavError("Property is missing on '%s': %s" % (self.path, properties.reason), properties.code)
        return properties

    def readProperty(self, nameSpace, name):
        """
        Reads the given property.
        
        @param nameSpace: XML-namespace
        @type nameSpace: string
        @param name: A property name.
        @type name: string
        
        @return: a map from property names to DOM Element or String values.
        """
        results = self.readProperties((nameSpace, name))
        if  len(results) == 0:
            raise WebdavError("Property is missing: " + results.reason)
        return results.values()[0]

    def readAllProperties(self):
        """
        Reads all properties of this resource.
        
        @return: a map from property names to DOM Element or String values.
        """
        response = self.connection.allprops(self.path, depth=0)
        return response.msr.values()[0]

    def readAllPropertyNames(self):
        """
        Returns the names of all properties attached to this resource.
        
        @return: List of property names
        """
        response = self.connection.propnames(self.path, depth=0)
        return response.msr.values()[0]

    def readStandardProperties(self):
        """
        Read all WebDAV live properties.
        
        @return: A L{LiveProperties} instance which contains a getter method for each live property.
        """
        body = createFindBody(LiveProperties.NAMES, Constants.NS_DAV)
        response = self.connection.propfind(self.path, body, depth=0)
        properties = response.msr.values()[0]
        return LiveProperties(properties)

    def writeProperties(self, properties, lockToken=None):
        """
        Sets or updates the given properties.
        
        @param lockToken: if the resource has been locked this is the lock token.
        @type  lockToken: L{LockToken}
        @param properties: a map from property names to a String or
                           DOM element value for each property to add or update.
        """
        assert isinstance(properties, types.DictType)
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        body = createUpdateBody(properties, self.defaultNamespace)
        response = self.connection.proppatch(self.path, body, header)
        if  response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    def deleteProperties(self, lockToken=None, *names):
        """
        Removes the given properties from this resource.
        
        @param lockToken: if the resource has been locked this is the lock token.
        @type  lockToken: L{LockToken}
        @param names: a collection of property names.
               A property name is a (XmlNameSpace, propertyName) tuple.
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        body = createDeleteBody(names, self.defaultNamespace)
        response = self.connection.proppatch(self.path, body, header)
        if  response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    # ACP extension
    def setAcl(self, acl, lockToken=None):
        """
        Sets ACEs in the non-inherited and non-protected ACL or the resource.
        This is the implementation of the ACL method of the WebDAV ACP.
        
        @param acl: ACL to be set on resource as ACL object.
        @param lockToken: If the resource has been locked this is the lock token (defaults to None).
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        headers = {}
        if lockToken:
            headers = lockToken.toHeader()
        headers['Content-Type'] = XML_CONTENT_TYPE
        body                    = acl.toXML()
        response = self.connection._request('ACL', self.path, body, headers)
        return response
        ## TODO: parse DAV:error response

    def getAcl(self):
        """
        Returns this resource's ACL in an ACL instance.
        
        @return: Access Control List.
        @rtype: L{ACL<webdav.acp.Acl.ACL>}
        """
        xmlAcl = self.readProperty(Constants.NS_DAV, Constants.TAG_ACL)
        return ACL(xmlAcl)

    def getCurrentUserPrivileges(self):
        """
        Returns a tuple of the current user privileges.
        
        @return: list of Privilege instances
        @rtype: list of L{Privilege<webdav.acp.Privilege.Privilege>}
        """
        privileges = self.readProperty(Constants.NS_DAV, Constants.PROP_CURRENT_USER_PRIVILEGE_SET)
        result = []
        for child in privileges.children:
            result.append(Privilege(domroot=child))
        return result
    
    def getPrincipalCollections(self):
        """
        Returns a list principal collection URLs.
        
        @return: list of principal collection URLs
        @rtype: C{list} of C{unicode} elements
        """
        webdavQueryResult = self.readProperty(Constants.NS_DAV, Constants.PROP_PRINCIPAL_COLLECTION_SET)
        principalCollectionList = []
        for child in webdavQueryResult.children:
            principalCollectionList.append(child.first_cdata)            
        return principalCollectionList
    
    def getOwnerUrl(self):
        """ Explicitly retireve the Url of the owner. """
        
        result = self.readProperty(Constants.NS_DAV, Constants.PROP_OWNER)
        if result and len(result.children):
            return result.children[0].textof()
        return None
Example #6
0
class ResourceStorer(object):
    """
    This class provides client access to a WebDAV resource
    identified by an URI. It provides all WebDAV class 2 features which include
    uploading data, getting and setting properties qualified by a XML name space,
    locking and unlocking the resource.
    This class does not cache resource data. This has to be performed by its clients.

    @author: Roland Betz
    """

    # Instance properties
    url = property(lambda self: str(self.connection) + self.path, None, None, "Resource's URL")

    def __init__(self, url, connection=None, validateResourceNames=True):
        """
        Creates an instance for the given URL
        User must invoke validate() after construction to check the resource on the server.

        @param url: Unique resource location for this storer.
        @type  url: C{string}
        @param connection: this optional parameter contains a Connection object
            for the host part of the given URL. Passing a connection saves
            memory by sharing this connection. (defaults to None)
        @type  connection: L{webdav.Connection}
        @raise WebdavError: If validation of resource name path parts fails.
        """

        assert connection == None or isinstance(connection, Connection)
        parts = urlsplit(url, allow_fragments=False)
        self.path = parts[2]
        self.validateResourceNames = validateResourceNames

        # validate URL path
        for part in self.path.split('/'):
            if part != '' and not "ino:" in part: # explicitly allowing this character sequence as a part of a path (Tamino 4.4)
                if self.validateResourceNames:
                    try:
                        validateResourceName(part)
                    except WrongNameError:
                        raise WebdavError("Found invalid resource name part.")
                self.name = part
        # was: filter(lambda part: part and validateResourceName(part), self.path.split('/'))
        # but filter is deprecated

        self.defaultNamespace = None     # default XML name space of properties
        if connection:
            self.connection = connection
        else:
            conn = parts[1].split(":")
            if len(conn) == 1:
                self.connection = Connection(conn[0], protocol = parts[0])  # host and protocol
            else:
                self.connection = Connection(conn[0], int(conn[1]), protocol = parts[0])  # host and port and protocol
        self.versionHandler = VersionHandler(self.connection, self.path)


    def validate(self):
        """
        Check whether URL contains a WebDAV resource
        Uses the WebDAV OPTIONS method.

        @raise WebdavError: L{WebdavError} if URL does not contain a WebDAV resource
        """
        #davHeader = response.getheader(HTTP_HEADER_DAV)
        davHeader = self.getSpecificOption(Constants.HTTP_HEADER_DAV)
        self.connection.logger.debug("HEADER DAV: %s" % davHeader)
        if not(davHeader) or davHeader.find("2") < 0:   # DAV class 2 supported ?
            raise WebdavError("URL does not support WebDAV", 0)

    def options(self):
        """
        Send an OPTIONS request to server and return all HTTP headers.

        @return: map of all HTTP headers returned by the OPTIONS method.
        """
        response = self.connection.options(self.path)
        result = {}
        result.update(response.msg)
        self.connection.logger.debug("OPTION returns: " + str(result.keys()))
        return result

    def _getAclSupportAvailable(self):
        """
        Returns True if the current connection has got ACL support.

        @return: ACL support (True / False)
        @rtype: C{bool}
        """
        options = self.getSpecificOption(Constants.HTTP_HEADER_DAV)
        if options.find(Constants.HTTP_HEADER_OPTION_ACL) >= 0:
            return True
        else:
            return False

    aclSupportAvailable = property(_getAclSupportAvailable)

    def _getDaslBasicsearchSupportAvailable(self):
        """
        Returns True if the current connection supports DASL basic search.

        @return: DASL basic search support (True / False)
        @rtype: C{bool}
        """
        options = self.getSpecificOption(Constants.HTTP_HEADER_DASL)
        if not options or \
           not options.find(Constants.HTTP_HEADER_OPTION_DAV_BASIC_SEARCH) >= 0:
            return False
        else:
            return True

    daslBasicsearchSupportAvailable = property(_getDaslBasicsearchSupportAvailable)

    def isConnectedToCatacombServer(self):
        """
        Returns True if connected to a Catacomb WebDav server.

        @return: if connected to Catacomb Webdav server (True / False)
        @rtype: C{bool}
        """
        if not self.connection.serverTypeChecked:
            options = self.getSpecificOption(Constants.HTTP_HEADER_SERVER)
            if options.find(Constants.HTTP_HEADER_SERVER_TAMINO) >= 0:
                self.connection.isConnectedToCatacomb = False
            else:
                self.connection.isConnectedToCatacomb = True
            self.connection.serverTypeChecked = True
        return self.connection.isConnectedToCatacomb

    def getSpecificOption(self, option):
        """
        Returns specified WebDav options.
        @param option: name of the option

        @return: String containing the value of the option.
        @rtype: C{string}
        """
        options = ''
        try:
            options = self.options().get(option)
        except KeyError:
            return options
        return options

    ### delegate some method invocations
    def __getattr__(self, name):
        """
        Build-in method:
        Forwards unknow lookups (methods) to delegate object 'versionHandler'.

        @param name: name of unknown attribute
        """
        # delegate Delta-V methods
        return getattr(self.versionHandler, name)

    def copy(self, toUrl, infinity=True):
        """
        Copies this resource.

        @param toUrl: target URI path
        @param infinity: Flag that indicates that the complete content of collection is copied. (default)
        @type depth: C{boolean}
        """
        self.connection.logger.debug("Copy to " + repr(toUrl));
        _checkUrl(toUrl)
        if infinity:
            response = self.connection.copy(self.path, toUrl)
        else:
            response = self.connection.copy(self.path, toUrl, 0)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    def delete(self, lockToken=None):
        """
        Deletes this resource.

        @param lockToken: String returned by last lock operation or null.
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        response = self.connection.delete(self.path, header)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    def move(self, toUrl):
        """
        Moves this resource to the given path or renames it.

        @param toUrl: new (URI) path
        """
        self.connection.logger.debug("Move to " + repr(toUrl));
        _checkUrl(toUrl)
        response = self.connection.move(self.path, toUrl)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)


    def lock(self, owner):
        """
        Locks this resource for exclusive write access. This means that for succeeding
        write operations the returned lock token has to be passed.
        If the methode does not throw an exception the lock has been granted.

        @param owner: describes the lock holder
        @return: lock token string (automatically generated)
        @rtype: L{LockToken}
        """
        response = self.connection.lock(self.path, owner)
        if  response.status == Constants.CODE_MULTISTATUS and response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)
        return LockToken(self.url, response.locktoken)

    def unlock(self, lockToken):
        """
        Removes the lock from this resource.

        @param lockToken: which has been return by the lock() methode
        @type  lockToken: L{LockToken}
        """
        self.connection.unlock(self.path, lockToken.token)


    def deleteContent(self, lockToken=None):
        """
        Delete binary data at permanent storage.

        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        self.connection.put(self.path, "", extra_hdrs=header)

    def uploadContent(self, content, lockToken=None, extra_hdrs={}):
        """
        Write binary data to permanent storage.

        @param content: containing binary data
        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert not content or isinstance(content, types.UnicodeType) or\
                isinstance(content, types.StringType), "Content is not a string: " + content.__class__.__name__
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        response = None
        if not content is None:
            header["Content-length"] = str(len(content))
        else:
            header["Content-length"] = "0"
        header.update(extra_hdrs)
        try:
            response = self.connection.put(self.path, content, extra_hdrs=header)
        finally:
            if response:
                self.connection.logger.debug(response.read())
                response.close()

    def uploadFile(self, newFile, lockToken=None):
        """
        Write binary data to permanent storage.

        @param newFile: File containing binary data.
        @param lockToken: None or lock token from last lock request
        @type  lockToken: L{LockToken}
        """
        assert isinstance(newFile, types.FileType), "Argument is no file: " + file.__class__.__name__
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        self.connection.putFile(self.path, newFile, header=header)

    def downloadContent(self, extra_hdrs={}):
        """
        Read binary data from permanent storage.
        """
        response = self.connection.get(self.path, extra_hdrs=extra_hdrs)
        # TODO: Other interface ? return self.connection.getfile()
        return response

    def downloadFile(self, localFileName):
        """
        Copy binary data from permanent storage to a local file.

        @param localFileName: file to write binary data to
        """
        localFile = open(localFileName, 'wb')
        remoteFile = self.downloadContent()
        _blockCopyFile(remoteFile, localFile, Connection.blockSize)
        remoteFile.close()
        localFile.close()

    def readProperties(self, *names, **kwargs):
        """
        Reads the given properties.

        @param names: a list of property names.
                      A property name is a (XmlNameSpace, propertyName) tuple.
        @param ignore404: a boolean flag.
                      Indicates if an error should be raised for missing properties.
        @return: a map from property names to DOM Element or String values.
        """
        assert names, "Property names are missing."
        ignore404 = kwargs.pop('ignore404', False)
        body = createFindBody(names, self.defaultNamespace)
        response = self.connection.propfind(self.path, body, depth=0)
        properties = response.msr.values()[0]
        if  not ignore404 and properties.errorCount > 0 :
            raise WebdavError("Property is missing on '%s': %s" % (self.path, properties.reason), properties.code)
        return properties

    def readProperty(self, nameSpace, name):
        """
        Reads the given property.

        @param nameSpace: XML-namespace
        @type nameSpace: string
        @param name: A property name.
        @type name: string

        @return: a map from property names to DOM Element or String values.
        """
        results = self.readProperties((nameSpace, name))
        if  len(results) == 0:
            raise WebdavError("Property is missing: " + results.reason)
        return results.values()[0]

    def readAllProperties(self):
        """
        Reads all properties of this resource.

        @return: a map from property names to DOM Element or String values.
        """
        response = self.connection.allprops(self.path, depth=0)
        return response.msr.values()[0]

    def readAllPropertyNames(self):
        """
        Returns the names of all properties attached to this resource.

        @return: List of property names
        """
        response = self.connection.propnames(self.path, depth=0)
        return response.msr.values()[0]

    def readStandardProperties(self):
        """
        Read all WebDAV live properties.

        @return: A L{LiveProperties} instance which contains a getter method for each live property.
        """
        body = createFindBody(LiveProperties.NAMES, Constants.NS_DAV)
        response = self.connection.propfind(self.path, body, depth=0)
        properties = response.msr.values()[0]
        return LiveProperties(properties)

    def writeProperties(self, properties, lockToken=None):
        """
        Sets or updates the given properties.

        @param lockToken: if the resource has been locked this is the lock token.
        @type  lockToken: L{LockToken}
        @param properties: a map from property names to a String or
                           DOM element value for each property to add or update.
        """
        assert isinstance(properties, types.DictType)
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        body = createUpdateBody(properties, self.defaultNamespace)
        response = self.connection.proppatch(self.path, body, header)
        if  response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    def deleteProperties(self, lockToken=None, *names):
        """
        Removes the given properties from this resource.

        @param lockToken: if the resource has been locked this is the lock token.
        @type  lockToken: L{LockToken}
        @param names: a collection of property names.
               A property name is a (XmlNameSpace, propertyName) tuple.
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        header = {}
        if lockToken:
            header = lockToken.toHeader()
        body = createDeleteBody(names, self.defaultNamespace)
        response = self.connection.proppatch(self.path, body, header)
        if  response.msr.errorCount > 0:
            raise WebdavError("Request failed: " + response.msr.reason, response.msr.code)

    # ACP extension
    def setAcl(self, acl, lockToken=None):
        """
        Sets ACEs in the non-inherited and non-protected ACL or the resource.
        This is the implementation of the ACL method of the WebDAV ACP.

        @param acl: ACL to be set on resource as ACL object.
        @param lockToken: If the resource has been locked this is the lock token (defaults to None).
        @type  lockToken: L{LockToken}
        """
        assert lockToken == None or isinstance(lockToken, LockToken), \
                "Invalid lockToken argument %s" % type(lockToken)
        headers = {}
        if lockToken:
            headers = lockToken.toHeader()
        headers['Content-Type'] = XML_CONTENT_TYPE
        body                    = acl.toXML()
        response = self.connection._request('ACL', self.path, body, headers)
        return response
        ## TODO: parse DAV:error response

    def getAcl(self):
        """
        Returns this resource's ACL in an ACL instance.

        @return: Access Control List.
        @rtype: L{ACL<webdav.acp.Acl.ACL>}
        """
        xmlAcl = self.readProperty(Constants.NS_DAV, Constants.TAG_ACL)
        return ACL(xmlAcl)

    def getCurrentUserPrivileges(self):
        """
        Returns a tuple of the current user privileges.

        @return: list of Privilege instances
        @rtype: list of L{Privilege<webdav.acp.Privilege.Privilege>}
        """
        privileges = self.readProperty(Constants.NS_DAV, Constants.PROP_CURRENT_USER_PRIVILEGE_SET)
        result = []
        for child in privileges.children:
            result.append(Privilege(domroot=child))
        return result

    def getPrincipalCollections(self):
        """
        Returns a list principal collection URLs.

        @return: list of principal collection URLs
        @rtype: C{list} of C{unicode} elements
        """
        webdavQueryResult = self.readProperty(Constants.NS_DAV, Constants.PROP_PRINCIPAL_COLLECTION_SET)
        principalCollectionList = []
        for child in webdavQueryResult.children:
            principalCollectionList.append(child.first_cdata)
        return principalCollectionList

    def getOwnerUrl(self):
        """ Explicitly retireve the Url of the owner. """

        result = self.readProperty(Constants.NS_DAV, Constants.PROP_OWNER)
        if result and len(result.children):
            return result.children[0].textof()
        return None