[tryton-debian-vcs] pywebdav branch upstream created. b7905edefb68e2899bcccf84d9b1903e3a42d729

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Wed Nov 27 16:48:27 UTC 2013


The following commit has been merged in the upstream branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/pywebdav.git;a=commitdiff;h=b7905edefb68e2899bcccf84d9b1903e3a42d729
commit b7905edefb68e2899bcccf84d9b1903e3a42d729
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Mar 27 12:08:46 2012 +0200

    Adding upstream version 0.9.8.

diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
deleted file mode 100644
index b6815f2..0000000
--- a/DAV/AuthServer.py
+++ /dev/null
@@ -1,238 +0,0 @@
-#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-"""
-    Authenticating HTTP Server
-
-    This module builds on BaseHTTPServer and implements
-    basic authentication
-
-"""
-
-from DAV.utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__  = AUTHOR
-
-import os
-import sys
-import time
-import socket
-import string
-import posixpath
-import SocketServer
-import BufferingHTTPServer
-import BaseHTTPServer
-import base64
-
-from string import atoi,split
-
-AUTH_ERROR_MSG="""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
-<HTML><HEAD>
-<TITLE>401 Authorization Required</TITLE>
-</HEAD><BODY>
-<H1>Authorization Required</H1>
-This server could not verify that you
-are authorized to access the document
-requested.  Either you supplied the wrong
-credentials (e.g., bad password), or your
-browser doesn't understand how to supply
-the credentials required.<P>
-</BODY></HTML>"""
-
-class AuthRequestHandler:
-    """
-    Simple handler that use buffering and can check for auth headers 
-
-    In order to use it create a subclass of BufferedAuthRequestHandler
-    or BasicAuthRequestHandler depending on if you want to send
-    responses as block or as stream.
-
-    In your subclass you have to define the method get_userinfo(user,pw)
-    which should return 1 or None depending on whether the password was
-    ok or not. None means that the user is not authorized.
-    """
-
-    # False means no authentiation
-    DO_AUTH=1
-
-    AUTH_ERROR_MSG="""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
-    <HTML><HEAD>
-    <TITLE>401 Authorization Required</TITLE>
-    </HEAD><BODY>
-    <H1>Authorization Required</H1>
-    This server could not verify that you
-    are authorized to access the document
-    requested.  Either you supplied the wrong
-    credentials (e.g., bad password), or your
-    browser doesn't understand how to supply
-    the credentials required.<P>
-    </BODY></HTML>"""
-
-    server_version = "AuthHTTP/" + __version__
-
-    def _log(self, message):
-        pass
-
-    def handle(self):
-        """
-        Special handle method with buffering and authentication
-        """
-
-        self.raw_requestline = self.rfile.readline()
-        self.request_version = version = "HTTP/0.9" # Default
-        requestline = self.raw_requestline
-
-        # needed by send_error
-        self.command = requestline
-        self.headers = {}
-
-        if requestline[-2:] == '\r\n':
-            requestline = requestline[:-2]
-        elif requestline[-1:] == '\n':
-            requestline = requestline[:-1]
-
-        self.requestline = requestline
-        words = string.split(requestline)
-        if len(words) == 3:
-            [command, path, version] = words
-            if version[:5] != 'HTTP/':
-                self.send_error(400, "Bad request version (%s)" % `version`)
-                return
-        elif len(words) == 2:
-            [command, path] = words
-            if command != 'GET':
-                self.send_error(400,
-                                "Bad HTTP/0.9 request type (%s)" % `command`)
-                return
-        else:
-            self.send_error(400, "Bad request syntax (%s)" % `requestline`)
-            return
-
-        self.command, self.path, self.request_version = command, path, version
-        self.headers = self.MessageClass(self.rfile, 0)
-
-        # test authentification
-        if self.DO_AUTH:
-            try:
-                a=self.headers["Authorization"]
-                m,up=string.split(a)
-                up2=base64.decodestring(up)
-                user,pw=string.split(up2,":")
-
-                # Check if the given user can access
-                if not self.get_userinfo(user,pw,command):
-                    self.send_autherror(401,"Authorization Required"); return
-            except:
-                self.send_autherror(401,"Authorization Required")
-                return
-
-        # check for methods starting with do_
-        mname = 'do_' + command
-        if not hasattr(self, mname):
-            self.send_error(501, "Unsupported method (%s)" % `command`)
-            return
-
-        method = getattr(self, mname)
-        method()
-
-        self._flush()
-
-    def send_response(self,code, message=None):
-        """Override send_response to use the correct http version
-           in the response."""
-
-        if message is None:
-            if self.responses.has_key(code):
-                message = self.responses[code][0]
-            else:
-                message = ''
-
-        if self.request_version != 'HTTP/0.9':
-            self._append("%s %s %s\r\n" %
-                             (self.request_version, str(code), message))
-
-        self.send_header('Server', self.version_string())
-        self.send_header('Date', self.date_time_string())
-        self.send_header('Connection', 'close')
-
-    def send_head(self):
-        """Common code for GET and HEAD commands.
-
-        This sends the response code and MIME headers.
-
-        Return value is either a file object (which has to be copied
-        to the outputfile by the caller unless the command was HEAD,
-        and must be closed by the caller under all circumstances), or
-        None, in which case the caller has nothing further to do.
-
-        """
-        path = self.translate_path(self.path)
-        if os.path.isdir(path):
-            self.send_error(403, "Directory listing not supported")
-            return None
-        try:
-            f = open(path, 'rb')
-        except IOError:
-            self.send_error(404, "File not found")
-            return None
-
-        self.send_response(200)
-        self.send_header("Content-type", self.guess_type(path))
-        self.end_headers()
-        return f
-
-    def send_autherror(self,code,message=None):
-        try:
-            short, long = self.responses[code]
-        except KeyError:
-            short, long = '???', '???'
-        if not message:
-            message = short
-        explain = long
-
-        emsg=self.AUTH_ERROR_MSG
-        self.log_error("code %d, message %s", code, message)
-        self.send_response(code, message)
-        self.send_header("WWW-Authenticate","Basic realm=\"PyWebDAV\"")
-        self.send_header("Content-Type", 'text/html')
-        self.end_headers()
-
-        lines=split(emsg,"\n")
-        for l in lines:
-            self._append("%s\r\n" %l)
-
-    def get_userinfo(self,user, password, command):
-        """Checks if the given user and the given
-        password are allowed to access.
-        """
-
-        # Always reject
-        return None
-
-class BufferedAuthRequestHandler(BufferingHTTPServer.BufferedHTTPRequestHandler,AuthRequestHandler):
-
-    def handle(self):
-        self._init_buffer()
-        AuthRequestHandler.handle(self)
-        self._flush()
-
-class BasicAuthRequestHandler(BufferingHTTPServer.BufferedHTTPRequestHandler,AuthRequestHandler):
-
-    def _append(self,s):
-        """ write the string to wfile """
-        self.wfile.write(s)
-
diff --git a/DAV/BufferingHTTPServer.py b/DAV/BufferingHTTPServer.py
deleted file mode 100644
index 897fe59..0000000
--- a/DAV/BufferingHTTPServer.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-"""
-    Buffering HTTP Server
-
-"""
-
-
-from DAV.utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__  = AUTHOR
-
-from BaseHTTPServer import BaseHTTPRequestHandler
-
-class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
-    """
-    Buffering HTTP Request Handler
-
-    This class is an extension to the BaseHTTPRequestHandler
-    class which buffers the whole output and sends it at once
-    after the processing if the request is finished.
-
-    This makes it possible to work together with some clients
-    which otherwise would break (e.g. cadaver)
-
-    """
-
-    responses = dict(
-        BaseHTTPRequestHandler.responses.items() +
-        {
-            424: ('Failed Dependency', 'The request failed due to failure of a previous request')
-        }.items()
-    )
-
-    def _init_buffer(self):
-        """initialize the buffer.
-
-        If you override the handle() method remember to call
-        this (see below)
-        """
-        self.__buffer=""
-
-    def _append(self,s):
-        """ append a string to the buffer """
-        self.__buffer=self.__buffer+s
-
-    def _flush(self):
-        """ flush the buffer to wfile """
-        self.wfile.write(self.__buffer)
-        self.wfile.flush()
-        self.__buffer=""
-
-    def handle(self):
-        """ Handle a HTTP request """
-
-        self._init_buffer()
-        BaseHTTPRequestHandler.handle(self)
-        self._flush()
-
-    def send_header(self, keyword, value):
-        """Send a MIME header."""
-        if self.request_version != 'HTTP/0.9':
-            self._append("%s: %s\r\n" % (keyword, value))
-
-    def end_headers(self):
-        """Send the blank line ending the MIME headers."""
-        if self.request_version != 'HTTP/0.9':
-            self._append("\r\n")
-
-    def send_response(self, code, message=None):
-        self.log_request(code)
-
-        if message is None:
-            if self.responses.has_key(code):
-                message = self.responses[code][0]
-            else:
-                message = ''
-
-        if self.request_version != 'HTTP/0.9':
-            self._append("%s %s %s\r\n" %
-                    (self.protocol_version, str(code), message))
-
-        self.send_header('Server', self.version_string())
-        self.send_header('Connection', 'close')
-        self.send_header('Date', self.date_time_string())
-
-    protocol_version="HTTP/1.1"
-
diff --git a/DAV/__init__.py b/DAV/__init__.py
deleted file mode 100644
index 5a88f42..0000000
--- a/DAV/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-
-"""
-    python davserver
-
-"""
diff --git a/DAV/constants.py b/DAV/constants.py
deleted file mode 100644
index 131f80b..0000000
--- a/DAV/constants.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-# definition for resourcetype
-COLLECTION=1
-OBJECT=None
-
-# attributes for resources
-DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
-
-# Request classes in propfind
-RT_ALLPROP=1
-RT_PROPNAME=2
-RT_PROP=3
-
-# server mode
-DAV_VERSION_1 = {
-        'version' : '1',
-        'options' : 
-        'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, REPORT'
-}
-
-DAV_VERSION_2 = {
-        'version' : '1,2',
-        'options' : 
-        DAV_VERSION_1['options'] + ', LOCK, UNLOCK'
-}
diff --git a/DAV/delete.py b/DAV/delete.py
deleted file mode 100644
index 79eb8ba..0000000
--- a/DAV/delete.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-import os
-import string
-import urllib
-from StringIO import StringIO
-
-from utils import gen_estring, quote_uri, make_xmlresponse
-from davcmd import deltree
-
-class DELETE:
-
-    def __init__(self,uri,dataclass):
-        self.__dataclass=dataclass
-        self.__uri=uri
-
-    def delcol(self):
-        """ delete a collection """
-
-        dc=self.__dataclass
-        result=dc.deltree(self.__uri)
-
-        if not len(result.items()):
-            return None # everything ok
-
-        # create the result element
-        return make_xmlresponse(result)
-
-    def delone(self):
-        """ delete a resource """
-
-        dc=self.__dataclass
-        return dc.delone(self.__uri)
-
diff --git a/DAV/status.py b/DAV/status.py
deleted file mode 100644
index 4133b40..0000000
--- a/DAV/status.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-"""
-
-status codes for DAV services
-
-
-"""
-
-
-STATUS_CODES={
-        100:    "Continue",
-        102:    "Processing",
-        200:    "Ok",
-        201:    "Created",
-        204:    "No Content",
-        207:    "Multi-Status",
-        201:    "Created",
-        400:    "Bad Request",
-        403:    "Forbidden",
-        404:    "Not Found",
-        405:    "Method Not Allowed",
-        409:    "Conflict",
-        412:    "Precondition failed",
-        423:    "Locked",
-        415:    "Unsupported Media Type",
-        507:    "Insufficient Storage",
-        422:    "Unprocessable Entity",
-        423:    "Locked",
-        424:    "Failed Dependency",
-        502:    "Bad Gateway",
-        507:    "Insufficient Storage"
-}
diff --git a/MANIFEST.in b/MANIFEST.in
index 22c139a..1ece202 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
 include setup.py README VERSION
 include doc/*
-include DAVServer/*.py DAVServer/*.ini
+include pywebdav/lib/*.py pywebdav/server/*.ini
diff --git a/PKG-INFO b/PKG-INFO
index 128ea8d..7d97168 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.4.1
+Version: 0.9.8
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -14,40 +14,41 @@ Description:
         
         Currently supports
         
-        * WebDAV level 1
-        * Level 2 (LOCK, UNLOCK)
-        * Experimental iterator support
+            * WebDAV level 1
+            * Level 2 (LOCK, UNLOCK)
+            * Experimental iterator support
         
         It plays nice with
         
-        * Mac OS X Finder
-        * Windows Explorer
-        * iCal
-        * cadaver
-        * Nautilus
+            * Mac OS X Finder
+            * Windows Explorer
+            * iCal
+            * cadaver
+            * Nautilus
         
         This package does *not* provide client functionality.
         
         Installation
         ============
         
-        After installation of this package you will have a new script in you $PYTHON/bin directory called
-        *davserver*. This serves as the main entry point to the server.
+        After installation of this package you will have a new script in you
+        $PYTHON/bin directory called *davserver*. This serves as the main entry point
+        to the server.
         
         Examples
         ========
         
         Example (using easy_install)::
         
-        easy_install PyWebDAV
-        davserver -D /tmp -n
+            easy_install PyWebDAV
+            davserver -D /tmp -n
         
         Example (unpacking file locally)::
         
-        tar xvzf PyWebDAV-$VERSION.tar.gz
-        cd pywebdav
-        python setup.py develop
-        davserver -D /tmp -n
+            tar xvzf PyWebDAV-$VERSION.tar.gz
+            cd pywebdav
+            python setup.py develop
+            davserver -D /tmp -n
         
         For more information: http://code.google.com/p/pywebdav/
         
@@ -55,16 +56,22 @@ Description:
         =======
         
         
-        0.9.4.1 (Feb 17 2011)
-        -------------------
+        0.9.8 (March 25 2011)
+        ---------------------
         
-        Fixed MySQL injection possibility in MySQLAuthHandler
-        Found by Teeed <op.pl> filed under CVE-2011-C0432
+        Restructured. Moved DAV package to pywebdav.lib. All integrators must simply replace
+        ''from DAV'' imports to ''from pywebdav.lib''.
         [Simon Pamies]
         
+        Remove BufferingHTTPServer, reuse the header parser of BaseHTTPServer.
+        [Cédric Krier]
+        
+        Fix issue 44: Incomplete PROPFIND response
+        [Sascha Silbe]
+        
         0.9.4 (April 15 2010)
         ---------------------
-        
+          
         Add somme configuration setting variable to enable/disable iterator and chunk support
         [Stephane Klein]
         
@@ -156,7 +163,7 @@ Description:
         Make propfind respect the depth from queries
         [Cedric Krier]
         
-        Add ETag in the header of GET. This is needed to implement
+        Add ETag in the header of GET. This is needed to implement 
         GroupDAV, CardDAV and CalDAV.
         [Cedric Krier]
         
@@ -249,9 +256,9 @@ Description:
         Changes since 0.5
         -----------------
         
-        added constants.py
+        added constants.py 
         data.py must now return COLLECTION or OBJECT when getting asked for
-        resourcetype. propfind.py will automatically generate the right xml
+        resourcetype. propfind.py will automatically generate the right xml 
         element.
         <href> now only contains the path
         changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
index 128ea8d..7d97168 100644
--- a/PyWebDAV.egg-info/PKG-INFO
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.4.1
+Version: 0.9.8
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -14,40 +14,41 @@ Description:
         
         Currently supports
         
-        * WebDAV level 1
-        * Level 2 (LOCK, UNLOCK)
-        * Experimental iterator support
+            * WebDAV level 1
+            * Level 2 (LOCK, UNLOCK)
+            * Experimental iterator support
         
         It plays nice with
         
-        * Mac OS X Finder
-        * Windows Explorer
-        * iCal
-        * cadaver
-        * Nautilus
+            * Mac OS X Finder
+            * Windows Explorer
+            * iCal
+            * cadaver
+            * Nautilus
         
         This package does *not* provide client functionality.
         
         Installation
         ============
         
-        After installation of this package you will have a new script in you $PYTHON/bin directory called
-        *davserver*. This serves as the main entry point to the server.
+        After installation of this package you will have a new script in you
+        $PYTHON/bin directory called *davserver*. This serves as the main entry point
+        to the server.
         
         Examples
         ========
         
         Example (using easy_install)::
         
-        easy_install PyWebDAV
-        davserver -D /tmp -n
+            easy_install PyWebDAV
+            davserver -D /tmp -n
         
         Example (unpacking file locally)::
         
-        tar xvzf PyWebDAV-$VERSION.tar.gz
-        cd pywebdav
-        python setup.py develop
-        davserver -D /tmp -n
+            tar xvzf PyWebDAV-$VERSION.tar.gz
+            cd pywebdav
+            python setup.py develop
+            davserver -D /tmp -n
         
         For more information: http://code.google.com/p/pywebdav/
         
@@ -55,16 +56,22 @@ Description:
         =======
         
         
-        0.9.4.1 (Feb 17 2011)
-        -------------------
+        0.9.8 (March 25 2011)
+        ---------------------
         
-        Fixed MySQL injection possibility in MySQLAuthHandler
-        Found by Teeed <op.pl> filed under CVE-2011-C0432
+        Restructured. Moved DAV package to pywebdav.lib. All integrators must simply replace
+        ''from DAV'' imports to ''from pywebdav.lib''.
         [Simon Pamies]
         
+        Remove BufferingHTTPServer, reuse the header parser of BaseHTTPServer.
+        [Cédric Krier]
+        
+        Fix issue 44: Incomplete PROPFIND response
+        [Sascha Silbe]
+        
         0.9.4 (April 15 2010)
         ---------------------
-        
+          
         Add somme configuration setting variable to enable/disable iterator and chunk support
         [Stephane Klein]
         
@@ -156,7 +163,7 @@ Description:
         Make propfind respect the depth from queries
         [Cedric Krier]
         
-        Add ETag in the header of GET. This is needed to implement
+        Add ETag in the header of GET. This is needed to implement 
         GroupDAV, CardDAV and CalDAV.
         [Cedric Krier]
         
@@ -249,9 +256,9 @@ Description:
         Changes since 0.5
         -----------------
         
-        added constants.py
+        added constants.py 
         data.py must now return COLLECTION or OBJECT when getting asked for
-        resourcetype. propfind.py will automatically generate the right xml
+        resourcetype. propfind.py will automatically generate the right xml 
         element.
         <href> now only contains the path
         changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
diff --git a/PyWebDAV.egg-info/SOURCES.txt b/PyWebDAV.egg-info/SOURCES.txt
index e80cab5..c811ea1 100644
--- a/PyWebDAV.egg-info/SOURCES.txt
+++ b/PyWebDAV.egg-info/SOURCES.txt
@@ -1,34 +1,8 @@
 MANIFEST.in
 README
 VERSION
-ez_setup.py
 setup.cfg
 setup.py
-DAV/AuthServer.py
-DAV/BufferingHTTPServer.py
-DAV/INI_Parse.py
-DAV/WebDAVServer.py
-DAV/__init__.py
-DAV/constants.py
-DAV/davcmd.py
-DAV/davcopy.py
-DAV/davmove.py
-DAV/dbconn.py
-DAV/delete.py
-DAV/errors.py
-DAV/iface.py
-DAV/locks.py
-DAV/propfind.py
-DAV/report.py
-DAV/status.py
-DAV/utils.py
-DAVServer/__init__.py
-DAVServer/config.ini
-DAVServer/daemonize.py
-DAVServer/fileauth.py
-DAVServer/fshandler.py
-DAVServer/mysqlauth.py
-DAVServer/server.py
 PyWebDAV.egg-info/PKG-INFO
 PyWebDAV.egg-info/SOURCES.txt
 PyWebDAV.egg-info/dependency_links.txt
@@ -37,9 +11,33 @@ PyWebDAV.egg-info/not-zip-safe
 PyWebDAV.egg-info/top_level.txt
 doc/ARCHITECTURE
 doc/Changes
-doc/Changes.rej
 doc/INSTALL
 doc/LICENSE
 doc/TODO
 doc/interface_class
-doc/walker
\ No newline at end of file
+doc/walker
+pywebdav/__init__.py
+pywebdav/lib/AuthServer.py
+pywebdav/lib/INI_Parse.py
+pywebdav/lib/WebDAVServer.py
+pywebdav/lib/__init__.py
+pywebdav/lib/constants.py
+pywebdav/lib/davcmd.py
+pywebdav/lib/davcopy.py
+pywebdav/lib/davmove.py
+pywebdav/lib/dbconn.py
+pywebdav/lib/delete.py
+pywebdav/lib/errors.py
+pywebdav/lib/iface.py
+pywebdav/lib/locks.py
+pywebdav/lib/propfind.py
+pywebdav/lib/report.py
+pywebdav/lib/status.py
+pywebdav/lib/utils.py
+pywebdav/server/__init__.py
+pywebdav/server/config.ini
+pywebdav/server/daemonize.py
+pywebdav/server/fileauth.py
+pywebdav/server/fshandler.py
+pywebdav/server/mysqlauth.py
+pywebdav/server/server.py
\ No newline at end of file
diff --git a/PyWebDAV.egg-info/entry_points.txt b/PyWebDAV.egg-info/entry_points.txt
index 5a11fa1..6b9ffae 100644
--- a/PyWebDAV.egg-info/entry_points.txt
+++ b/PyWebDAV.egg-info/entry_points.txt
@@ -1,3 +1,3 @@
 [console_scripts]
-davserver = DAVServer.server:run
+davserver = pywebdav.server.server:run
 
diff --git a/PyWebDAV.egg-info/top_level.txt b/PyWebDAV.egg-info/top_level.txt
index e863022..68ad5b3 100644
--- a/PyWebDAV.egg-info/top_level.txt
+++ b/PyWebDAV.egg-info/top_level.txt
@@ -1,2 +1 @@
-DAVServer
-DAV
+pywebdav
diff --git a/README b/README
index cfcd9be..e210d7b 100644
--- a/README
+++ b/README
@@ -79,8 +79,8 @@ OPTIONAL
 --------
 
 - MySQLdb (http://sourceforge.net/projects/mysql-python)
-	- Mysql server 4.0+ for Mysql authentication with
-	  with read/write access to one database
+- Mysql server 4.0+ for Mysql authentication with
+  with read/write access to one database
 
 
 NOTES
diff --git a/VERSION b/VERSION
index 199bf2a..e3e1807 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.4.1
+0.9.8
diff --git a/doc/ARCHITECTURE b/doc/ARCHITECTURE
index b683365..c6926fa 100644
--- a/doc/ARCHITECTURE
+++ b/doc/ARCHITECTURE
@@ -4,28 +4,18 @@ OVERVIEW
 
 Here is a little overview of the package:
 
-A. In the DAV/ package:
+A. In the pywebdav/lib/ package:
 
-    1. BufferingHTTPServer
-       
-       This is the same as the normal BasicHTTPServer but instead of
-       directly sending each string over the network this implementation
-       caches it and sends it at once after finishing one request.
-
-       This has the advantage that clients like cadaver don't break as
-       they want to peek at the next lines when encountering e.g. a header.
-
-    2. AuthHTTPServer
-       
+    1. AuthHTTPServer
        This works on top of either the BasicHTTPServer or the 
        BufferingHTTPServer and implements basic authentication.
 
-    3. WebDAVServer
+    2. WebDAVServer
        This server uses AuthHTTPServer for the base functionality. It also uses
        a dav interface class for interfacing with the actual data storage (e.g.
        a filesystem or a database).
 
-B. In the PyDAVServer directory:
+B. In the pywebdav/server/ directory:
 
     1. server.py
        Main file for server. Serves as a start point for the WebDAV server
@@ -47,51 +37,13 @@ B. In the PyDAVServer directory:
        PyWebDav configuration file.
 
 
-Class Tree
-----------
-
-PyWebDAVServer.fileauth.DAVAuthHandler
-<Very simple handler for authentication. Must have its data
-injected (look at server.py). Overwrites get_userinfo from AuthRequestHandler>
-
-            |
-            |
-            |
-            
-DAV.WebDAVServer.DAVRequestHandler
-<Provides methods for DAV commands. These methods are triggered
-by AuthRequestHandler.handle().>
-
-            |
-            |
-            |
-
-DAV.AuthServer.BufferedAuthRequestHandler
-<Calls the right methods to buffer request data>
-
-        |                      |
-        |                      |
-        |                      |
-
-DAV.BufferingHTTPServer     DAV.AuthServer.AuthRequestHandler
-<Saves the complete result  <Overwrites handle() method in order
-in one file and returns     to provide authentication and to pass
-only if request is          control to the do_ methods handling
-complete>                   DAV commands>
-
-
 Information
 ----------
 
 This document describes the architecture of the python davserver.
 
-The main programm is stored in DAV/WebDAVServer.py. It exports a class WebDAVServer
-which is subclassed from AuthServer which again is subclassed from 
-BufferingHTTPServer.
-
-The BufferingHTTPServer class extends the BaseHTTPServer class by
-storing all output in a buffer and sending it at once when the request
-is finished. Otherwise clients like cadaver might break.
+The main programm is stored in pywebdav/lib/WebDAVServer.py. It exports a class
+WebDAVServer which is subclassed from AuthServer.
 
 The AuthServer class implements Basic Authentication in order to make
 connections somewhat more secure.
@@ -99,7 +51,7 @@ connections somewhat more secure.
 For processing requests the WebDAVServer class needs some connection to
 the actual data on server. In contrast to a normal web server this
 data must not simply be stored on a filesystem but can also live in
-databases and the like.  
+databases and the like.
 
 Thus the WebDAVServer class needs an interface
 to this data which is implemented via an interface class (in our
@@ -124,10 +76,9 @@ following:
   You might use the existing class as skeleton and explanation of which 
   methods are needed.
 
-That should be basically all you need to do. Have a look at PyDAVServer/fileauth.py in order
-to get an example how to subclass WebDAVServer.
+That should be basically all you need to do. Have a look at
+pywebdav/server/fileauth.py in order to get an example how to subclass
+WebDAVServer.
 
 ===
 * describe the methods which need to be implemented.
-
-
diff --git a/doc/Changes b/doc/Changes
index a346e06..6f17fc4 100644
--- a/doc/Changes
+++ b/doc/Changes
@@ -1,11 +1,17 @@
 
-0.9.4.1 (Feb 17 2011)
--------------------
+0.9.8 (March 25 2011)
+---------------------
 
-Fixed MySQL injection possibility in MySQLAuthHandler
-Found by Teeed <op.pl> filed under CVE-2011-C0432
+Restructured. Moved DAV package to pywebdav.lib. All integrators must simply replace
+''from DAV'' imports to ''from pywebdav.lib''.
 [Simon Pamies]
 
+Remove BufferingHTTPServer, reuse the header parser of BaseHTTPServer.
+[Cédric Krier]
+
+Fix issue 44: Incomplete PROPFIND response
+[Sascha Silbe]
+
 0.9.4 (April 15 2010)
 ---------------------
   
diff --git a/doc/Changes.rej b/doc/Changes.rej
deleted file mode 100644
index 6d7fc72..0000000
--- a/doc/Changes.rej
+++ /dev/null
@@ -1,34 +0,0 @@
-***************
-*** 1,4 ****
-- - Enhance logging mechanism
-    [Stephane Klein]
-  
-  - Fix issue 15 : I've error when I execute PUT action with Apple Finder client
---- 1,27 ----
-+ - Add somme configuration setting variable to enable/disable iterator and chunk support
-+   [Stephane Klein]
-+ 
-+ - "log_request" is called after action (like Apache and other server), not before action
-+   [Stephane Klein]
-+ 
-+ - Fix issue 23 : PyWebDAV need to use iterator to avoid over memory consuption
-+   [Stephane Klein]
-+ 
-+ - Fix issue 22 : pywebdav need handle "Range" header information 
-+                  in do_GET request
-+   [Stephane Klein]
-+ 
-+ - Fix issue 21 : Add thread support
-+   [Stephane Klein]
-+ 
-+ - Print User-Agent information in log request.
-+   [Stephane Klein]
-+ 
-+ - Fix issue 13 : return http 1.0 compatible response (not chunked) when 
-+                  request http version is 1.0
-+   [cliff.wells]
-+ 
-+ - Fix issue 18 : Enhance logging mechanism
-    [Stephane Klein]
-  
-  - Fix issue 15 : I've error when I execute PUT action with Apple Finder client
diff --git a/doc/TODO b/doc/TODO
index b36f8e2..f97d9be 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -2,10 +2,10 @@ GENERAL
 -------
 
 - web page needs to get done:
-	- Download
-	- News
-	- TODO list
-	- Changes
+    - Download
+    - News
+    - TODO list
+    - Changes
     - Name
 
 - use a better solution than DAV/INI_Parse.py [Stephane Klein]
diff --git a/doc/interface_class b/doc/interface_class
index 74946b4..6810fc0 100644
--- a/doc/interface_class
+++ b/doc/interface_class
@@ -15,7 +15,7 @@ and change it. You actually have implement the following methods:
 
 
 
-get_childs(self,uri)
+get_childs(self, uri, filter=None)
 
     This method should return a list of all childs for the
     object specified by the given uri.
@@ -29,10 +29,10 @@ get_props(self,uri,values=None,all=None,proplist=[])
     about properties for the object specified with the given uri.
     The parameters are as follows:
 
-    values	-- ?? cannot remember ;-)
-    all		-- if set to 1 return all properties
-    proplist	-- alternatively you can give get a list of
-		   properties to return 
+    values      -- ?? cannot remember ;-)
+    all         -- if set to 1 return all properties
+    proplist    -- alternatively you can give get a list of
+                   properties to return
 
     The result of this method should be a dictionary of the form
 
@@ -50,7 +50,7 @@ get_data(self,uri)
 
 
 get_dav(self,uri,propname)
-    
+
     This method will be called when the server needs access to a DAV
     property. In the example implementation it will simply delegate it
     to the corresponding _get_dav_<propname> method. You maybe should
@@ -58,7 +58,7 @@ get_dav(self,uri,propname)
 
 
 _get_dav_<propname>(uri)
-    
+
     These methods will be called by get_dav() when the value of a DAV
     property is needed. The defined properties are:
 
@@ -100,7 +100,7 @@ rmcol(self,uri)
 
 
 rm(self,uri)
-    
+
     This is the same for single objects, the same as above applies.
 
 
diff --git a/doc/walker b/doc/walker
index cb33a27..a6264e7 100644
--- a/doc/walker
+++ b/doc/walker
@@ -3,7 +3,7 @@ Walker methods
 
 In the COPY, DELETE and MOVE methods we need to walk over
 a tree of resources and collections in order to copy, delete
-or move them. 
+or move them.
 
 The difference between all these walks is only that we perform
 a different action on the resources we visit. Thus it might be
@@ -28,17 +28,17 @@ perform on it.
 
 
 Here the iterative approach (in order to save memory):
-dc=dataclass
-queue=list=[start_uri]
+dc = dataclass
+queue = list = [start_uri]
 while len(queue):
-    element=queue[-1]
-    childs=dc.get_childs(element)
+    element = queue[-1]
+    childs = dc.get_childs(element)
     if childs:
-	list=list+childs
+        list = list + childs
     # update queue
-    del queue[-1]   
+    del queue[-1]
     if childs:
-	queue=queue+childs
+        queue = queue + childs
 
 
 (first try..)
diff --git a/ez_setup.py b/ez_setup.py
deleted file mode 100644
index d24e845..0000000
--- a/ez_setup.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!python
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
-    from ez_setup import use_setuptools
-    use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import sys
-DEFAULT_VERSION = "0.6c9"
-DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
-
-md5_data = {
-    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
-    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
-    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
-    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
-    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
-    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
-    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
-    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
-    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
-    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
-    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
-    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
-    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
-    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
-    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
-    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
-    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
-    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
-    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
-    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
-    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
-    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
-    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
-    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
-    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
-    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
-    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
-    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
-    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
-    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
-    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
-    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
-    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
-    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
-}
-
-import sys, os
-try: from hashlib import md5
-except ImportError: from md5 import md5
-
-def _validate_md5(egg_name, data):
-    if egg_name in md5_data:
-        digest = md5(data).hexdigest()
-        if digest != md5_data[egg_name]:
-            print >>sys.stderr, (
-                "md5 validation of %s failed!  (Possible download problem?)"
-                % egg_name
-            )
-            sys.exit(2)
-    return data
-
-def use_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
-    download_delay=15
-):
-    """Automatically find/download setuptools and make it available on sys.path
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end with
-    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
-    it is not already available.  If `download_delay` is specified, it should
-    be the number of seconds that will be paused before initiating a download,
-    should one be required.  If an older version of setuptools is installed,
-    this routine will print a message to ``sys.stderr`` and raise SystemExit in
-    an attempt to abort the calling script.
-    """
-    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
-    def do_download():
-        egg = download_setuptools(version, download_base, to_dir, download_delay)
-        sys.path.insert(0, egg)
-        import setuptools; setuptools.bootstrap_install_from = egg
-    try:
-        import pkg_resources
-    except ImportError:
-        return do_download()       
-    try:
-        pkg_resources.require("setuptools>="+version); return
-    except pkg_resources.VersionConflict, e:
-        if was_imported:
-            print >>sys.stderr, (
-            "The required version of setuptools (>=%s) is not available, and\n"
-            "can't be installed while this script is running. Please install\n"
-            " a more recent version first, using 'easy_install -U setuptools'."
-            "\n\n(Currently using %r)"
-            ) % (version, e.args[0])
-            sys.exit(2)
-        else:
-            del pkg_resources, sys.modules['pkg_resources']    # reload ok
-            return do_download()
-    except pkg_resources.DistributionNotFound:
-        return do_download()
-
-def download_setuptools(
-    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
-    delay = 15
-):
-    """Download setuptools from a specified location and return its filename
-
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end
-    with a '/'). `to_dir` is the directory where the egg will be downloaded.
-    `delay` is the number of seconds to pause before an actual download attempt.
-    """
-    import urllib2, shutil
-    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
-    url = download_base + egg_name
-    saveto = os.path.join(to_dir, egg_name)
-    src = dst = None
-    if not os.path.exists(saveto):  # Avoid repeated downloads
-        try:
-            from distutils import log
-            if delay:
-                log.warn("""
----------------------------------------------------------------------------
-This script requires setuptools version %s to run (even to display
-help).  I will attempt to download it for you (from
-%s), but
-you may need to enable firewall access for this script first.
-I will start the download in %d seconds.
-
-(Note: if this machine does not have network access, please obtain the file
-
-   %s
-
-and place it in this directory before rerunning this script.)
----------------------------------------------------------------------------""",
-                    version, download_base, delay, url
-                ); from time import sleep; sleep(delay)
-            log.warn("Downloading %s", url)
-            src = urllib2.urlopen(url)
-            # Read/write all in one block, so we don't create a corrupt file
-            # if the download is interrupted.
-            data = _validate_md5(egg_name, src.read())
-            dst = open(saveto,"wb"); dst.write(data)
-        finally:
-            if src: src.close()
-            if dst: dst.close()
-    return os.path.realpath(saveto)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-def main(argv, version=DEFAULT_VERSION):
-    """Install or upgrade setuptools and EasyInstall"""
-    try:
-        import setuptools
-    except ImportError:
-        egg = None
-        try:
-            egg = download_setuptools(version, delay=0)
-            sys.path.insert(0,egg)
-            from setuptools.command.easy_install import main
-            return main(list(argv)+[egg])   # we're done here
-        finally:
-            if egg and os.path.exists(egg):
-                os.unlink(egg)
-    else:
-        if setuptools.__version__ == '0.0.1':
-            print >>sys.stderr, (
-            "You have an obsolete version of setuptools installed.  Please\n"
-            "remove it from your system entirely before rerunning this script."
-            )
-            sys.exit(2)
-
-    req = "setuptools>="+version
-    import pkg_resources
-    try:
-        pkg_resources.require(req)
-    except pkg_resources.VersionConflict:
-        try:
-            from setuptools.command.easy_install import main
-        except ImportError:
-            from easy_install import main
-        main(list(argv)+[download_setuptools(delay=0)])
-        sys.exit(0) # try to force an exit
-    else:
-        if argv:
-            from setuptools.command.easy_install import main
-            main(argv)
-        else:
-            print "Setuptools version",version,"or greater has been installed."
-            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-
-def update_md5(filenames):
-    """Update our built-in md5 registry"""
-
-    import re
-
-    for name in filenames:
-        base = os.path.basename(name)
-        f = open(name,'rb')
-        md5_data[base] = md5(f.read()).hexdigest()
-        f.close()
-
-    data = ["    %r: %r,\n" % it for it in md5_data.items()]
-    data.sort()
-    repl = "".join(data)
-
-    import inspect
-    srcfile = inspect.getsourcefile(sys.modules[__name__])
-    f = open(srcfile, 'rb'); src = f.read(); f.close()
-
-    match = re.search("\nmd5_data = {\n([^}]+)}", src)
-    if not match:
-        print >>sys.stderr, "Internal error!"
-        sys.exit(2)
-
-    src = src[:match.start(1)] + repl + src[match.end(1):]
-    f = open(srcfile,'w')
-    f.write(src)
-    f.close()
-
-
-if __name__=='__main__':
-    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
-        update_md5(sys.argv[2:])
-    else:
-        main(sys.argv[1:])
-
-
-
-
-
-
diff --git a/DAVServer/__init__.py b/pywebdav/__init__.py
similarity index 100%
rename from DAVServer/__init__.py
rename to pywebdav/__init__.py
diff --git a/pywebdav/lib/AuthServer.py b/pywebdav/lib/AuthServer.py
new file mode 100644
index 0000000..d4c26c5
--- /dev/null
+++ b/pywebdav/lib/AuthServer.py
@@ -0,0 +1,102 @@
+"""Authenticating HTTP Server
+
+This module builds on BaseHTTPServer and implements basic authentication
+
+"""
+
+import base64
+import binascii
+import BaseHTTPServer
+
+
+DEFAULT_AUTH_ERROR_MESSAGE = """
+<head>
+<title>%(code)s - %(message)s</title>
+</head>
+<body>
+<h1>Authorization Required</h1>
+this server could not verify that you
+are authorized to access the document
+requested.  Either you supplied the wrong
+credentials (e.g., bad password), or your
+browser doesn't understand how to supply
+the credentials required.
+</body>"""
+
+
+def _quote_html(html):
+    return html.replace("&", "&").replace("<", "<").replace(">", ">")
+
+
+class AuthRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    """
+    Simple handler that can check for auth headers
+
+    In your subclass you have to define the method get_userinfo(user, password)
+    which should return 1 or None depending on whether the password was
+    ok or not. None means that the user is not authorized.
+    """
+
+    # False means no authentiation
+    DO_AUTH = 1
+
+    def parse_request(self):
+        if not BaseHTTPServer.BaseHTTPRequestHandler.parse_request(self):
+            return False
+
+        if self.DO_AUTH:
+            authorization = self.headers.get('Authorization', '')
+            if not authorization:
+                self.send_autherror(401, "Authorization Required")
+                return False
+            scheme, credentials = authorization.split()
+            if scheme != 'Basic':
+                self.send_error(501)
+                return False
+            credentials = base64.decodestring(credentials)
+            user, password = credentials.split(':', 2)
+            if not self.get_userinfo(user, password, self.command):
+                self.send_autherror(401, "Authorization Required")
+                return False
+        return True
+
+    def send_autherror(self, code, message=None):
+        """Send and log an auth error reply.
+
+        Arguments are the error code, and a detailed message.
+        The detailed message defaults to the short entry matching the
+        response code.
+
+        This sends an error response (so it must be called before any
+        output has been generated), logs the error, and finally sends
+        a piece of HTML explaining the error to the user.
+
+        """
+        try:
+            short, long = self.responses[code]
+        except KeyError:
+            short, long = '???', '???'
+        if message is None:
+            message = short
+        explain = long
+        self.log_error("code %d, message %s", code, message)
+
+        # using _quote_html to prevent Cross Site Scripting attacks (see bug
+        # #1100201)
+        content = (self.error_auth_message_format % {'code': code, 'message':
+                   _quote_html(message), 'explain': explain})
+        self.send_response(code, message)
+        self.send_header('Content-Type', self.error_content_type)
+        self.send_header('WWW-Authenticate', 'Basic realm="PyWebDAV"')
+        self.send_header('Connection', 'close')
+        self.end_headers()
+        self.wfile.write(content)
+
+    error_auth_message_format = DEFAULT_AUTH_ERROR_MESSAGE
+
+    def get_userinfo(self, user, password, command):
+        """Checks if the given user and the given
+        password are allowed to access.
+        """
+        # Always reject
+        return None
diff --git a/DAV/INI_Parse.py b/pywebdav/lib/INI_Parse.py
similarity index 100%
rename from DAV/INI_Parse.py
rename to pywebdav/lib/INI_Parse.py
diff --git a/DAV/WebDAVServer.py b/pywebdav/lib/WebDAVServer.py
similarity index 68%
rename from DAV/WebDAVServer.py
rename to pywebdav/lib/WebDAVServer.py
index 481e32a..beecc67 100644
--- a/DAV/WebDAVServer.py
+++ b/pywebdav/lib/WebDAVServer.py
@@ -1,31 +1,11 @@
-"""
-    Python WebDAV Server.
-
-    This module builds on AuthServer by implementing the standard DAV
-    methods.
+"""DAV HTTP Server
 
-    Subclass this class and specify an IFACE_CLASS. See example.
+This module builds on BaseHTTPServer and implements DAV commands
 
 """
-
-DEBUG=None
-
-from DAV.utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__  = AUTHOR
-
-
-import os
-import sys
-import time
-import socket
-import string
-import posixpath
-import base64
 import AuthServer
 import urlparse
 import urllib
-import random
 import logging
 
 from propfind import PROPFIND
@@ -35,19 +15,24 @@ from davcopy import COPY
 from davmove import MOVE
 
 from utils import rfc1123_date, IfParser, tokenFinder
-from string import atoi,split
-from errors import *
+from string import atoi
+from errors import DAV_Error, DAV_NotFound
 
 from constants import DAV_VERSION_1, DAV_VERSION_2
 from locks import LockManager
 import gzip
 import StringIO
 
+from pywebdav.lib import VERSION
+
+from xml.parsers.expat import ExpatError
+
 log = logging.getLogger(__name__)
 
-BUFFER_SIZE = 128 * 1000 # 128 Ko
+BUFFER_SIZE = 128 * 1000  # 128 Ko
 
-class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
+
+class DAVRequestHandler(AuthServer.AuthRequestHandler, LockManager):
     """Simple DAV request handler with
 
     - GET
@@ -68,21 +53,23 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
     """
 
-    server_version = "DAV/" + __version__
-    encode_threshold = 1400 # common MTU
+    server_version = "DAV/" + VERSION
+    encode_threshold = 1400  # common MTU
 
-    def send_body(self, DATA, code = None, msg = None, desc = None, ctype='application/octet-stream', headers={}):
+    def send_body(self, DATA, code=None, msg=None, desc=None,
+                  ctype='application/octet-stream', headers={}):
         """ send a body in one part """
         log.debug("Use send_body method")
 
-        self.send_response(code,message=msg)
+        self.send_response(code, message=msg)
         self.send_header("Connection", "close")
         self.send_header("Accept-Ranges", "bytes")
         self.send_header('Date', rfc1123_date())
-        self.send_header('DAV', DAV_VERSION_2['version'])
 
-        for a,v in headers.items():
-            self.send_header(a,v)
+        self._send_dav_version()
+
+        for a, v in headers.items():
+            self.send_header(a, v)
 
         if DATA:
             if 'gzip' in self.headers.get('Accept-Encoding', '').split(',') \
@@ -108,52 +95,46 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         if DATA:
             if isinstance(DATA, str) or isinstance(DATA, unicode):
                 log.debug("Don't use iterator")
-                self._append(DATA)
+                self.wfile.write(DATA)
             else:
                 if self._config.DAV.getboolean('http_response_use_iterator'):
                     # Use iterator to reduce using memory
                     log.debug("Use iterator")
-                    self.wfile.write(self._buffer)
-                    self.wfile.flush()
-                    self._buffer=""
                     for buf in DATA:
                         self.wfile.write(buf)
                         self.wfile.flush()
                 else:
                     # Don't use iterator, it's a compatibility option
                     log.debug("Don't use iterator")
-                    self._append(DATA.read())
+                    self.wfile.write(DATA.read())
 
-    def send_body_chunks_if_http11(self, DATA, code, msg = None, desc = None, 
-                                   ctype='text/xml; encoding="utf-8"', 
+    def send_body_chunks_if_http11(self, DATA, code, msg=None, desc=None,
+                                   ctype='text/xml; encoding="utf-8"',
                                    headers={}):
-        if (
-            self.request_version == 'HTTP/1.0' or
-            not self._config.DAV.getboolean('chunked_http_response')
-        ):
+        if (self.request_version == 'HTTP/1.0' or
+            not self._config.DAV.getboolean('chunked_http_response')):
             self.send_body(DATA, code, msg, desc, ctype, headers)
         else:
             self.send_body_chunks(DATA, code, msg, desc, ctype, headers)
- 
-    def send_body_chunks(self, DATA, code, msg=None, desc=None, 
-                            ctype='text/xml"', headers={}):
+
+    def send_body_chunks(self, DATA, code, msg=None, desc=None,
+                         ctype='text/xml"', headers={}):
         """ send a body in chunks """
 
-        self.responses[207]=(msg,desc)
-        self.send_response(code,message=msg)
+        self.responses[207] = (msg, desc)
+        self.send_response(code, message=msg)
         self.send_header("Content-type", ctype)
         self.send_header("Transfer-Encoding", "chunked")
         self.send_header('Date', rfc1123_date())
-        self.send_header('DAV', DAV_VERSION_2['version'])
 
-        for a,v in headers.items():
-            self.send_header(a,v)
+        self._send_dav_version()
+
+        for a, v in headers.items():
+            self.send_header(a, v)
 
         if DATA:
-            if (
-                ('gzip' in self.headers.get('Accept-Encoding', '').split(',')) and
-                (len(DATA) > self.encode_threshold)
-            ):
+            if ('gzip' in self.headers.get('Accept-Encoding', '').split(',')
+                and len(DATA) > self.encode_threshold):
                 buffer = StringIO.StringIO()
                 output = gzip.GzipFile(mode='wb', fileobj=buffer)
                 if isinstance(DATA, str):
@@ -176,20 +157,16 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
         if DATA:
             if isinstance(DATA, str) or isinstance(DATA, unicode):
-                self._append(hex(len(DATA))[2:]+"\r\n")
-                self._append(DATA)
-                self._append("\r\n")
-                self._append("0\r\n")
-                self._append("\r\n")
+                self.wfile.write(hex(len(DATA))[2:] + "\r\n")
+                self.wfile.write(DATA)
+                self.wfile.write("\r\n")
+                self.wfile.write("0\r\n")
+                self.wfile.write("\r\n")
             else:
                 if self._config.DAV.getboolean('http_response_use_iterator'):
                     # Use iterator to reduce using memory
-                    self.wfile.write(self._buffer)
-                    self.wfile.flush()
-                    self._buffer=""
-
                     for buf in DATA:
-                        self.wfile.write(hex(len(buf))[2:]+"\r\n")
+                        self.wfile.write(hex(len(buf))[2:] + "\r\n")
                         self.wfile.write(buf)
                         self.wfile.write("\r\n")
 
@@ -197,11 +174,17 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                     self.wfile.write("\r\n")
                 else:
                     # Don't use iterator, it's a compatibility option
-                    self._append(hex(len(DATA))[2:]+"\r\n")
-                    self._append(DATA.read())
-                    self._append("\r\n")
-                    self._append("0\r\n")
-                    self._append("\r\n")
+                    self.wfile.write(hex(len(DATA))[2:] + "\r\n")
+                    self.wfile.write(DATA.read())
+                    self.wfile.write("\r\n")
+                    self.wfile.write("0\r\n")
+                    self.wfile.write("\r\n")
+
+    def _send_dav_version(self):
+        if self._config.DAV.getboolean('lockemulation'):
+            self.send_header('DAV', DAV_VERSION_2['version'])
+        else:
+            self.send_header('DAV', DAV_VERSION_1['version'])
 
     ### HTTP METHODS called by the server
 
@@ -211,18 +194,14 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         self.send_response(200)
         self.send_header("Content-Length", 0)
 
-        if self._config.DAV.getboolean('lockemulation') is True:
-            if self._config.DAV.getboolean('verbose') is True:
-                log.info('Activated LOCK,UNLOCK emulation (experimental)')
-
+        if self._config.DAV.getboolean('lockemulation'):
             self.send_header('Allow', DAV_VERSION_2['options'])
-            self.send_header('DAV', DAV_VERSION_2['version'])
-
         else:
             self.send_header('Allow', DAV_VERSION_1['options'])
-            self.send_header('DAV', DAV_VERSION_1['version'])
 
-        self.send_header('MS-Author-Via', 'DAV') # this is for M$
+        self._send_dav_version()
+
+        self.send_header('MS-Author-Via', 'DAV')  # this is for M$
         self.end_headers()
 
     def _HEAD_GET(self, with_body=False):
@@ -236,18 +215,22 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
         # get the last modified date (RFC 1123!)
         try:
-            headers['Last-Modified'] = dc.get_prop(uri,"DAV:","getlastmodified")
-        except DAV_NotFound: headers['Last-Modified'] = "Sun, 01 Dec 2038 00:00:00 GMT"
+            headers['Last-Modified'] = dc.get_prop(
+                uri, "DAV:", "getlastmodified")
+        except DAV_NotFound:
+            pass
 
         # get the ETag if any
         try:
             headers['Etag'] = dc.get_prop(uri, "DAV:", "getetag")
-        except DAV_NotFound: pass
+        except DAV_NotFound:
+            pass
 
         # get the content type
         try:
-            content_type = dc.get_prop(uri,"DAV:","getcontenttype")
-        except DAV_NotFound: content_type = "application/octet-stream"
+            content_type = dc.get_prop(uri, "DAV:", "getcontenttype")
+        except DAV_NotFound:
+            content_type = "application/octet-stream"
 
         range = None
         status_code = 200
@@ -260,7 +243,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         # get the data
         try:
             data = dc.get_data(uri, range)
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             self.send_status(ec)
             return ec
 
@@ -269,15 +252,15 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             data = None
 
         if isinstance(data, str) or isinstance(data, unicode):
-            self.send_body(data, status_code, None, None, content_type, headers)
+            self.send_body(data, status_code, None, None, content_type,
+                           headers)
         else:
             headers['Keep-Alive'] = 'timeout=15, max=86'
             headers['Connection'] = 'Keep-Alive'
-            self.send_body_chunks_if_http11(data, status_code, None, None, 
+            self.send_body_chunks_if_http11(data, status_code, None, None,
                                             content_type, headers)
 
         return status_code
-            
 
     def do_HEAD(self):
         """ Send a HEAD response: Retrieves resource information w/o body """
@@ -300,15 +283,19 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 raise
 
     def do_TRACE(self):
-        """ This will always fail because we can not reproduce HTTP requests. 
+        """ This will always fail because we can not reproduce HTTP requests.
         We send back a 405=Method Not Allowed. """
 
-        self.send_body(None, '405', 'Method Not Allowed', 'Method Not Allowed')
+        self.send_body(None, 405, 'Method Not Allowed', 'Method Not Allowed')
 
     def do_POST(self):
         """ Replacement for GET response. Not implemented here. """
 
-        self.send_body(None, '405', 'Method Not Allowed', 'Method Not Allowed')
+        self.send_body(None, 405, 'Method Not Allowed', 'Method Not Allowed')
+
+    def do_PROPPATCH(self):
+        # currently unsupported
+        return self.send_status(423)
 
     def do_PROPFIND(self):
         """ Retrieve properties on defined resource. """
@@ -318,18 +305,22 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         # read the body containing the xml request
         # iff there is no body then this is an ALLPROP request
         body = None
-        if self.headers.has_key('Content-Length'):
+        if 'Content-Length' in self.headers:
             l = self.headers['Content-Length']
             body = self.rfile.read(atoi(l))
 
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
 
-        pf = PROPFIND(uri, dc, self.headers.get('Depth', 'infinity'), body)
+        try:
+            pf = PROPFIND(uri, dc, self.headers.get('Depth', 'infinity'), body)
+        except ExpatError:
+            # parse error
+            return self.send_status(400)
 
         try:
             DATA = '%s\n' % pf.createResponse()
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             return self.send_status(ec)
 
         # work around MSIE DAV bug for creation and modified date
@@ -337,11 +328,18 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         if (self.headers.get('User-Agent') ==
             'Microsoft Data Access Internet Publishing Provider DAV 1.1'):
             DATA = DATA.replace('<ns0:getlastmodified xmlns:ns0="DAV:">',
-                                    '<ns0:getlastmodified xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">')
+                                '<ns0:getlastmodified xmlns:n="DAV:" '
+                                'xmlns:b="urn:uuid:'
+                                'c2f41010-65b3-11d1-a29f-00aa00c14882/" '
+                                'b:dt="dateTime.rfc1123">')
             DATA = DATA.replace('<ns0:creationdate xmlns:ns0="DAV:">',
-                                    '<ns0:creationdate xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.tz">')
+                                '<ns0:creationdate xmlns:n="DAV:" '
+                                'xmlns:b="urn:uuid:'
+                                'c2f41010-65b3-11d1-a29f-00aa00c14882/" '
+                                'b:dt="dateTime.tz">')
 
-        self.send_body_chunks_if_http11(DATA, '207','Multi-Status','Multiple responses')
+        self.send_body_chunks_if_http11(DATA, 207, 'Multi-Status',
+                                        'Multiple responses')
 
     def do_REPORT(self):
         """ Query properties on defined resource. """
@@ -351,7 +349,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         # read the body containing the xml request
         # iff there is no body then this is an ALLPROP request
         body = None
-        if self.headers.has_key('Content-Length'):
+        if 'Content-Length' in self.headers:
             l = self.headers['Content-Length']
             body = self.rfile.read(atoi(l))
 
@@ -362,23 +360,33 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
         try:
             DATA = '%s\n' % rp.createResponse()
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             return self.send_status(ec)
 
-        self.send_body_chunks_if_http11(DATA, '207','Multi-Status','Multiple responses')
+        self.send_body_chunks_if_http11(DATA, 207, 'Multi-Status',
+                                        'Multiple responses')
 
     def do_MKCOL(self):
         """ create a new collection """
 
-        dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
-        uri=urllib.unquote(uri)
+        # according to spec body must be empty
+        body = None
+        if 'Content-Length' in self.headers:
+            l = self.headers['Content-Length']
+            body = self.rfile.read(atoi(l))
+
+        if body:
+            return self.send_status(415)
+
+        dc = self.IFACE_CLASS
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
 
         try:
             dc.mkcol(uri)
             self.send_status(201)
             self.log_request(201)
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             self.log_request(ec)
             return self.send_status(ec)
 
@@ -389,8 +397,16 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
 
+        # hastags not allowed
+        if uri.find('#') >= 0:
+            return self.send_status(404)
+
+        # locked resources are not allowed to delete
+        if self._l_isLocked(uri):
+            return self.send_body(None, 423, 'Locked', 'Locked')
+
         # Handle If-Match
-        if self.headers.has_key('If-Match'):
+        if 'If-Match' in self.headers:
             test = False
             etag = None
             try:
@@ -412,7 +428,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 return
 
         # Handle If-None-Match
-        if self.headers.has_key('If-None-Match'):
+        if 'If-None-Match' in self.headers:
             test = True
             etag = None
             try:
@@ -433,33 +449,29 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 self.log_request(412)
                 return
 
-        # locked resources are not allowed to delete
-        if self._l_isLocked(uri):
-            return self.send_body(None, '423', 'Locked', 'Locked')
-
         try:
-            dl = DELETE(uri,dc)
+            dl = DELETE(uri, dc)
             if dc.is_collection(uri):
-                res=dl.delcol()
+                res = dl.delcol()
                 if res:
-                    self.send_status(207,body=res)
+                    self.send_status(207, body=res)
                 else:
                     self.send_status(204)
             else:
-                res=dl.delone() or 204
-                self.send_status(res) 
+                res = dl.delone() or 204
+                self.send_status(res)
         except DAV_NotFound:
-            self.send_body(None, '404', 'Not Found', 'Not Found')
+            self.send_body(None, 404, 'Not Found', 'Not Found')
 
     def do_PUT(self):
-        dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
-        uri=urllib.unquote(uri)
+        dc = self.IFACE_CLASS
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
 
         log.debug("do_PUT: uri = %s" % uri)
         log.debug('do_PUT: headers = %s' % self.headers)
         # Handle If-Match
-        if self.headers.has_key('If-Match'):
+        if 'If-Match' in self.headers:
             log.debug("do_PUT: If-Match %s" % self.headers['If-Match'])
             test = False
             etag = None
@@ -485,8 +497,9 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 return
 
         # Handle If-None-Match
-        if self.headers.has_key('If-None-Match'):
-            log.debug("do_PUT: If-None-Match %s" % self.headers['If-None-Match'])
+        if 'If-None-Match' in self.headers:
+            log.debug("do_PUT: If-None-Match %s" %
+                      self.headers['If-None-Match'])
 
             test = True
             etag = None
@@ -517,7 +530,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             (self._l_isLocked(uri)) and
             (not ifheader)
         ):
-            return self.send_body(None, '423', 'Locked', 'Locked')
+            return self.send_body(None, 423, 'Locked', 'Locked')
 
         if ((self._l_isLocked(uri)) and (ifheader)):
             uri_token = self._l_getLockForUri(uri)
@@ -536,7 +549,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 if found:
                     break
             if not found:
-                res = self.send_body(None, '423', 'Locked', 'Locked')
+                res = self.send_body(None, 423, 'Locked', 'Locked')
                 self.log_request(423)
                 return res
 
@@ -546,10 +559,9 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 self.protocol_version >= 'HTTP/1.1' and
                 self.request_version >= 'HTTP/1.1'):
             self.send_status(100)
-            self._flush()
 
         content_type = None
-        if self.headers.has_key("Content-Type"):
+        if 'Content-Type' in self.headers:
             content_type = self.headers['Content-Type']
 
         headers = {}
@@ -567,26 +579,25 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             self.protocol_version >= 'HTTP/1.1' and
             self.request_version >= 'HTTP/1.1'
         ):
-            self.send_body(None, '201', 'Created', '', headers=headers)
-            self._flush()
+            self.send_body(None, 201, 'Created', '', headers=headers)
 
             dc.put(uri, self._readChunkedData(), content_type)
         else:
             # read the body
-            body=None
-            if self.headers.has_key("Content-Length"):
-                l=self.headers['Content-Length']
+            body = None
+            if 'Content-Length' in self.headers:
+                l = self.headers['Content-Length']
                 log.debug("do_PUT: Content-Length = %s" % l)
-                body=self._readNoChunkedData(atoi(l))
+                body = self._readNoChunkedData(atoi(l))
             else:
                 log.debug("do_PUT: Content-Length = empty")
 
             try:
                 dc.put(uri, body, content_type)
-            except DAV_Error, (ec,dd):
+            except DAV_Error, (ec, dd):
                 return self.send_status(ec)
 
-            self.send_body(None, '201', 'Created', '', headers=headers)
+            self.send_body(None, 201, 'Created', '', headers=headers)
             self.log_request(201)
 
     def _readChunkedData(self):
@@ -604,7 +615,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         else:
             # Don't use iterator, it's a compatibility option
             return self.__readNoChunkedDataWithoutIterator(content_length)
-        
+
     def __readNoChunkedDataWithIterator(self, content_length):
         while True:
             if content_length > BUFFER_SIZE:
@@ -619,105 +630,100 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     def __readNoChunkedDataWithoutIterator(self, content_length):
         return self.rfile.read(content_length)
 
-
     def do_COPY(self):
         """ copy one resource to another """
         try:
             self.copymove(COPY)
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             return self.send_status(ec)
 
     def do_MOVE(self):
         """ move one resource to another """
         try:
             self.copymove(MOVE)
-        except DAV_Error, (ec,dd):
+        except DAV_Error, (ec, dd):
             return self.send_status(ec)
 
-    def copymove(self,CLASS):
+    def copymove(self, CLASS):
         """ common method for copying or moving objects """
-        dc=self.IFACE_CLASS
+        dc = self.IFACE_CLASS
 
         # get the source URI
-        source_uri=urlparse.urljoin(self.get_baseuri(dc),self.path)
-        source_uri=urllib.unquote(source_uri)
+        source_uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        source_uri = urllib.unquote(source_uri)
 
         # get the destination URI
-        dest_uri=self.headers['Destination']
-        dest_uri=urllib.unquote(dest_uri)
+        dest_uri = self.headers['Destination']
+        dest_uri = urllib.unquote(dest_uri)
 
         # check locks on source and dest
         if self._l_isLocked(source_uri) or self._l_isLocked(dest_uri):
-            return self.send_body(None, '423', 'Locked', 'Locked')
+            return self.send_body(None, 423, 'Locked', 'Locked')
 
         # Overwrite?
-        overwrite=1
-        result_code=204
-        if self.headers.has_key("Overwrite"):
-            if self.headers['Overwrite']=="F":
-                overwrite=None
-                result_code=201
+        overwrite = 1
+        result_code = 204
+        if 'Overwrite' in self.headers:
+            if self.headers['Overwrite'] == "F":
+                overwrite = None
+                result_code = 201
 
         # instanciate ACTION class
-        cp=CLASS(dc,source_uri,dest_uri,overwrite)
+        cp = CLASS(dc, source_uri, dest_uri, overwrite)
 
         # Depth?
-        d="infinity"
-        if self.headers.has_key("Depth"):
-            d=self.headers['Depth']
+        d = "infinity"
+        if 'Depth' in self.headers:
+            d = self.headers['Depth']
 
-            if d!="0" and d!="infinity":
+            if d != "0" and d != "infinity":
                 self.send_status(400)
                 return
 
-            if d=="0":
-                res=cp.single_action()
-                self.send_status(res)
+            if d == "0":
+                res = cp.single_action()
+                self.send_status(res or 201)
                 return
 
-        # now it only can be "infinity" but we nevertheless check for a collection
+        # now it only can be "infinity" but we nevertheless check for a
+        # collection
         if dc.is_collection(source_uri):
             try:
-                res=cp.tree_action()
-            except DAV_Error, (ec,dd):
+                res = cp.tree_action()
+            except DAV_Error, (ec, dd):
                 self.send_status(ec)
                 return
         else:
             try:
-                res=cp.single_action()
-            except DAV_Error, (ec,dd):
+                res = cp.single_action()
+            except DAV_Error, (ec, dd):
                 self.send_status(ec)
                 return
 
         if res:
-            self.send_body_chunks_if_http11(res,207,self.responses[207][0],
-                            self.responses[207][1],
-                            ctype='text/xml; charset="utf-8"')
+            self.send_body_chunks_if_http11(res, 207, self.responses[207][0],
+                                            self.responses[207][1],
+                                            ctype='text/xml; charset="utf-8"')
         else:
             self.send_status(result_code)
 
-    def get_userinfo(self,user,pw):
+    def get_userinfo(self, user, pw):
         """ Dummy method which lets all users in """
-
         return 1
 
-    def send_status(self,code=200,mediatype='text/xml;  charset="utf-8"', \
-                                msg=None,body=None):
+    def send_status(self, code=200, mediatype='text/xml;  charset="utf-8"',
+                    msg=None, body=None):
 
         if not msg:
-            msg=self.responses.get(code, ['', ''])[1]
+            msg = self.responses.get(code, ['', ''])[1]
 
-        self.send_body(body,code,self.responses.get(code, [''])[0],msg,mediatype)
+        self.send_body(body, code, self.responses.get(code, [''])[0], msg,
+                       mediatype)
 
     def get_baseuri(self, dc):
         baseuri = dc.baseuri
-        if self.headers.has_key('Host'):
+        if 'Host' in self.headers:
             uparts = list(urlparse.urlparse(dc.baseuri))
             uparts[1] = self.headers['Host']
             baseuri = urlparse.urlunparse(uparts)
         return baseuri
-
-    def log_message(self, *args):
-        AuthServer.BufferedAuthRequestHandler.log_message(self, 
-            *tuple(('- %s - ' + args[0],) + (self.headers.get('User-Agent', '?'),) + args[1:])
-        )
diff --git a/pywebdav/lib/__init__.py b/pywebdav/lib/__init__.py
new file mode 100644
index 0000000..e1af983
--- /dev/null
+++ b/pywebdav/lib/__init__.py
@@ -0,0 +1,9 @@
+
+import pkg_resources
+
+# get version from package
+package = pkg_resources.require('PyWebDAV')[0]
+VERSION = package.version
+
+# author hardcoded here
+AUTHOR = 'Simon Pamies (spamsch at gmail.com)'
diff --git a/pywebdav/lib/constants.py b/pywebdav/lib/constants.py
new file mode 100644
index 0000000..4eb99b9
--- /dev/null
+++ b/pywebdav/lib/constants.py
@@ -0,0 +1,24 @@
+# definition for resourcetype
+COLLECTION=1
+OBJECT=None
+
+# attributes for resources
+DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
+
+# Request classes in propfind
+RT_ALLPROP=1
+RT_PROPNAME=2
+RT_PROP=3
+
+# server mode
+DAV_VERSION_1 = {
+        'version' : '1',
+        'options' : 
+        'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, REPORT'
+}
+
+DAV_VERSION_2 = {
+        'version' : '1,2',
+        'options' : 
+        DAV_VERSION_1['options'] + ', LOCK, UNLOCK'
+}
diff --git a/DAV/davcmd.py b/pywebdav/lib/davcmd.py
similarity index 85%
rename from DAV/davcmd.py
rename to pywebdav/lib/davcmd.py
index a69d0a0..b7b24d1 100644
--- a/DAV/davcmd.py
+++ b/pywebdav/lib/davcmd.py
@@ -1,22 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
-#Copyright (c) 2009 Cedric Krier (cedric.krier at b2ck.com)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 """
 
 davcmd.py
diff --git a/DAV/davcopy.py b/pywebdav/lib/davcopy.py
similarity index 80%
rename from DAV/davcopy.py
rename to pywebdav/lib/davcopy.py
index 77485db..7de5173 100644
--- a/DAV/davcopy.py
+++ b/pywebdav/lib/davcopy.py
@@ -1,20 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
 
diff --git a/DAV/davmove.py b/pywebdav/lib/davmove.py
similarity index 74%
rename from DAV/davmove.py
rename to pywebdav/lib/davmove.py
index 87229d0..ce43152 100644
--- a/DAV/davmove.py
+++ b/pywebdav/lib/davmove.py
@@ -1,21 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
-
 import sys
 import string
 import urlparse
diff --git a/DAV/dbconn.py b/pywebdav/lib/dbconn.py
similarity index 57%
rename from DAV/dbconn.py
rename to pywebdav/lib/dbconn.py
index 2541d3f..c80ec8c 100644
--- a/DAV/dbconn.py
+++ b/pywebdav/lib/dbconn.py
@@ -1,22 +1,4 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import logging
-import sys
 
 log = logging.getLogger(__name__)
 
@@ -26,27 +8,26 @@ except ImportError:
     log.info('No SQL support - MySQLdb missing...')
     pass
 
-class Mconn:
+import sys
 
+class Mconn:
     def connect(self,username,userpasswd,host,port,db):
-        try: 
-            connection = DB.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
+        try: connection = MySQLdb.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
         except MySQLdb.OperationalError, message:
             log.error("%d:\n%s" % (message[ 0 ], message[ 1 ] ))
             return 0
         else:
             self.db = connection.cursor()
+
             return 1
 
-    def execute(self, qry, parameters):
+    def execute(self,qry):
         if self.db:
-            try: 
-                # parameters are escaped by DB API correctly
-                res = self.db.execute(qry, parameters)
-
+            try: res=self.db.execute(qry)
             except MySQLdb.OperationalError, message:
                 log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
                 return 0
+
             except MySQLdb.ProgrammingError, message:
                 log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
                 return 0
@@ -56,11 +37,11 @@ class Mconn:
                 return self.db.fetchall()
 
     def create_user(self,user,passwd):
-        qry="select * from Users where User=%s"
-        res=self.execute(qry, user)
+        qry="select * from Users where User='%s'"%(user)
+        res=self.execute(qry)
         if not res or len(res) ==0:
-            qry="insert into Users (User,Pass) values (%s,%s)"
-            res=self.execute(qry, (user, passwd))
+            qry="insert into Users (User,Pass) values('%s','%s')"%(user,passwd)
+            res=self.execute(qry)
         else:
             log.debug("Username already in use")
 
@@ -72,7 +53,8 @@ class Mconn:
             `Write` tinyint(1) default '0',
                   PRIMARY KEY  (`uid`)
                 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"""
-        self.execute(qry, tuple())
+        self.execute(qry)
+
 
     def first_run(self,user,passwd):
         res= self.execute('select * from Users')
@@ -82,6 +64,7 @@ class Mconn:
             self.create_table()
             self.create_user(user,passwd)
 
+
     def __init__(self,user,password,host,port,db):
         self.db=0
         self.connect(user,password,host,port,db)
diff --git a/pywebdav/lib/delete.py b/pywebdav/lib/delete.py
new file mode 100644
index 0000000..3ca081b
--- /dev/null
+++ b/pywebdav/lib/delete.py
@@ -0,0 +1,32 @@
+import os
+import string
+import urllib
+from StringIO import StringIO
+
+from utils import gen_estring, quote_uri, make_xmlresponse
+from davcmd import deltree
+
+class DELETE:
+
+    def __init__(self,uri,dataclass):
+        self.__dataclass=dataclass
+        self.__uri=uri
+
+    def delcol(self):
+        """ delete a collection """
+
+        dc=self.__dataclass
+        result=dc.deltree(self.__uri)
+
+        if not len(result.items()):
+            return None # everything ok
+
+        # create the result element
+        return make_xmlresponse(result)
+
+    def delone(self):
+        """ delete a resource """
+
+        dc=self.__dataclass
+        return dc.delone(self.__uri)
+
diff --git a/DAV/errors.py b/pywebdav/lib/errors.py
similarity index 65%
rename from DAV/errors.py
rename to pywebdav/lib/errors.py
index a4ec9a2..d174fb2 100644
--- a/DAV/errors.py
+++ b/pywebdav/lib/errors.py
@@ -1,20 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 """
 
     Exceptions for the DAVserver implementation
diff --git a/DAV/iface.py b/pywebdav/lib/iface.py
similarity index 91%
rename from DAV/iface.py
rename to pywebdav/lib/iface.py
index e3582cb..3a793ff 100644
--- a/DAV/iface.py
+++ b/pywebdav/lib/iface.py
@@ -1,20 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 """
 
 basic interface class
diff --git a/DAV/locks.py b/pywebdav/lib/locks.py
similarity index 89%
rename from DAV/locks.py
rename to pywebdav/lib/locks.py
index d32d90b..83c223f 100644
--- a/DAV/locks.py
+++ b/pywebdav/lib/locks.py
@@ -1,20 +1,3 @@
-#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import os
 import sys
 import time
@@ -111,6 +94,10 @@ class LockManager:
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
 
+        # check lock token - must contain a dash
+        if not self.headers.get('Lock-Token', '').find('-')>0:
+            return self.send_status(400)
+
         token = tokenFinder(self.headers.get('Lock-Token'))
         if self._l_isLocked(uri):
             self._l_delLock(token)
@@ -122,7 +109,7 @@ class LockManager:
 
         dc = self.IFACE_CLASS
 
-        log.debug('LOCKing resource %s' % self.headers)
+        log.info('LOCKing resource %s' % self.headers)
 
         body = None
         if self.headers.has_key('Content-Length'):
@@ -133,11 +120,11 @@ class LockManager:
 
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
-        log.debug('do_LOCK: uri = %s' % uri)
+        log.info('do_LOCK: uri = %s' % uri)
 
         ifheader = self.headers.get('If')
         alreadylocked = self._l_isLocked(uri)
-        log.debug('do_LOCK: alreadylocked = %s' % alreadylocked)
+        log.info('do_LOCK: alreadylocked = %s' % alreadylocked)
 
         if body and alreadylocked:
             # Full LOCK request but resource already locked
diff --git a/DAV/propfind.py b/pywebdav/lib/propfind.py
similarity index 53%
rename from DAV/propfind.py
rename to pywebdav/lib/propfind.py
index 8d4650e..07ac6e7 100644
--- a/DAV/propfind.py
+++ b/pywebdav/lib/propfind.py
@@ -1,38 +1,19 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
 
 import logging
-import sys
-import string
 import urlparse
 import urllib
-from StringIO import StringIO
 
 import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
+from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
+from errors import DAV_Error, DAV_NotFound
 
 log = logging.getLogger(__name__)
 
+
 class PROPFIND:
-    """ parse a propfind xml element and extract props 
+    """ parse a propfind xml element and extract props
 
     It will set the following instance vars:
 
@@ -46,25 +27,26 @@ class PROPFIND:
 
     """
 
-    def __init__(self,uri,dataclass,depth, body):
-        self.request_type=None
-        self.nsmap={}
-        self.proplist={}
-        self.default_ns=None
-        self._dataclass=dataclass
-        self._depth=str(depth)
-        self._uri=uri.rstrip('/')
-        self._has_body=None    # did we parse a body?
+    def __init__(self, uri, dataclass, depth, body):
+        self.request_type = None
+        self.nsmap = {}
+        self.proplist = {}
+        self.default_ns = None
+        self._dataclass = dataclass
+        self._depth = str(depth)
+        self._uri = uri.rstrip('/')
+        self._has_body = None   # did we parse a body?
 
         if dataclass.verbose:
             log.info('PROPFIND: Depth is %s, URI is %s' % (depth, uri))
 
         if body:
-            self.request_type, self.proplist, self.namespaces = utils.parse_propfind(body)
+            self.request_type, self.proplist, self.namespaces = \
+                utils.parse_propfind(body)
             self._has_body = True
 
     def createResponse(self):
-        """ Create the multistatus response 
+        """ Create the multistatus response
 
         This will be delegated to the specific method
         depending on which request (allprop, propname, prop)
@@ -83,13 +65,13 @@ class PROPFIND:
             raise DAV_NotFound
 
         df = None
-        if self.request_type==RT_ALLPROP:
+        if self.request_type == RT_ALLPROP:
             df = self.create_allprop()
 
-        if self.request_type==RT_PROPNAME:
+        if self.request_type == RT_PROPNAME:
             df = self.create_propname()
 
-        if self.request_type==RT_PROP:
+        if self.request_type == RT_PROP:
             df = self.create_prop()
 
         if df != None:
@@ -102,33 +84,33 @@ class PROPFIND:
     def create_propname(self):
         """ create a multistatus response for the prop names """
 
-        dc=self._dataclass
+        dc = self._dataclass
         # create the document generator
         doc = domimpl.createDocument(None, "multistatus", None)
         ms = doc.documentElement
         ms.setAttribute("xmlns:D", "DAV:")
         ms.tagName = 'D:multistatus'
 
-        if self._depth=="0":
-            pnames=dc.get_propnames(self._uri)
-            re=self.mk_propname_response(self._uri,pnames, doc)
+        if self._depth == "0":
+            pnames = dc.get_propnames(self._uri)
+            re = self.mk_propname_response(self._uri, pnames, doc)
             ms.appendChild(re)
 
-        elif self._depth=="1":
-            pnames=dc.get_propnames(self._uri)
-            re=self.mk_propname_response(self._uri,pnames, doc)
+        elif self._depth == "1":
+            pnames = dc.get_propnames(self._uri)
+            re = self.mk_propname_response(self._uri, pnames, doc)
             ms.appendChild(re)
 
             for newuri in dc.get_childs(self._uri):
-                pnames=dc.get_propnames(newuri)
-                re=self.mk_propname_response(newuri,pnames, doc)
+                pnames = dc.get_propnames(newuri)
+                re = self.mk_propname_response(newuri, pnames, doc)
                 ms.appendChild(re)
-        elif self._depth=='infinity':
+        elif self._depth == 'infinity':
             uri_list = [self._uri]
             while uri_list:
                 uri = uri_list.pop()
-                pnames=dc.get_propnames(uri)
-                re=self.mk_propname_response(uri,pnames, doc)
+                pnames = dc.get_propnames(uri)
+                re = self.mk_propname_response(uri, pnames, doc)
                 ms.appendChild(re)
                 uri_childs = self._dataclass.get_childs(uri)
                 if uri_childs:
@@ -138,10 +120,10 @@ class PROPFIND:
 
     def create_allprop(self):
         """ return a list of all properties """
-        self.proplist={}
-        self.namespaces=[]
-        for ns,plist in self._dataclass.get_propnames(self._uri).items():
-            self.proplist[ns]=plist
+        self.proplist = {}
+        self.namespaces = []
+        for ns, plist in self._dataclass.get_propnames(self._uri).items():
+            self.proplist[ns] = plist
             self.namespaces.append(ns)
 
         return self.create_prop()
@@ -153,7 +135,7 @@ class PROPFIND:
 
         1. set up the <multistatus>-Framework
 
-        2. read the property values for each URI 
+        2. read the property values for each URI
            (which is dependant on the Depth header)
            This is done by the get_propvalues() method.
 
@@ -162,39 +144,37 @@ class PROPFIND:
            document.
 
         We differ between "good" properties, which have been
-        assigned a value by the interface class and "bad" 
+        assigned a value by the interface class and "bad"
         properties, which resulted in an error, either 404
         (Not Found) or 403 (Forbidden).
 
         """
-
-
         # create the document generator
         doc = domimpl.createDocument(None, "multistatus", None)
         ms = doc.documentElement
         ms.setAttribute("xmlns:D", "DAV:")
         ms.tagName = 'D:multistatus'
 
-        if self._depth=="0":
-            gp,bp=self.get_propvalues(self._uri)
-            res=self.mk_prop_response(self._uri,gp,bp,doc)
+        if self._depth == "0":
+            gp, bp = self.get_propvalues(self._uri)
+            res = self.mk_prop_response(self._uri, gp, bp, doc)
             ms.appendChild(res)
 
-        elif self._depth=="1":
-            gp,bp=self.get_propvalues(self._uri)
-            res=self.mk_prop_response(self._uri,gp,bp,doc)
+        elif self._depth == "1":
+            gp, bp = self.get_propvalues(self._uri)
+            res = self.mk_prop_response(self._uri, gp, bp, doc)
             ms.appendChild(res)
 
             for newuri in self._dataclass.get_childs(self._uri):
-                gp,bp=self.get_propvalues(newuri)
-                res=self.mk_prop_response(newuri,gp,bp,doc)
+                gp, bp = self.get_propvalues(newuri)
+                res = self.mk_prop_response(newuri, gp, bp, doc)
                 ms.appendChild(res)
-        elif self._depth=='infinity':
+        elif self._depth == 'infinity':
             uri_list = [self._uri]
             while uri_list:
                 uri = uri_list.pop()
-                gp,bp=self.get_propvalues(uri)
-                res=self.mk_prop_response(uri,gp,bp,doc)
+                gp, bp = self.get_propvalues(uri)
+                res = self.mk_prop_response(uri, gp, bp, doc)
                 ms.appendChild(res)
                 uri_childs = self._dataclass.get_childs(uri)
                 if uri_childs:
@@ -202,100 +182,112 @@ class PROPFIND:
 
         return doc.toxml(encoding="utf-8")
 
-    def mk_propname_response(self,uri,propnames,doc):
-        """ make a new <prop> result element for a PROPNAME request 
+    def mk_propname_response(self, uri, propnames, doc):
+        """ make a new <prop> result element for a PROPNAME request
 
         This will simply format the propnames list.
         propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
 
         """
-        re=doc.createElement("D:response")
+        re = doc.createElement("D:response")
+
+        if self._dataclass.baseurl:
+            uri = self._dataclass.baseurl + '/' + '/'.join(uri.split('/')[3:])
 
         # write href information
-        uparts=urlparse.urlparse(uri)
-        fileloc=uparts[2]
-        href=doc.createElement("D:href")
-        huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
+        uparts = urlparse.urlparse(uri)
+        fileloc = uparts[2]
+        href = doc.createElement("D:href")
+
+        huri = doc.createTextNode(uparts[0] + '://' +
+                                  '/'.join(uparts[1:2]) +
+                                  urllib.quote(fileloc))
         href.appendChild(huri)
         re.appendChild(href)
 
-        ps=doc.createElement("D:propstat")
-        nsnum=0
+        ps = doc.createElement("D:propstat")
+        nsnum = 0
 
-        for ns,plist in propnames.items():
+        for ns, plist in propnames.items():
             # write prop element
-            pr=doc.createElement("D:prop")
-            nsp="ns"+str(nsnum)
-            pr.setAttribute("xmlns:"+nsp,ns)
-            nsnum=nsnum+1
+            pr = doc.createElement("D:prop")
+            nsp = "ns" + str(nsnum)
+            pr.setAttribute("xmlns:" + nsp, ns)
+            nsnum += 1
 
-        # write propertynames
-        for p in plist:
-            pe=doc.createElement(nsp+":"+p)
-            pr.appendChild(pe)
+            # write propertynames
+            for p in plist:
+                pe = doc.createElement(nsp + ":" + p)
+                pr.appendChild(pe)
 
-        ps.appendChild(pr)
+            ps.appendChild(pr)
         re.appendChild(ps)
 
         return re
 
-    def mk_prop_response(self,uri,good_props,bad_props,doc):
-        """ make a new <prop> result element 
+    def mk_prop_response(self, uri, good_props, bad_props, doc):
+        """ make a new <prop> result element
 
         We differ between the good props and the bad ones for
         each generating an extra <propstat>-Node (for each error
         one, that means).
 
         """
-        re=doc.createElement("D:response")
+        re = doc.createElement("D:response")
         # append namespaces to response
-        nsnum=0
+        nsnum = 0
         for nsname in self.namespaces:
             if nsname != 'DAV:':
-                re.setAttribute("xmlns:ns"+str(nsnum),nsname)
-            nsnum=nsnum+1
+                re.setAttribute("xmlns:ns" + str(nsnum), nsname)
+            nsnum += 1
+
+        if self._dataclass.baseurl:
+            uri = self._dataclass.baseurl + '/' + '/'.join(uri.split('/')[3:])
 
         # write href information
-        uparts=urlparse.urlparse(uri)
-        fileloc=uparts[2]
-        href=doc.createElement("D:href")
-        huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
+        uparts = urlparse.urlparse(uri)
+        fileloc = uparts[2]
+        href = doc.createElement("D:href")
+
+        huri = doc.createTextNode(uparts[0] + '://' +
+                                  '/'.join(uparts[1:2]) +
+                                  urllib.quote(fileloc))
         href.appendChild(huri)
         re.appendChild(href)
 
         # write good properties
-        ps=doc.createElement("D:propstat")
+        ps = doc.createElement("D:propstat")
         if good_props:
             re.appendChild(ps)
 
-        gp=doc.createElement("D:prop")
+        gp = doc.createElement("D:prop")
         for ns in good_props.keys():
             if ns != 'DAV:':
-                ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+                ns_prefix = "ns" + str(self.namespaces.index(ns)) + ":"
             else:
                 ns_prefix = 'D:'
-            for p,v in good_props[ns].items():
+            for p, v in good_props[ns].items():
 
-                pe=doc.createElement(ns_prefix+str(p))
-                if hasattr(v, '__class__') and v.__class__.__name__ == 'Element':
+                pe = doc.createElement(ns_prefix + str(p))
+                if isinstance(v, xml.dom.minidom.Element):
                     pe.appendChild(v)
                 elif isinstance(v, list):
                     for val in v:
                         pe.appendChild(val)
                 else:
-                    if p=="resourcetype":
-                        if v==1:
-                            ve=doc.createElement("D:collection")
+                    if p == "resourcetype":
+                        if v == 1:
+                            ve = doc.createElement("D:collection")
                             pe.appendChild(ve)
                     else:
-                        ve=doc.createTextNode(v)
+                        ve = doc.createTextNode(v)
                         pe.appendChild(ve)
 
                 gp.appendChild(pe)
 
         ps.appendChild(gp)
-        s=doc.createElement("D:status")
-        t=doc.createTextNode("HTTP/1.1 200 OK")
+        s = doc.createElement("D:status")
+        t = doc.createTextNode("HTTP/1.1 200 OK")
         s.appendChild(t)
         ps.appendChild(s)
         re.appendChild(ps)
@@ -305,23 +297,23 @@ class PROPFIND:
 
             # write a propstat for each error code
             for ecode in bad_props.keys():
-                ps=doc.createElement("D:propstat")
+                ps = doc.createElement("D:propstat")
                 re.appendChild(ps)
-                bp=doc.createElement("D:prop")
+                bp = doc.createElement("D:prop")
                 ps.appendChild(bp)
 
                 for ns in bad_props[ecode].keys():
                     if ns != 'DAV:':
-                        ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+                        ns_prefix = "ns" + str(self.namespaces.index(ns)) + ":"
                     else:
                         ns_prefix = 'D:'
 
                     for p in bad_props[ecode][ns]:
-                        pe=doc.createElement(ns_prefix+str(p))
+                        pe = doc.createElement(ns_prefix + str(p))
                         bp.appendChild(pe)
 
-                s=doc.createElement("D:status")
-                t=doc.createTextNode(utils.gen_estring(ecode))
+                s = doc.createElement("D:status")
+                t = doc.createTextNode(utils.gen_estring(ecode))
                 s.appendChild(t)
                 ps.appendChild(s)
                 re.appendChild(ps)
@@ -329,8 +321,8 @@ class PROPFIND:
         # return the new response element
         return re
 
-    def get_propvalues(self,uri):
-        """ create lists of property values for an URI 
+    def get_propvalues(self, uri):
+        """ create lists of property values for an URI
 
         We create two lists for an URI: the properties for
         which we found a value and the ones for which we
@@ -338,30 +330,30 @@ class PROPFIND:
         found or the user is not allowed to read them.
 
         """
-        good_props={}
-        bad_props={}
+        good_props = {}
+        bad_props = {}
 
         ddc = self._dataclass
-        for (ns,plist) in self.proplist.items():
-            good_props[ns]={}
+        for ns, plist in self.proplist.items():
+            good_props[ns] = {}
             for prop in plist:
                 ec = 0
                 try:
-                    r=ddc.get_prop(uri,ns,prop)
-                    good_props[ns][prop]=r
+                    r = ddc.get_prop(uri, ns, prop)
+                    good_props[ns][prop] = r
                 except DAV_Error, error_code:
-                    ec=error_code[0]
+                    ec = error_code[0]
 
                 # ignore props with error_code if 0 (invisible)
-                if ec==0: continue
+                if ec == 0:
+                    continue
 
-                if bad_props.has_key(ec):
-                    if bad_props[ec].has_key(ns):
+                if ec in bad_props:
+                    if ns in bad_props[ec]:
                         bad_props[ec][ns].append(prop)
                     else:
-                        bad_props[ec][ns]=[prop]
+                        bad_props[ec][ns] = [prop]
                 else:
-                    bad_props[ec]={ns:[prop]}
+                    bad_props[ec] = {ns: [prop]}
 
         return good_props, bad_props
-
diff --git a/DAV/report.py b/pywebdav/lib/report.py
similarity index 84%
rename from DAV/report.py
rename to pywebdav/lib/report.py
index 39457b7..a34a04e 100644
--- a/DAV/report.py
+++ b/pywebdav/lib/report.py
@@ -1,25 +1,8 @@
-#Copyright (c) 2009 Cedric Krier (cedric.krier at b2ck.com)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 from propfind import PROPFIND
 from xml.dom import minidom
 domimpl = minidom.getDOMImplementation()
-from utils import get_parenturi
 
+from utils import get_parenturi
 
 class REPORT(PROPFIND):
 
@@ -135,4 +118,3 @@ class REPORT(PROPFIND):
 
         return doc.toxml(encoding="utf-8")
 
-
diff --git a/pywebdav/lib/status.py b/pywebdav/lib/status.py
new file mode 100644
index 0000000..bbfda33
--- /dev/null
+++ b/pywebdav/lib/status.py
@@ -0,0 +1,24 @@
+
+STATUS_CODES={
+        100:    "Continue",
+        102:    "Processing",
+        200:    "Ok",
+        201:    "Created",
+        204:    "No Content",
+        207:    "Multi-Status",
+        201:    "Created",
+        400:    "Bad Request",
+        403:    "Forbidden",
+        404:    "Not Found",
+        405:    "Method Not Allowed",
+        409:    "Conflict",
+        412:    "Precondition failed",
+        423:    "Locked",
+        415:    "Unsupported Media Type",
+        507:    "Insufficient Storage",
+        422:    "Unprocessable Entity",
+        423:    "Locked",
+        424:    "Failed Dependency",
+        502:    "Bad Gateway",
+        507:    "Insufficient Storage"
+}
diff --git a/DAV/utils.py b/pywebdav/lib/utils.py
similarity index 88%
rename from DAV/utils.py
rename to pywebdav/lib/utils.py
index 53da88d..43394aa 100755
--- a/DAV/utils.py
+++ b/pywebdav/lib/utils.py
@@ -1,20 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import time
 import re
 
@@ -27,9 +10,6 @@ from StringIO import StringIO
 from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
 from BaseHTTPServer import BaseHTTPRequestHandler
 
-VERSION = '0.9.4-dev'
-AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
-
 def gen_estring(ecode):
     """ generate an error string from the given code """
     ec=atoi(str(ecode))
diff --git a/pywebdav/server/__init__.py b/pywebdav/server/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/pywebdav/server/__init__.py
@@ -0,0 +1 @@
+
diff --git a/DAVServer/config.ini b/pywebdav/server/config.ini
similarity index 100%
rename from DAVServer/config.ini
rename to pywebdav/server/config.ini
diff --git a/DAVServer/daemonize.py b/pywebdav/server/daemonize.py
similarity index 100%
rename from DAVServer/daemonize.py
rename to pywebdav/server/daemonize.py
diff --git a/DAVServer/fileauth.py b/pywebdav/server/fileauth.py
similarity index 94%
rename from DAVServer/fileauth.py
rename to pywebdav/server/fileauth.py
index f2a649d..4fde33f 100644
--- a/DAVServer/fileauth.py
+++ b/pywebdav/server/fileauth.py
@@ -22,12 +22,13 @@ This is an example implementation of a DAVserver using the DAV package.
 
 """
 
+import sys
 import logging
 
-from DAV.WebDAVServer import DAVRequestHandler
+from pywebdav.lib.WebDAVServer import DAVRequestHandler
+from pywebdav.lib.dbconn import Mconn
+
 from fshandler import FilesystemHandler
-import sys
-from DAV.dbconn import Mconn
 
 log = logging.getLogger()
 
diff --git a/DAVServer/fshandler.py b/pywebdav/server/fshandler.py
similarity index 90%
rename from DAVServer/fshandler.py
rename to pywebdav/server/fshandler.py
index 58323b7..40ecc09 100644
--- a/DAVServer/fshandler.py
+++ b/pywebdav/server/fshandler.py
@@ -1,20 +1,3 @@
-#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
-
 import sys
 import urlparse
 import os
@@ -24,11 +7,10 @@ import logging
 import types
 import shutil
 
-from DAV.constants import COLLECTION, OBJECT
-from DAV.errors import *
-from DAV.iface import *
-
-from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
+from pywebdav.lib.constants import COLLECTION, OBJECT
+from pywebdav.lib.errors import *
+from pywebdav.lib.iface import *
+from pywebdav.lib.davcmd import copyone, copytree, moveone, movetree, delone, deltree
 
 log = logging.getLogger(__name__)
 
@@ -38,7 +20,9 @@ MAGIC_AVAILABLE = False
 try:
     import mimetypes
     MAGIC_AVAILABLE = True
+    log.info('Mimetype support ENABLED')
 except ImportError:
+    log.info('Mimetype support DISABLED')
     pass
 
 class Resource(object):
@@ -87,7 +71,7 @@ class FilesystemHandler(dav_interface):
 
         # should we be verbose?
         self.verbose = verbose
-        log.info('Initialized with %s-%s' % (directory, uri))
+        log.info('Initialized with %s %s' % (directory, uri))
 
     def setDirectory(self, path):
         """ Sets the directory """
@@ -121,7 +105,7 @@ class FilesystemHandler(dav_interface):
         return uri
 
 
-    def get_childs(self,uri):
+    def get_childs(self, uri, filter=None):
         """ return the child objects as self.baseuris for the given URI """
 
         fileloc=self.uri2local(uri)
@@ -144,6 +128,7 @@ class FilesystemHandler(dav_interface):
 
     def get_data(self,uri, range = None):
         """ return the content of an object """
+
         path=self.uri2local(uri)
         if os.path.exists(path):
             if os.path.isfile(path):
@@ -173,6 +158,11 @@ class FilesystemHandler(dav_interface):
                     fp.seek(range[0])
                     log.info('Serving range %s -> %s content of %s' % (range[0], range[1], uri))
                     return Resource(fp, range[1] - range[0])
+            elif os.path.isdir(path):
+                # GET for collections is defined as 'return s/th meaningful' :-)
+                from StringIO import StringIO
+                stio = StringIO('Directory at %s' % uri)
+                return Resource(StringIO('Directory at %s' % uri), stio.len)
             else:
                 # also raise an error for collections
                 # don't know what should happen then..
@@ -399,9 +389,9 @@ class FilesystemHandler(dav_interface):
         dstfile=self.uri2local(dst)
         try:
             shutil.copy(srcfile, dstfile)
-        except OSError:
+        except (OSError, IOError):
             log.info('copy: forbidden')
-            raise DAV_Error, Forbidden
+            raise DAV_Error, 409
 
     def copycol(self, src, dst):
         """ copy a collection.
diff --git a/DAVServer/mysqlauth.py b/pywebdav/server/mysqlauth.py
similarity index 93%
rename from DAVServer/mysqlauth.py
rename to pywebdav/server/mysqlauth.py
index 7a86199..ab2d7d4 100644
--- a/DAVServer/mysqlauth.py
+++ b/pywebdav/server/mysqlauth.py
@@ -33,8 +33,8 @@ class MySQLAuthHandler(DAVAuthHandler):
         if self.verbose:
             print >>sys.stderr,user,command
 
-        qry="select * from %s.Users where User=%%s and Pass=%%s" % Mysql.dbtable
-        Auth=DB.execute(qry, (user, pw))
+        qry="select * from %s.Users where User='%s' and Pass='%s'"%(Mysql.dbtable,user,pw)
+        Auth=DB.execute(qry)
 
         if len(Auth) == 1:
             can_write=Auth[0][3]
diff --git a/DAVServer/server.py b/pywebdav/server/server.py
similarity index 84%
rename from DAVServer/server.py
rename to pywebdav/server/server.py
index 1fbe47e..c5d3c97 100755
--- a/DAVServer/server.py
+++ b/pywebdav/server/server.py
@@ -1,20 +1,4 @@
 #!/usr/bin/env python
-#Copyright (c) 1999-2005 Christian Scholz (cs at comlounge.net)
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU Library General Public
-#License as published by the Free Software Foundation; either
-#version 2 of the License, or (at your option) any later version.
-#
-#This library 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
-#Library General Public License for more details.
-#
-#You should have received a copy of the GNU Library General Public
-#License along with this library; if not, write to the Free
-#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-#MA 02111-1307, USA
 
 """
 Python WebDAV Server.
@@ -33,20 +17,18 @@ from BaseHTTPServer import HTTPServer
 from SocketServer import ThreadingMixIn
 
 try:
-    import DAV
+    import pywebdav.lib
 except ImportError:
-    print 'DAV package not found! Please install into site-packages or set PYTHONPATH!'
+    print 'pywebdav.lib package not found! Please install into site-packages or set PYTHONPATH!'
     sys.exit(2)
 
-from DAV.utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__  = AUTHOR
-
 from fileauth import DAVAuthHandler
 from mysqlauth import MySQLAuthHandler
 from fshandler import FilesystemHandler
 from daemonize import startstop
-from DAV.INI_Parse import Configuration
+
+from pywebdav.lib.INI_Parse import Configuration
+from pywebdav.lib import VERSION, AUTHOR
 
 LEVELS = {'debug': logging.DEBUG,
           'info': logging.INFO,
@@ -105,6 +87,10 @@ def runserver(
         handler.IFACE_CLASS.mimecheck = False
         log.info('Disabled mimetype sniffing (All files will have type application/octet-stream)')
 
+    if handler._config.DAV.baseurl:
+        log.info('Using %s as base url for PROPFIND requests' % handler._config.DAV.baseurl)
+    handler.IFACE_CLASS.baseurl = handler._config.DAV.baseurl
+
     # initialize server on specified port
     runner = server( (host, port), handler )
     print('Listening on %s (%i)' % (host, port))
@@ -130,6 +116,10 @@ Parameters:
                     The user that runs this server must have permissions
                     on that directory. NEVER run as root!
                     Default directory is /tmp
+    -B, --baseurl   Behind a proxy pywebdav needs to generate other URIs for PROPFIND.
+                    If you are experiencing problems with links or such when behind
+                    a proxy then just set this to a sensible default (e.g. http://dav.domain.com).
+                    Make sure that you include the protocol.
     -H, --host      Host where to listen on (default: localhost)
     -P, --port      Port to bind server to  (default: 8008)
     -u, --user      Username for authentication
@@ -139,11 +129,12 @@ Parameters:
     -m, --mysql     Pass this parameter if you want MySQL based authentication.
                     If you want to use MySQL then the usage of a configuration
                     file is mandatory.
-    -J, --lockemu   Activate experimental LOCK and UNLOCK mode (WebDAV Version 2).
-                    Currently know to work but needs more tests. Default is ON.
+    -J, --nolock    Deactivate LOCK and UNLOCK mode (WebDAV Version 2).
     -M, --nomime    Deactivate mimetype sniffing. Sniffing is based on magic numbers
                     detection but can be slow under heavy load. If you are experiencing
                     speed problems try to use this parameter.
+    -T, --noiter    Deactivate iterator. Use this if you encounter file corruption during 
+                    download. Also disables chunked body response.
     -i, --icounter  If you want to run multiple instances then you have to
                     give each instance it own number so that logfiles and such
                     can be identified. Default is 0
@@ -156,13 +147,13 @@ Parameters:
                         restart - Restart complete server
                         status  - Returns status of server
 
-    -v, --verbose   Be verbose
+    -v, --verbose   Be verbose.
     -l, --loglevel  Select the log level : DEBUG, INFO, WARNING, ERROR, CRITICAL
                     Default is WARNING
     -h, --help      Show this screen
 
 Please send bug reports and feature requests to %s
-""" % (__version__, __author__)
+""" % (VERSION, AUTHOR)
 
 def setupDummyConfig(**kw):
 
@@ -191,16 +182,20 @@ def run():
     counter = 0
     mysql = False
     lockemulation = True
+    http_response_use_iterator = True
+    chunked_http_response = True
     configfile = ''
     mimecheck = True
     loglevel = 'warning'
+    baseurl = ''
 
     # parse commandline
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:Ml:',
+        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:Ml:TB:',
                 ['host=', 'port=', 'directory=', 'user=', 'password=',
                  'daemon=', 'noauth', 'help', 'verbose', 'mysql', 
-                 'icounter=', 'config=', 'lockemu', 'nomime', 'loglevel'])
+                 'icounter=', 'config=', 'nolock', 'nomime', 'loglevel', 'noiter',
+                 'baseurl='])
     except getopt.GetoptError, e:
         print usage
         print '>>>> ERROR: %s' % str(e)
@@ -216,8 +211,12 @@ def run():
         if o in ['-M', '--nomime']:
             mimecheck = False
 
-        if o in ['-J', '--lockemu']:
-            lockemulation = True
+        if o in ['-J', '--nolock']:
+            lockemulation = False
+
+        if o in ['-T', '--noiter']:
+            http_response_use_iterator = False
+            chunked_http_response = False
 
         if o in ['-c', '--config']:
             configfile = a
@@ -254,11 +253,11 @@ def run():
             daemonize = True
             daemonaction = a
 
-    chunked_http_response = 1
+        if o in ['-B', '--baseurl']:
+            baseurl = a.lower()
 
     # This feature are disabled because they are unstable
     http_request_use_iterator = 0
-    http_response_use_iterator = 0
 
     conf = None
     if configfile != '':
@@ -306,7 +305,8 @@ def run():
                 'mimecheck' : mimecheck,
                 'chunked_http_response': chunked_http_response,
                 'http_request_use_iterator': http_request_use_iterator,
-                'http_response_use_iterator': http_response_use_iterator
+                'http_response_use_iterator': http_response_use_iterator,
+                'baseurl' : baseurl
                 }
 
         conf = setupDummyConfig(**_dc)
@@ -316,20 +316,24 @@ def run():
 
     logging.getLogger().setLevel(LEVELS[loglevel])
 
+    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+    for handler in logging.getLogger().handlers:
+        handler.setFormatter(formatter)
+
     if mysql == True and configfile == '':
         log.error('You can only use MySQL with configuration file!')
         sys.exit(3)
 
     if daemonaction != 'stop':
-        log.info('Starting up PyWebDAV server (version %s)' % __version__)
+        log.info('Starting up PyWebDAV server (version %s)' % VERSION)
     else:
-        log.info('Stopping PyWebDAV server (version %s)' % __version__)
+        log.info('Stopping PyWebDAV server (version %s)' % VERSION)
 
     if not noauth and daemonaction not in ['status', 'stop']:
         if not user:
             print usage
-            print >>sys.stderr, '>> ERROR: No usable parameter specified!'
-            print >>sys.stderr, '>> Example: davserver -D /home/files -n'
+            print >>sys.stderr, '>> ERROR: No parameter specified!'
+            print >>sys.stderr, '>> Example: davserver -D /tmp -n'
             sys.exit(3)
 
     if daemonaction == 'status':
diff --git a/setup.py b/setup.py
index 6357c73..ea8f87e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,12 +1,12 @@
 #!/usr/bin/env python
 
-try: 
-   from ez_setup import use_setuptools 
-   use_setuptools() 
-except ImportError: 
-   pass 
+try:
+    from ez_setup import use_setuptools
+    use_setuptools()
+except ImportError:
+    pass
 
-from setuptools import setup
+from setuptools import setup, find_packages
 
 VERSION = open('VERSION', 'r').read()
 VERSION = VERSION.replace('\n', '')
@@ -14,12 +14,12 @@ VERSION = VERSION.replace('\n', '')
 CHANGES = open('doc/Changes', 'r').read()
 
 DOC = """
-WebDAV library for python. 
+WebDAV library for python.
 
 Consists of a *server* that is ready to run
 Serve and the DAV package that provides WebDAV server(!) functionality.
 
-Currently supports 
+Currently supports
 
     * WebDAV level 1
     * Level 2 (LOCK, UNLOCK)
@@ -38,8 +38,9 @@ This package does *not* provide client functionality.
 Installation
 ============
 
-After installation of this package you will have a new script in you $PYTHON/bin directory called
-*davserver*. This serves as the main entry point to the server.
+After installation of this package you will have a new script in you
+$PYTHON/bin directory called *davserver*. This serves as the main entry point
+to the server.
 
 Examples
 ========
@@ -64,7 +65,6 @@ Changes
 %s
 """ % CHANGES
 
-from distutils.core import setup
 setup(name='PyWebDAV',
       description='WebDAV library including a standalone server for python',
       author='Simon Pamies',
@@ -76,30 +76,31 @@ setup(name='PyWebDAV',
       license='GPL v2',
       version=VERSION,
       long_description=DOC,
-      classifiers = [
-          'Development Status :: 5 - Production/Stable',
-          'Environment :: Console',
-          'Environment :: Web Environment',
-          'Intended Audience :: Developers',
-          'Intended Audience :: System Administrators',
-          'License :: OSI Approved :: GNU General Public License (GPL)',
-          'Operating System :: MacOS :: MacOS X',
-          'Operating System :: POSIX',
-          'Programming Language :: Python',
-          'Topic :: Software Development :: Libraries'
-          ],
-      keywords = ['webdav',
-                  'server',
-                  'dav',
-                  'standalone',
-                  'library',
-                  'gpl',
-                  'http',
-                  'rfc2518',
-                  'rfc 2518'],
-      packages=['DAV', 'DAVServer'],
+      classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Console',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'Intended Audience :: System Administrators',
+        'License :: OSI Approved :: GNU General Public License (GPL)',
+        'Operating System :: MacOS :: MacOS X',
+        'Operating System :: POSIX',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Libraries',
+        ],
+      keywords=['webdav',
+                'server',
+                'dav',
+                'standalone',
+                'library',
+                'gpl',
+                'http',
+                'rfc2518',
+                'rfc 2518'
+                ],
+      packages=find_packages(),
       zip_safe=False,
       entry_points={
-          'console_scripts' : ['davserver = DAVServer.server:run']
-      }
+        'console_scripts': ['davserver = pywebdav.server.server:run']
+        }
       )
commit 1d109ec1e7ed4af8d1f6e1c19d6a62702683c2c0
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Fri Feb 18 17:05:40 2011 +0100

    Adding upstream version 0.9.4.1.

diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
index dff2ddd..b6815f2 100644
--- a/DAV/AuthServer.py
+++ b/DAV/AuthServer.py
@@ -98,6 +98,7 @@ class AuthRequestHandler:
 
         # needed by send_error
         self.command = requestline
+        self.headers = {}
 
         if requestline[-2:] == '\r\n':
             requestline = requestline[:-2]
diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
index 89e5a47..481e32a 100644
--- a/DAV/WebDAVServer.py
+++ b/DAV/WebDAVServer.py
@@ -437,17 +437,19 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         if self._l_isLocked(uri):
             return self.send_body(None, '423', 'Locked', 'Locked')
 
-        dl = DELETE(uri,dc)
-        if dc.is_collection(uri):
-            res=dl.delcol()
-            if res:
-                self.send_status(207,body=res)
+        try:
+            dl = DELETE(uri,dc)
+            if dc.is_collection(uri):
+                res=dl.delcol()
+                if res:
+                    self.send_status(207,body=res)
+                else:
+                    self.send_status(204)
             else:
-                self.send_status(204)
-        else:
-            res=dl.delone() or 204
-            self.send_status(res)
-
+                res=dl.delone() or 204
+                self.send_status(res) 
+        except DAV_NotFound:
+            self.send_body(None, '404', 'Not Found', 'Not Found')
 
     def do_PUT(self):
         dc=self.IFACE_CLASS
diff --git a/DAV/dbconn.py b/DAV/dbconn.py
index 17dbee0..2541d3f 100644
--- a/DAV/dbconn.py
+++ b/DAV/dbconn.py
@@ -16,34 +16,37 @@
 #MA 02111-1307, USA
 
 import logging
+import sys
 
 log = logging.getLogger(__name__)
 
 try:
     import MySQLdb
 except ImportError:
+    log.info('No SQL support - MySQLdb missing...')
     pass
 
-import sys
-
 class Mconn:
+
     def connect(self,username,userpasswd,host,port,db):
-        try: connection = MySQLdb.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
+        try: 
+            connection = DB.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
         except MySQLdb.OperationalError, message:
             log.error("%d:\n%s" % (message[ 0 ], message[ 1 ] ))
             return 0
         else:
             self.db = connection.cursor()
-
             return 1
 
-    def execute(self,qry):
+    def execute(self, qry, parameters):
         if self.db:
-            try: res=self.db.execute(qry)
+            try: 
+                # parameters are escaped by DB API correctly
+                res = self.db.execute(qry, parameters)
+
             except MySQLdb.OperationalError, message:
                 log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
                 return 0
-
             except MySQLdb.ProgrammingError, message:
                 log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
                 return 0
@@ -53,11 +56,11 @@ class Mconn:
                 return self.db.fetchall()
 
     def create_user(self,user,passwd):
-        qry="select * from Users where User='%s'"%(user)
-        res=self.execute(qry)
+        qry="select * from Users where User=%s"
+        res=self.execute(qry, user)
         if not res or len(res) ==0:
-            qry="insert into Users (User,Pass) values('%s','%s')"%(user,passwd)
-            res=self.execute(qry)
+            qry="insert into Users (User,Pass) values (%s,%s)"
+            res=self.execute(qry, (user, passwd))
         else:
             log.debug("Username already in use")
 
@@ -69,8 +72,7 @@ class Mconn:
             `Write` tinyint(1) default '0',
                   PRIMARY KEY  (`uid`)
                 ) ENGINE=MyISAM DEFAULT CHARSET=latin1"""
-        self.execute(qry)
-
+        self.execute(qry, tuple())
 
     def first_run(self,user,passwd):
         res= self.execute('select * from Users')
@@ -80,7 +82,7 @@ class Mconn:
             self.create_table()
             self.create_user(user,passwd)
 
-
     def __init__(self,user,password,host,port,db):
         self.db=0
         self.connect(user,password,host,port,db)
+
diff --git a/DAV/iface.py b/DAV/iface.py
index 8151527..e3582cb 100644
--- a/DAV/iface.py
+++ b/DAV/iface.py
@@ -101,7 +101,7 @@ class dav_interface:
     ### DATA methods (for GET and PUT)
     ###
 
-    def get_data(self,uri):
+    def get_data(self, uri, range=None):
         """ return the content of an object
 
         return data or raise an exception
@@ -109,7 +109,7 @@ class dav_interface:
         """
         raise DAV_NotFound
 
-    def put(self,uri,data):
+    def put(self, uri, data, content_type=None):
         """ write an object to the repository
 
         return the location uri or raise an exception
diff --git a/DAVServer/fshandler.py b/DAVServer/fshandler.py
index 70e366f..58323b7 100644
--- a/DAVServer/fshandler.py
+++ b/DAVServer/fshandler.py
@@ -153,16 +153,16 @@ class FilesystemHandler(dav_interface):
                     log.info('Serving content of %s' % uri)
                     return Resource(fp, file_size)
                 else:
-                    if range[0] == '':
-                        range[0] = 0
-                    else:
-                        range[0] = int(range[0])
-
                     if range[1] == '':
                         range[1] = file_size
                     else:
                         range[1] = int(range[1])
 
+                    if range[0] == '':
+                        range[0] = file_size - range[1]
+                    else:
+                        range[0] = int(range[0])
+
                     if range[0] > file_size:
                         raise DAV_Requested_Range_Not_Satisfiable
 
diff --git a/DAVServer/mysqlauth.py b/DAVServer/mysqlauth.py
index ab2d7d4..7a86199 100644
--- a/DAVServer/mysqlauth.py
+++ b/DAVServer/mysqlauth.py
@@ -33,8 +33,8 @@ class MySQLAuthHandler(DAVAuthHandler):
         if self.verbose:
             print >>sys.stderr,user,command
 
-        qry="select * from %s.Users where User='%s' and Pass='%s'"%(Mysql.dbtable,user,pw)
-        Auth=DB.execute(qry)
+        qry="select * from %s.Users where User=%%s and Pass=%%s" % Mysql.dbtable
+        Auth=DB.execute(qry, (user, pw))
 
         if len(Auth) == 1:
             can_write=Auth[0][3]
diff --git a/PKG-INFO b/PKG-INFO
index 946c108..128ea8d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.4
+Version: 0.9.4.1
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -55,6 +55,13 @@ Description:
         =======
         
         
+        0.9.4.1 (Feb 17 2011)
+        -------------------
+        
+        Fixed MySQL injection possibility in MySQLAuthHandler
+        Found by Teeed <op.pl> filed under CVE-2011-C0432
+        [Simon Pamies]
+        
         0.9.4 (April 15 2010)
         ---------------------
         
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
index 946c108..128ea8d 100644
--- a/PyWebDAV.egg-info/PKG-INFO
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.4
+Version: 0.9.4.1
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -55,6 +55,13 @@ Description:
         =======
         
         
+        0.9.4.1 (Feb 17 2011)
+        -------------------
+        
+        Fixed MySQL injection possibility in MySQLAuthHandler
+        Found by Teeed <op.pl> filed under CVE-2011-C0432
+        [Simon Pamies]
+        
         0.9.4 (April 15 2010)
         ---------------------
         
diff --git a/VERSION b/VERSION
index a602fc9..199bf2a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.4
+0.9.4.1
diff --git a/doc/Changes b/doc/Changes
index 85a6769..a346e06 100644
--- a/doc/Changes
+++ b/doc/Changes
@@ -1,4 +1,11 @@
 
+0.9.4.1 (Feb 17 2011)
+-------------------
+
+Fixed MySQL injection possibility in MySQLAuthHandler
+Found by Teeed <op.pl> filed under CVE-2011-C0432
+[Simon Pamies]
+
 0.9.4 (April 15 2010)
 ---------------------
   
commit b04235b2e5e2eea20964f436d5a2fa9177dc418d
Author: Daniel Baumann <daniel at debian.org>
Date:   Fri Apr 23 07:11:52 2010 +0200

    Adding upstream version 0.9.4.

diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
index 2058ede..dff2ddd 100644
--- a/DAV/AuthServer.py
+++ b/DAV/AuthServer.py
@@ -1,9 +1,24 @@
-#!/usr/bin/env python
+#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 """
     Authenticating HTTP Server
 
-    This module builds on BaseHTTPServer and implements 
+    This module builds on BaseHTTPServer and implements
     basic authentication
 
 """
@@ -51,7 +66,7 @@ class AuthRequestHandler:
     ok or not. None means that the user is not authorized.
     """
 
-    # False means no authentiation 
+    # False means no authentiation
     DO_AUTH=1
 
     AUTH_ERROR_MSG="""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
@@ -76,7 +91,7 @@ class AuthRequestHandler:
         """
         Special handle method with buffering and authentication
         """
-        
+
         self.raw_requestline = self.rfile.readline()
         self.request_version = version = "HTTP/0.9" # Default
         requestline = self.raw_requestline
@@ -105,7 +120,7 @@ class AuthRequestHandler:
         else:
             self.send_error(400, "Bad request syntax (%s)" % `requestline`)
             return
-        
+
         self.command, self.path, self.request_version = command, path, version
         self.headers = self.MessageClass(self.rfile, 0)
 
@@ -139,7 +154,6 @@ class AuthRequestHandler:
         """Override send_response to use the correct http version
            in the response."""
 
-        self.log_request(code)
         if message is None:
             if self.responses.has_key(code):
                 message = self.responses[code][0]
@@ -195,7 +209,7 @@ class AuthRequestHandler:
         self.send_header("WWW-Authenticate","Basic realm=\"PyWebDAV\"")
         self.send_header("Content-Type", 'text/html')
         self.end_headers()
-        
+
         lines=split(emsg,"\n")
         for l in lines:
             self._append("%s\r\n" %l)
@@ -204,7 +218,7 @@ class AuthRequestHandler:
         """Checks if the given user and the given
         password are allowed to access.
         """
-        
+
         # Always reject
         return None
 
diff --git a/DAV/BufferingHTTPServer.py b/DAV/BufferingHTTPServer.py
index db96618..897fe59 100644
--- a/DAV/BufferingHTTPServer.py
+++ b/DAV/BufferingHTTPServer.py
@@ -1,22 +1,22 @@
-#!/usr/bin/env python
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 """
     Buffering HTTP Server
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 """
 
@@ -40,15 +40,21 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
 
     """
 
+    responses = dict(
+        BaseHTTPRequestHandler.responses.items() +
+        {
+            424: ('Failed Dependency', 'The request failed due to failure of a previous request')
+        }.items()
+    )
 
     def _init_buffer(self):
-        """initialize the buffer. 
-        
+        """initialize the buffer.
+
         If you override the handle() method remember to call
         this (see below)
         """
         self.__buffer=""
-    
+
     def _append(self,s):
         """ append a string to the buffer """
         self.__buffer=self.__buffer+s
@@ -88,7 +94,7 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
         if self.request_version != 'HTTP/0.9':
             self._append("%s %s %s\r\n" %
                     (self.protocol_version, str(code), message))
-        
+
         self.send_header('Server', self.version_string())
         self.send_header('Connection', 'close')
         self.send_header('Date', self.date_time_string())
diff --git a/DAV/INI_Parse.py b/DAV/INI_Parse.py
index 03cdff8..efd61fb 100644
--- a/DAV/INI_Parse.py
+++ b/DAV/INI_Parse.py
@@ -1,21 +1,19 @@
-#!/usr/bin/python
-
 from ConfigParser import SafeConfigParser
 
 class Configuration:
-    def __init__ (self, fileName):
+    def __init__(self, fileName):
         cp = SafeConfigParser()
         cp.read(fileName)
         self.__parser = cp
         self.fileName = fileName
-        
-    def __getattr__ (self, name):
+
+    def __getattr__(self, name):
         if name in self.__parser.sections():
             return Section(name, self.__parser)
         else:
             return None
-            
-    def __str__ (self):
+
+    def __str__(self):
         p = self.__parser
         result = []
         result.append('<Configuration from %s>' % self.fileName)
@@ -26,14 +24,35 @@ class Configuration:
         return '\n'.join(result)
 
 class Section:
-    def __init__ (self, name, parser):
+    def __init__(self, name, parser):
         self.name = name
         self.__parser = parser
-    def __getattr__ (self, name):
+
+    def __getattr__(self, name):
         return self.__parser.get(self.name, name)
 
+    def __str__(self):
+        return str(self.__repr__())
+
+    def __repr__(self):
+        return self.__parser.items(self.name)
+
+    def getboolean(self, name):
+        return self.__parser.getboolean(self.name, name)
+
+    def __contains__(self, name):
+        return self.__parser.has_option(self.name, name)
+
+    def get(self, name, default):
+        if name in self:
+            return self.__getattr__(name)
+        else:
+            return default
+
+    def set(self, name, value):
+        self.__parser.set(self.name, name, str(value))
+
 # Test
 if __name__ == '__main__':
     c = Configuration('Importador.ini')
     print c.Origem.host, c.Origem.port
-    
\ No newline at end of file
diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
index bbc60ec..89e5a47 100644
--- a/DAV/WebDAVServer.py
+++ b/DAV/WebDAVServer.py
@@ -1,25 +1,10 @@
 """
     Python WebDAV Server.
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
 
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
+    This module builds on AuthServer by implementing the standard DAV
+    methods.
 
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-This module builds on AuthServer by implementing the standard DAV
-methods.
-
-Subclass this class and specify an IFACE_CLASS. See example.
+    Subclass this class and specify an IFACE_CLASS. See example.
 
 """
 
@@ -41,6 +26,7 @@ import AuthServer
 import urlparse
 import urllib
 import random
+import logging
 
 from propfind import PROPFIND
 from report import REPORT
@@ -48,7 +34,7 @@ from delete import DELETE
 from davcopy import COPY
 from davmove import MOVE
 
-from utils import rfc1123_date
+from utils import rfc1123_date, IfParser, tokenFinder
 from string import atoi,split
 from errors import *
 
@@ -57,9 +43,13 @@ from locks import LockManager
 import gzip
 import StringIO
 
+log = logging.getLogger(__name__)
+
+BUFFER_SIZE = 128 * 1000 # 128 Ko
+
 class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
-    """Simple DAV request handler with 
-    
+    """Simple DAV request handler with
+
     - GET
     - HEAD
     - PUT
@@ -75,33 +65,35 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
     It uses the resource/collection classes for serving and
     storing content.
-    
+
     """
 
     server_version = "DAV/" + __version__
     encode_threshold = 1400 # common MTU
 
-    ### utility functions
-    def _log(self, message):
-        pass
-
-    def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers={}):
+    def send_body(self, DATA, code = None, msg = None, desc = None, ctype='application/octet-stream', headers={}):
         """ send a body in one part """
+        log.debug("Use send_body method")
 
         self.send_response(code,message=msg)
         self.send_header("Connection", "close")
         self.send_header("Accept-Ranges", "bytes")
         self.send_header('Date', rfc1123_date())
-        
+        self.send_header('DAV', DAV_VERSION_2['version'])
+
         for a,v in headers.items():
             self.send_header(a,v)
-        
+
         if DATA:
             if 'gzip' in self.headers.get('Accept-Encoding', '').split(',') \
                     and len(DATA) > self.encode_threshold:
                 buffer = StringIO.StringIO()
                 output = gzip.GzipFile(mode='wb', fileobj=buffer)
-                output.write(DATA)
+                if isinstance(DATA, str) or isinstance(DATA, unicode):
+                    output.write(DATA)
+                else:
+                    for buf in DATA:
+                        output.write(buf)
                 output.close()
                 buffer.seek(0)
                 DATA = buffer.getvalue()
@@ -111,38 +103,105 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             self.send_header('Content-Type', ctype)
         else:
             self.send_header('Content-Length', 0)
-        
+
         self.end_headers()
         if DATA:
-            self._append(DATA)
-
-    def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
+            if isinstance(DATA, str) or isinstance(DATA, unicode):
+                log.debug("Don't use iterator")
+                self._append(DATA)
+            else:
+                if self._config.DAV.getboolean('http_response_use_iterator'):
+                    # Use iterator to reduce using memory
+                    log.debug("Use iterator")
+                    self.wfile.write(self._buffer)
+                    self.wfile.flush()
+                    self._buffer=""
+                    for buf in DATA:
+                        self.wfile.write(buf)
+                        self.wfile.flush()
+                else:
+                    # Don't use iterator, it's a compatibility option
+                    log.debug("Don't use iterator")
+                    self._append(DATA.read())
+
+    def send_body_chunks_if_http11(self, DATA, code, msg = None, desc = None, 
+                                   ctype='text/xml; encoding="utf-8"', 
+                                   headers={}):
+        if (
+            self.request_version == 'HTTP/1.0' or
+            not self._config.DAV.getboolean('chunked_http_response')
+        ):
+            self.send_body(DATA, code, msg, desc, ctype, headers)
+        else:
+            self.send_body_chunks(DATA, code, msg, desc, ctype, headers)
+ 
+    def send_body_chunks(self, DATA, code, msg=None, desc=None, 
+                            ctype='text/xml"', headers={}):
         """ send a body in chunks """
-        
+
         self.responses[207]=(msg,desc)
         self.send_response(code,message=msg)
         self.send_header("Content-type", ctype)
-        self.send_header("Connection", "close")
         self.send_header("Transfer-Encoding", "chunked")
         self.send_header('Date', rfc1123_date())
+        self.send_header('DAV', DAV_VERSION_2['version'])
+
+        for a,v in headers.items():
+            self.send_header(a,v)
 
-        if 'gzip' in self.headers.get('Accept-Encoding', '').split(',') \
-                and len(DATA) > self.encode_threshold:
+        if DATA:
+            if (
+                ('gzip' in self.headers.get('Accept-Encoding', '').split(',')) and
+                (len(DATA) > self.encode_threshold)
+            ):
                 buffer = StringIO.StringIO()
                 output = gzip.GzipFile(mode='wb', fileobj=buffer)
-                output.write(DATA)
+                if isinstance(DATA, str):
+                    output.write(DATA)
+                else:
+                    for buf in DATA:
+                        output.write(buf)
                 output.close()
                 buffer.seek(0)
                 DATA = buffer.getvalue()
                 self.send_header('Content-Encoding', 'gzip')
 
+            self.send_header('Content-Length', len(DATA))
+            self.send_header('Content-Type', ctype)
+
+        else:
+            self.send_header('Content-Length', 0)
+
         self.end_headers()
 
-        self._append(hex(len(DATA))[2:]+"\r\n")
-        self._append(DATA)
-        self._append("\r\n")
-        self._append("0\r\n")
-        self._append("\r\n")
+        if DATA:
+            if isinstance(DATA, str) or isinstance(DATA, unicode):
+                self._append(hex(len(DATA))[2:]+"\r\n")
+                self._append(DATA)
+                self._append("\r\n")
+                self._append("0\r\n")
+                self._append("\r\n")
+            else:
+                if self._config.DAV.getboolean('http_response_use_iterator'):
+                    # Use iterator to reduce using memory
+                    self.wfile.write(self._buffer)
+                    self.wfile.flush()
+                    self._buffer=""
+
+                    for buf in DATA:
+                        self.wfile.write(hex(len(buf))[2:]+"\r\n")
+                        self.wfile.write(buf)
+                        self.wfile.write("\r\n")
+
+                    self.wfile.write("0\r\n")
+                    self.wfile.write("\r\n")
+                else:
+                    # Don't use iterator, it's a compatibility option
+                    self._append(hex(len(DATA))[2:]+"\r\n")
+                    self._append(DATA.read())
+                    self._append("\r\n")
+                    self._append("0\r\n")
+                    self._append("\r\n")
 
     ### HTTP METHODS called by the server
 
@@ -152,9 +211,9 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         self.send_response(200)
         self.send_header("Content-Length", 0)
 
-        if self._config.DAV.lockemulation is True:
-            if self._config.DAV.verbose is True:
-                print >>sys.stderr, 'Activated LOCK,UNLOCK emulation (experimental)'
+        if self._config.DAV.getboolean('lockemulation') is True:
+            if self._config.DAV.getboolean('verbose') is True:
+                log.info('Activated LOCK,UNLOCK emulation (experimental)')
 
             self.send_header('Allow', DAV_VERSION_2['options'])
             self.send_header('DAV', DAV_VERSION_2['version'])
@@ -190,18 +249,35 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             content_type = dc.get_prop(uri,"DAV:","getcontenttype")
         except DAV_NotFound: content_type = "application/octet-stream"
 
+        range = None
+        status_code = 200
+        if 'Range' in self.headers:
+            p = self.headers['Range'].find("bytes=")
+            if p != -1:
+                range = self.headers['Range'][p + 6:].split("-")
+                status_code = 206
+
         # get the data
         try:
-            data = dc.get_data(uri)
+            data = dc.get_data(uri, range)
         except DAV_Error, (ec,dd):
             self.send_status(ec)
-            return 
+            return ec
 
         # send the data
         if with_body is False:
             data = None
 
-        self.send_body(data, '200', "OK", "OK", content_type, headers)
+        if isinstance(data, str) or isinstance(data, unicode):
+            self.send_body(data, status_code, None, None, content_type, headers)
+        else:
+            headers['Keep-Alive'] = 'timeout=15, max=86'
+            headers['Connection'] = 'Keep-Alive'
+            self.send_body_chunks_if_http11(data, status_code, None, None, 
+                                            content_type, headers)
+
+        return status_code
+            
 
     def do_HEAD(self):
         """ Send a HEAD response: Retrieves resource information w/o body """
@@ -211,7 +287,17 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     def do_GET(self):
         """Serve a GET request."""
 
-        return self._HEAD_GET(with_body=True)
+        log.debug(self.headers)
+
+        try:
+            status_code = self._HEAD_GET(with_body=True)
+            self.log_request(status_code)
+            return status_code
+        except IOError, e:
+            if e.errno == 32:
+                self.log_request(206)
+            else:
+                raise
 
     def do_TRACE(self):
         """ This will always fail because we can not reproduce HTTP requests. 
@@ -255,7 +341,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             DATA = DATA.replace('<ns0:creationdate xmlns:ns0="DAV:">',
                                     '<ns0:creationdate xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.tz">')
 
-        self.send_body_chunks(DATA, '207','Multi-Status','Multiple responses')
+        self.send_body_chunks_if_http11(DATA, '207','Multi-Status','Multiple responses')
 
     def do_REPORT(self):
         """ Query properties on defined resource. """
@@ -272,14 +358,14 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
 
-        rp = REPORT(uri, dc, self.headers.get('Depth', 'infinity'), body)
+        rp = REPORT(uri, dc, self.headers.get('Depth', '0'), body)
 
         try:
             DATA = '%s\n' % rp.createResponse()
         except DAV_Error, (ec,dd):
             return self.send_status(ec)
 
-        self.send_body_chunks(DATA, '207','Multi-Status','Multiple responses')
+        self.send_body_chunks_if_http11(DATA, '207','Multi-Status','Multiple responses')
 
     def do_MKCOL(self):
         """ create a new collection """
@@ -291,7 +377,9 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         try:
             dc.mkcol(uri)
             self.send_status(201)
+            self.log_request(201)
         except DAV_Error, (ec,dd):
+            self.log_request(ec)
             return self.send_status(ec)
 
     def do_DELETE(self):
@@ -320,6 +408,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                         break
             if not test:
                 self.send_status(412)
+                self.log_request(412)
                 return
 
         # Handle If-None-Match
@@ -341,6 +430,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                         break
             if not test:
                 self.send_status(412)
+                self.log_request(412)
                 return
 
         # locked resources are not allowed to delete
@@ -350,25 +440,34 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         dl = DELETE(uri,dc)
         if dc.is_collection(uri):
             res=dl.delcol()
-        else: res=dl.delone()
+            if res:
+                self.send_status(207,body=res)
+            else:
+                self.send_status(204)
+        else:
+            res=dl.delone() or 204
+            self.send_status(res)
 
-        if res:
-            self.send_status(207,body=res)
-        else: self.send_status(204)
 
     def do_PUT(self):
         dc=self.IFACE_CLASS
         uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri=urllib.unquote(uri)
 
+        log.debug("do_PUT: uri = %s" % uri)
+        log.debug('do_PUT: headers = %s' % self.headers)
         # Handle If-Match
         if self.headers.has_key('If-Match'):
+            log.debug("do_PUT: If-Match %s" % self.headers['If-Match'])
             test = False
             etag = None
             try:
                 etag = dc.get_prop(uri, "DAV:", "getetag")
             except:
                 pass
+
+            log.debug("do_PUT: etag = %s" % etag)
+
             for match in self.headers['If-Match'].split(','):
                 if match == '*':
                     if dc.exists(uri):
@@ -380,16 +479,22 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                         break
             if not test:
                 self.send_status(412)
+                self.log_request(412)
                 return
 
         # Handle If-None-Match
         if self.headers.has_key('If-None-Match'):
+            log.debug("do_PUT: If-None-Match %s" % self.headers['If-None-Match'])
+
             test = True
             etag = None
             try:
                 etag = dc.get_prop(uri, "DAV:", "getetag")
             except:
                 pass
+
+            log.debug("do_PUT: etag = %s" % etag)
+
             for match in self.headers['If-None-Match'].split(','):
                 if match == '*':
                     if dc.exists(uri):
@@ -401,8 +506,38 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                         break
             if not test:
                 self.send_status(412)
+                self.log_request(412)
                 return
 
+        # locked resources are not allowed to be overwritten
+        ifheader = self.headers.get('If')
+        if (
+            (self._l_isLocked(uri)) and
+            (not ifheader)
+        ):
+            return self.send_body(None, '423', 'Locked', 'Locked')
+
+        if ((self._l_isLocked(uri)) and (ifheader)):
+            uri_token = self._l_getLockForUri(uri)
+            taglist = IfParser(ifheader)
+            found = False
+            for tag in taglist:
+                for listitem in tag.list:
+                    token = tokenFinder(listitem)
+                    if (
+                        token and
+                        (self._l_hasLock(token)) and
+                        (self._l_getLock(token) == uri_token)
+                    ):
+                        found = True
+                        break
+                if found:
+                    break
+            if not found:
+                res = self.send_body(None, '423', 'Locked', 'Locked')
+                self.log_request(423)
+                return res
+
         # Handle expect
         expect = self.headers.get('Expect', '')
         if (expect.lower() == '100-continue' and
@@ -411,35 +546,77 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             self.send_status(100)
             self._flush()
 
-        # read the body
-        body=None
-        if self.headers.has_key("Content-Length"):
-            l=self.headers['Content-Length']
-            body=self.rfile.read(atoi(l))
-
-        # locked resources are not allowed to be overwritten
-        if self._l_isLocked(uri):
-            return self.send_body(None, '423', 'Locked', 'Locked')
-
-        ct=None
+        content_type = None
         if self.headers.has_key("Content-Type"):
-            ct=self.headers['Content-Type']
-        try:
-            location = dc.put(uri,body,ct)
-        except DAV_Error, (ec,dd):
-            return self.send_status(ec)
+            content_type = self.headers['Content-Type']
 
         headers = {}
-        if location:
-            headers['Location'] = location
+        headers['Location'] = uri
 
         try:
-            etag = dc.get_prop(location or uri, "DAV:", "getetag")
+            etag = dc.get_prop(uri, "DAV:", "getetag")
             headers['ETag'] = etag
         except:
             pass
 
-        self.send_body(None, '201', 'Created', '', headers=headers)
+        expect = self.headers.get('transfer-encoding', '')
+        if (
+            expect.lower() == 'chunked' and
+            self.protocol_version >= 'HTTP/1.1' and
+            self.request_version >= 'HTTP/1.1'
+        ):
+            self.send_body(None, '201', 'Created', '', headers=headers)
+            self._flush()
+
+            dc.put(uri, self._readChunkedData(), content_type)
+        else:
+            # read the body
+            body=None
+            if self.headers.has_key("Content-Length"):
+                l=self.headers['Content-Length']
+                log.debug("do_PUT: Content-Length = %s" % l)
+                body=self._readNoChunkedData(atoi(l))
+            else:
+                log.debug("do_PUT: Content-Length = empty")
+
+            try:
+                dc.put(uri, body, content_type)
+            except DAV_Error, (ec,dd):
+                return self.send_status(ec)
+
+            self.send_body(None, '201', 'Created', '', headers=headers)
+            self.log_request(201)
+
+    def _readChunkedData(self):
+        l = int(self.rfile.readline(), 16)
+        while l > 0:
+            buf = self.rfile.read(l)
+            yield buf
+            self.rfile.readline()
+            l = int(self.rfile.readline(), 16)
+
+    def _readNoChunkedData(self, content_length):
+        if self._config.DAV.getboolean('http_request_use_iterator'):
+            # Use iterator to reduce using memory
+            return self.__readNoChunkedDataWithIterator(content_length)
+        else:
+            # Don't use iterator, it's a compatibility option
+            return self.__readNoChunkedDataWithoutIterator(content_length)
+        
+    def __readNoChunkedDataWithIterator(self, content_length):
+        while True:
+            if content_length > BUFFER_SIZE:
+                buf = self.rfile.read(BUFFER_SIZE)
+                content_length -= BUFFER_SIZE
+                yield buf
+            else:
+                buf = self.rfile.read(content_length)
+                yield buf
+                break
+
+    def __readNoChunkedDataWithoutIterator(self, content_length):
+        return self.rfile.read(content_length)
+
 
     def do_COPY(self):
         """ copy one resource to another """
@@ -486,12 +663,12 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         d="infinity"
         if self.headers.has_key("Depth"):
             d=self.headers['Depth']
-            
-            if d!="0" and d!="infinity": 
+
+            if d!="0" and d!="infinity":
                 self.send_status(400)
                 return
-            
-            if d=="0":  
+
+            if d=="0":
                 res=cp.single_action()
                 self.send_status(res)
                 return
@@ -511,7 +688,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
                 return
 
         if res:
-            self.send_body_chunks(res,207,self.responses[207][0],
+            self.send_body_chunks_if_http11(res,207,self.responses[207][0],
                             self.responses[207][1],
                             ctype='text/xml; charset="utf-8"')
         else:
@@ -525,8 +702,10 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     def send_status(self,code=200,mediatype='text/xml;  charset="utf-8"', \
                                 msg=None,body=None):
 
-        if not msg: msg=self.responses[code][1]
-        self.send_body(body,code,self.responses[code][0],msg,mediatype)
+        if not msg:
+            msg=self.responses.get(code, ['', ''])[1]
+
+        self.send_body(body,code,self.responses.get(code, [''])[0],msg,mediatype)
 
     def get_baseuri(self, dc):
         baseuri = dc.baseuri
@@ -535,3 +714,8 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             uparts[1] = self.headers['Host']
             baseuri = urlparse.urlunparse(uparts)
         return baseuri
+
+    def log_message(self, *args):
+        AuthServer.BufferedAuthRequestHandler.log_message(self, 
+            *tuple(('- %s - ' + args[0],) + (self.headers.get('User-Agent', '?'),) + args[1:])
+        )
diff --git a/DAV/__init__.py b/DAV/__init__.py
index 10abdb4..5a88f42 100644
--- a/DAV/__init__.py
+++ b/DAV/__init__.py
@@ -1,20 +1,22 @@
-"""
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
-    This library 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
-    Library General Public License for more details.
 
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 """
+    python davserver
 
-
+"""
diff --git a/DAV/constants.py b/DAV/constants.py
index 4eb99b9..131f80b 100644
--- a/DAV/constants.py
+++ b/DAV/constants.py
@@ -1,3 +1,20 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 # definition for resourcetype
 COLLECTION=1
 OBJECT=None
diff --git a/DAV/davcmd.py b/DAV/davcmd.py
index cd2f418..a69d0a0 100644
--- a/DAV/davcmd.py
+++ b/DAV/davcmd.py
@@ -1,3 +1,22 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
+#Copyright (c) 2009 Cedric Krier (cedric.krier at b2ck.com)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 """
 
 davcmd.py
@@ -15,7 +34,7 @@ from utils import create_treelist, is_prefix
 from errors import *
 
 def deltree(dc,uri,exclude={}):
-    """ delete a tree of resources 
+    """ delete a tree of resources
 
     dc  -- dataclass to use
     uri -- root uri to delete
@@ -36,7 +55,7 @@ def deltree(dc,uri,exclude={}):
     for i in range(len(tlist),0,-1):
         problem_uris=result.keys()
         element=tlist[i-1]
-    
+
         # test here, if an element is a prefix of an uri which
         # generated an error before.
         # note that we walk here from childs to parents, thus
@@ -47,31 +66,31 @@ def deltree(dc,uri,exclude={}):
             if is_prefix(element,p):
                 ok=None
                 break
-    
-            if not ok: continue 
-    
+
+            if not ok: continue
+
         # here we test for the exclude list which is the other way round!
         for p in exclude.keys():
             if is_prefix(p,element):
                 ok=None
                 break
-        
-            if not ok: continue 
-    
+
+            if not ok: continue
+
         # now delete stuff
         try:
             delone(dc,element)
-        except DAV_Error, (ec,dd):  
+        except DAV_Error, (ec,dd):
             result[element]=ec
-    
+
     return result
 
 def delone(dc,uri):
     """ delete a single object """
     if dc.is_collection(uri):
-        dc.rmcol(uri)   # should be empty
+        return dc.rmcol(uri)   # should be empty
     else:
-        dc.rm(uri)
+        return dc.rm(uri)
 
 ###
 ### COPY
@@ -80,24 +99,25 @@ def delone(dc,uri):
 # helper function
 
 def copy(dc,src,dst):
-    """ only copy the element 
+    """ only copy the element
 
     This is just a helper method factored out from copy and
     copytree. It will not handle the overwrite or depth header.
-    
+
     """
 
     # destination should have been deleted before
-    if dc.exists(dst): raise DAV_Error, 412
+    if dc.exists(dst): 
+        raise DAV_Error, 412
 
     # source should exist also
-    if not dc.exists(src): raise DAV_NotFound
+    if not dc.exists(src): 
+        raise DAV_NotFound
 
     if dc.is_collection(src):
-        dc.copycol(src,dst) # an exception will be passed thru
+        dc.copycol(src, dst) # an exception will be passed thru
     else:
-        dc.copy(src,dst)  # an exception will be passed thru
-
+        dc.copy(src, dst)  # an exception will be passed thru
 
 # the main functions
 
@@ -105,16 +125,17 @@ def copyone(dc,src,dst,overwrite=None):
     """ copy one resource to a new destination """
 
     if overwrite and dc.exists(dst):
-        delres=deltree(dc,dst)
+        delres = deltree(dc, dst)
     else:
         delres={}
 
     # if we cannot delete everything, then do not copy!
-    if delres: return delres
-    
+    if delres: 
+        return delres
+
     try:
-        copy(dc,src,dst)    # pass thru exceptions
-    except DAV_Error, (ec,dd):
+        copy(dc, src, dst)    # pass thru exceptions
+    except DAV_Error, (ec, dd):
         return ec
 
 def copytree(dc,src,dst,overwrite=None):
@@ -122,7 +143,7 @@ def copytree(dc,src,dst,overwrite=None):
 
     dc  -- dataclass to use
     src -- src uri from where to copy
-    dst -- dst uri 
+    dst -- dst uri
     overwrite -- if 1 then delete dst uri before
 
     returns dict of uri:error_code tuples from which
@@ -137,17 +158,18 @@ def copytree(dc,src,dst,overwrite=None):
         delres={}
 
     # if we cannot delete everything, then do not copy!
-    if delres: return delres
+    if delres: 
+        return delres
 
     # get the tree we have to copy
-    tlist=create_treelist(dc,src)
-    result={}
+    tlist = create_treelist(dc,src)
+    result = {}
 
     # prepare destination URIs (get the prefix)
-    dpath=urlparse.urlparse(dst)[2]
+    dpath = urlparse.urlparse(dst)[2]
 
     for element in tlist:
-        problem_uris=result.keys()
+        problem_uris = result.keys()
 
         # now URIs get longer and longer thus we have
         # to test if we had a parent URI which we were not
@@ -159,8 +181,8 @@ def copytree(dc,src,dst,overwrite=None):
             if is_prefix(p,element):
                 ok=None
                 break
-    
-        if not ok: continue 
+
+        if not ok: continue
 
         # now create the destination URI which corresponds to
         # the actual source URI. -> actual_dst
@@ -172,12 +194,11 @@ def copytree(dc,src,dst,overwrite=None):
         # now copy stuff
         try:
             copy(dc,element,actual_dst)
-        except DAV_Error, (ec,dd):  
+        except DAV_Error, (ec,dd):
             result[element]=ec
 
     return result
-        
-        
+
 
 ###
 ### MOVE
@@ -192,11 +213,11 @@ def moveone(dc,src,dst,overwrite=None):
     """
 
     # first copy it
-    copyone(dc,src,dst,overwrite)
+    copyone(dc, src, dst, overwrite)
 
     # then delete it
     dc.rm(src)
-      
+
 def movetree(dc,src,dst,overwrite=None):
     """ move a collection
 
@@ -208,10 +229,10 @@ def movetree(dc,src,dst,overwrite=None):
     """
 
     # first copy it
-    res=copytree(dc,src,dst,overwrite)
+    res = copytree(dc,src,dst,overwrite)
 
     # then delete it
-    res=deltree(dc,src,exclude=res)
+    res = deltree(dc,src,exclude=res)
 
     return res
-      
+
diff --git a/DAV/davcopy.py b/DAV/davcopy.py
index 726a0f1..77485db 100644
--- a/DAV/davcopy.py
+++ b/DAV/davcopy.py
@@ -1,3 +1,19 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
diff --git a/DAV/davmove.py b/DAV/davmove.py
index e0ea082..87229d0 100644
--- a/DAV/davmove.py
+++ b/DAV/davmove.py
@@ -1,3 +1,21 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
+
 import sys
 import string
 import urlparse
@@ -15,10 +33,10 @@ class MOVE:
     """ move resources and eventually create multistatus responses
 
     This module implements the MOVE class which is responsible for
-    moving resources. 
+    moving resources.
 
     MOVE is implemented by a COPY followed by a DELETE of the old
-    resource. 
+    resource.
 
     """
 
@@ -69,7 +87,7 @@ class MOVE:
         ps=urlparse.urlparse(self.__src)[2]
         pd=urlparse.urlparse(self.__dst)[2]
         if ps==pd: raise DAV_Error,  403
-        
+
         result=dc.movetree(self.__src,self.__dst,self.__overwrite)
         if not result: return None
 
diff --git a/DAV/dbconn.py b/DAV/dbconn.py
index 6805b7f..17dbee0 100644
--- a/DAV/dbconn.py
+++ b/DAV/dbconn.py
@@ -1,3 +1,24 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
+import logging
+
+log = logging.getLogger(__name__)
+
 try:
     import MySQLdb
 except ImportError:
@@ -6,61 +27,60 @@ except ImportError:
 import sys
 
 class Mconn:
-	def connect(self,username,userpasswd,host,port,db):
-		try: connection = MySQLdb.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
-		except MySQLdb.OperationalError, message:   
-			print >>sys.stderr,"Error %d:\n%s" % (message[ 0 ], message[ 1 ] )  
-			return 0
-		else:
-			self.db = connection.cursor()
-			
-			return 1
+    def connect(self,username,userpasswd,host,port,db):
+        try: connection = MySQLdb.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
+        except MySQLdb.OperationalError, message:
+            log.error("%d:\n%s" % (message[ 0 ], message[ 1 ] ))
+            return 0
+        else:
+            self.db = connection.cursor()
+
+            return 1
+
+    def execute(self,qry):
+        if self.db:
+            try: res=self.db.execute(qry)
+            except MySQLdb.OperationalError, message:
+                log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
+                return 0
+
+            except MySQLdb.ProgrammingError, message:
+                log.error("Error %d:\n%s" % (message[ 0 ], message[ 1 ] ))
+                return 0
+
+            else:
+                log.debug('Query Returned '+str(res)+' results')
+                return self.db.fetchall()
+
+    def create_user(self,user,passwd):
+        qry="select * from Users where User='%s'"%(user)
+        res=self.execute(qry)
+        if not res or len(res) ==0:
+            qry="insert into Users (User,Pass) values('%s','%s')"%(user,passwd)
+            res=self.execute(qry)
+        else:
+            log.debug("Username already in use")
+
+    def create_table(self):
+        qry="""CREATE TABLE `Users` (
+                  `uid` int(10) NOT NULL auto_increment,
+                  `User` varchar(60) default NULL,
+                  `Pass` varchar(60) default NULL,
+            `Write` tinyint(1) default '0',
+                  PRIMARY KEY  (`uid`)
+                ) ENGINE=MyISAM DEFAULT CHARSET=latin1"""
+        self.execute(qry)
+
+
+    def first_run(self,user,passwd):
+        res= self.execute('select * from Users')
+        if res or type(res)==type(()) :
+            pass
+        else:
+            self.create_table()
+            self.create_user(user,passwd)
+
 
-	def execute(self,qry):
-		if self.db:
-			try: res=self.db.execute(qry)
-			except MySQLdb.OperationalError, message:   
-				print >>sys.stderr, "Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
-				return 0
-				
-			except MySQLdb.ProgrammingError, message:   
-				print >>sys.stderr,"Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
-				return 0
-				
-			else:
-				print >>sys.stderr,'Query Returned '+str(res)+' results'
-				return self.db.fetchall()
-	
-	def create_user(self,user,passwd):
-		qry="select * from Users where User='%s'"%(user)
-		res=self.execute(qry)
-		if not res or len(res) ==0:
-			qry="insert into Users (User,Pass) values('%s','%s')"%(user,passwd)
-			res=self.execute(qry)
-		else:
-			print >>sys.stderr, "Username already in use"
-			
-	def create_table(self):
-		qry="""CREATE TABLE `Users` (                    
-          		`uid` int(10) NOT NULL auto_increment,  
-          		`User` varchar(60) default NULL,        
-          		`Pass` varchar(60) default NULL,        
-			`Write` tinyint(1) default '0',
-          		PRIMARY KEY  (`uid`)                    
-        		) ENGINE=MyISAM DEFAULT CHARSET=latin1"""
-		self.execute(qry)
-			
-	
-	def first_run(self,user,passwd):
-		res= self.execute('select * from Users')
-		if res or type(res)==type(()) :
-			pass
-		else:
-			self.create_table()
-			self.create_user(user,passwd)
-		
-		
-	def __init__(self,user,password,host,port,db):
-		self.db=0
-		self.connect(user,password,host,port,db)
-		
+    def __init__(self,user,password,host,port,db):
+        self.db=0
+        self.connect(user,password,host,port,db)
diff --git a/DAV/delete.py b/DAV/delete.py
index 15c880d..79eb8ba 100644
--- a/DAV/delete.py
+++ b/DAV/delete.py
@@ -1,3 +1,20 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 import os
 import string
 import urllib
@@ -28,12 +45,5 @@ class DELETE:
         """ delete a resource """
 
         dc=self.__dataclass
-        result=dc.delone(self.__uri)
-        
-        if not result: return None
-        if not len(result.items()):
-            return None # everything ok
-
-        # create the result element
-        return make_xmlresponse(result)
+        return dc.delone(self.__uri)
 
diff --git a/DAV/errors.py b/DAV/errors.py
index c2b12d1..a4ec9a2 100644
--- a/DAV/errors.py
+++ b/DAV/errors.py
@@ -1,4 +1,20 @@
-#!/usr/bin/env python
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 """
 
     Exceptions for the DAVserver implementation
@@ -8,8 +24,8 @@
 class DAV_Error(Exception):
     """ in general we can have the following arguments:
 
-	1. the error code
-	2. the error result element, e.g. a <multistatus> element
+    1. the error code
+    2. the error result element, e.g. a <multistatus> element
     """
 
     def __init__(self,*args):
@@ -17,10 +33,10 @@ class DAV_Error(Exception):
             self.args=(args[0],"")
         else:
             self.args=args
-    
+
 class DAV_Secret(DAV_Error):
     """ the user is not allowed to know anything about it
-    
+
     returning this for a property value means to exclude it
     from the response xml element.
     """
@@ -31,7 +47,7 @@ class DAV_Secret(DAV_Error):
 
 class DAV_NotFound(DAV_Error):
     """ a requested property was not found for a resource """
-    
+
     def __init__(self,*args):
         if len(args):
             DAV_Error.__init__(self,404,args[0])
@@ -42,7 +58,7 @@ class DAV_NotFound(DAV_Error):
 
 class DAV_Forbidden(DAV_Error):
     """ a method on a resource is not allowed """
-    
+
     def __init__(self,*args):
         if len(args):
             DAV_Error.__init__(self,403,args[0])
@@ -50,3 +66,14 @@ class DAV_Forbidden(DAV_Error):
             DAV_Error.__init__(self,403)
         pass
 
+class DAV_Requested_Range_Not_Satisfiable(DAV_Error):
+    """ none of the range-specifier values overlap the current extent 
+    of the selected resource """
+    
+    def __init__(self, *args):
+        if len(args):
+            DAV_Error.__init__(self, 416, args[0])
+        else:
+            DAV_Error.__init__(self, 416)
+        pass
+
diff --git a/DAV/iface.py b/DAV/iface.py
index ad78ce7..8151527 100644
--- a/DAV/iface.py
+++ b/DAV/iface.py
@@ -1,3 +1,20 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 """
 
 basic interface class
@@ -20,16 +37,16 @@ class dav_interface:
     ### defined properties (modify this but let the DAV stuff there!)
     ### the format is namespace: [list of properties]
 
-    PROPS={"DAV:" : ('creationdate', 
-                     'displayname', 
-                     'getcontentlanguage', 
-                     'getcontentlength', 
-                     'getcontenttype', 
-                     'getetag', 
-                     'getlastmodified', 
-                     'lockdiscovery', 
-                     'resourcetype', 
-                     'source', 
+    PROPS={"DAV:" : ('creationdate',
+                     'displayname',
+                     'getcontentlanguage',
+                     'getcontentlength',
+                     'getcontenttype',
+                     'getetag',
+                     'getlastmodified',
+                     'lockdiscovery',
+                     'resourcetype',
+                     'source',
                      'supportedlock'),
            "NS2" : ("p1","p2")
            }
@@ -42,20 +59,20 @@ class dav_interface:
           "NS2"  : "ns2" }
 
     def get_propnames(self,uri):
-        """ return the property names allowed for the given URI 
+        """ return the property names allowed for the given URI
 
         In this method we simply return the above defined properties
-        assuming that they are valid for any resource. 
+        assuming that they are valid for any resource.
         You can override this in order to return a different set
         of property names for each resource.
-        
+
         """
         return self.PROPS
 
     def get_prop2(self,uri,ns,pname):
-        """ return the value of a property 
+        """ return the value of a property
+
 
-        
         """
         if lower(ns)=="dav:": return self.get_dav(uri,pname)
 
@@ -74,26 +91,26 @@ class dav_interface:
             raise DAV_NotFound
         mname=prefix+"_"+propname.replace('-', '_')
         try:
-                m=getattr(self,mname)
-                r=m(uri)
-                return r
+            m=getattr(self,mname)
+            r=m(uri)
+            return r
         except AttributeError:
-                raise DAV_NotFound
+            raise DAV_NotFound
 
     ###
     ### DATA methods (for GET and PUT)
     ###
 
     def get_data(self,uri):
-        """ return the content of an object 
+        """ return the content of an object
 
         return data or raise an exception
-        
+
         """
         raise DAV_NotFound
 
     def put(self,uri,data):
-        """ write an object to the repository 
+        """ write an object to the repository
 
         return the location uri or raise an exception
         """
@@ -153,7 +170,7 @@ class dav_interface:
         """ return the last modification date of the resource """
         return time.time()
 
-    
+
     ###
     ### COPY MOVE DELETE
     ###
@@ -167,7 +184,7 @@ class dav_interface:
         before by the DELETE class in DAV/delete.py
 
         return a success code or raise an exception
-        
+
         """
         raise DAV_NotFound
 
@@ -175,7 +192,7 @@ class dav_interface:
         """ delete a single resource 
 
         return a success code or raise an exception
-        
+
         """
         raise DAV_NotFound
 
diff --git a/DAV/locks.py b/DAV/locks.py
index 0badcc9..d32d90b 100644
--- a/DAV/locks.py
+++ b/DAV/locks.py
@@ -1,3 +1,19 @@
+#Copyright (c) 2009 Simon Pamies (s.pamies at banality.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 import os
 import sys
@@ -10,6 +26,11 @@ import urlparse
 import urllib
 import random
 
+import logging
+
+log = logging.getLogger(__name__)
+
+import xml.dom
 from xml.dom import minidom
 
 from utils import rfc1123_date, IfParser, tokenFinder
@@ -84,8 +105,8 @@ class LockManager:
 
         dc = self.IFACE_CLASS
 
-        if self._config.DAV.verbose is True:
-            print >>sys.stderr, 'UNLOCKing resource %s' % self.headers
+        if self._config.DAV.getboolean('verbose') is True:
+            log.info('UNLOCKing resource %s' % self.headers)
 
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
@@ -93,7 +114,7 @@ class LockManager:
         token = tokenFinder(self.headers.get('Lock-Token'))
         if self._l_isLocked(uri):
             self._l_delLock(token)
-        
+
         self.send_body(None, '204', 'Ok', 'Ok')
 
     def do_LOCK(self):
@@ -101,8 +122,7 @@ class LockManager:
 
         dc = self.IFACE_CLASS
 
-        if self._config.DAV.verbose is True:
-            print >>sys.stderr, 'LOCKing resource %s' % self.headers
+        log.debug('LOCKing resource %s' % self.headers)
 
         body = None
         if self.headers.has_key('Content-Length'):
@@ -113,29 +133,31 @@ class LockManager:
 
         uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri = urllib.unquote(uri)
+        log.debug('do_LOCK: uri = %s' % uri)
 
         ifheader = self.headers.get('If')
         alreadylocked = self._l_isLocked(uri)
+        log.debug('do_LOCK: alreadylocked = %s' % alreadylocked)
 
         if body and alreadylocked:
             # Full LOCK request but resource already locked
             self.responses[423] = ('Locked', 'Already locked')
             return self.send_status(423)
-        
+
         elif body and not ifheader:
             # LOCK with XML information
             data = self._lock_unlock_parse(body)
             token, result = self._lock_unlock_create(uri, 'unknown', depth, data)
 
             if result:
-                self.send_body(result, '207', 'Error', 'Error', 
+                self.send_body(result, '207', 'Error', 'Error',
                                 'text/xml; charset="utf-8"')
 
             else:
                 lock = self._l_getLock(token)
-                self.send_body(lock.asXML(), '200', 'OK', 'OK', 
+                self.send_body(lock.asXML(), '200', 'OK', 'OK',
                                 'text/xml; charset="utf-8"',
-                                {'Lock-Token' : 'opaquelocktoken:%s' % token})
+                                {'Lock-Token' : '<opaquelocktoken:%s>' % token})
 
 
         else:
@@ -166,7 +188,7 @@ class LockItem:
     """ Lock with support for exclusive write locks. Some code taken from
     webdav.LockItem from the Zope project. """
 
-    def __init__(self, uri, creator, lockowner, depth=0, timeout='Infinite', 
+    def __init__(self, uri, creator, lockowner, depth=0, timeout='Infinite',
                     locktype='write', lockscope='exclusive', token=None, **kw):
 
         self.uri = uri
@@ -206,6 +228,12 @@ class LockItem:
         self.modified = time.time()
 
     def asXML(self, namespace='d', discover=False):
+        owner_str = ''
+        if isinstance(self.owner, str):
+            owner_str = self.owner
+        elif isinstance(self.owner, xml.dom.minicompat.NodeList):
+            owner_str = "".join([node.toxml() for node in self.owner[0].childNodes])
+
         token = self.token
         base = ('<%(ns)s:activelock>\n'
              '  <%(ns)s:locktype><%(ns)s:%(locktype)s/></%(ns)s:locktype>\n'
@@ -222,7 +250,7 @@ class LockItem:
                'locktype': self.locktype,
                'lockscope': self.lockscope,
                'depth': self.depth,
-               'owner': self.owner and self.owner or '',
+               'owner': owner_str,
                'timeout': self.getTimeoutString(),
                'locktoken': token,
                }
diff --git a/DAV/propfind.py b/DAV/propfind.py
index da56db6..8d4650e 100644
--- a/DAV/propfind.py
+++ b/DAV/propfind.py
@@ -1,7 +1,24 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
 
+import logging
 import sys
 import string
 import urlparse
@@ -12,6 +29,8 @@ import utils
 from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
 from errors import *
 
+log = logging.getLogger(__name__)
+
 class PROPFIND:
     """ parse a propfind xml element and extract props 
 
@@ -23,8 +42,8 @@ class PROPFIND:
 
     The list of properties will contain tuples of the form
     (element name, ns_prefix, ns_uri)
-    
-    
+
+
     """
 
     def __init__(self,uri,dataclass,depth, body):
@@ -38,7 +57,7 @@ class PROPFIND:
         self._has_body=None    # did we parse a body?
 
         if dataclass.verbose:
-            print >>sys.stderr, 'PROPFIND: Depth is %s, URI is %s' % (depth, uri)
+            log.info('PROPFIND: Depth is %s, URI is %s' % (depth, uri))
 
         if body:
             self.request_type, self.proplist, self.namespaces = utils.parse_propfind(body)
@@ -66,10 +85,10 @@ class PROPFIND:
         df = None
         if self.request_type==RT_ALLPROP:
             df = self.create_allprop()
-        
+
         if self.request_type==RT_PROPNAME:
             df = self.create_propname()
-        
+
         if self.request_type==RT_PROP:
             df = self.create_prop()
 
@@ -79,10 +98,10 @@ class PROPFIND:
         # no body means ALLPROP!
         df = self.create_allprop()
         return df
-    
+
     def create_propname(self):
         """ create a multistatus response for the prop names """
-        
+
         dc=self._dataclass
         # create the document generator
         doc = domimpl.createDocument(None, "multistatus", None)
@@ -99,7 +118,7 @@ class PROPFIND:
             pnames=dc.get_propnames(self._uri)
             re=self.mk_propname_response(self._uri,pnames, doc)
             ms.appendChild(re)
-        
+
             for newuri in dc.get_childs(self._uri):
                 pnames=dc.get_propnames(newuri)
                 re=self.mk_propname_response(newuri,pnames, doc)
@@ -124,7 +143,7 @@ class PROPFIND:
         for ns,plist in self._dataclass.get_propnames(self._uri).items():
             self.proplist[ns]=plist
             self.namespaces.append(ns)
-        
+
         return self.create_prop()
 
     def create_prop(self):
@@ -137,7 +156,7 @@ class PROPFIND:
         2. read the property values for each URI 
            (which is dependant on the Depth header)
            This is done by the get_propvalues() method.
-        
+
         3. For each URI call the append_result() method
            to append the actual <result>-Tag to the result
            document.
@@ -160,12 +179,12 @@ class PROPFIND:
             gp,bp=self.get_propvalues(self._uri)
             res=self.mk_prop_response(self._uri,gp,bp,doc)
             ms.appendChild(res)
-        
+
         elif self._depth=="1":
             gp,bp=self.get_propvalues(self._uri)
             res=self.mk_prop_response(self._uri,gp,bp,doc)
             ms.appendChild(res)
-        
+
             for newuri in self._dataclass.get_childs(self._uri):
                 gp,bp=self.get_propvalues(newuri)
                 res=self.mk_prop_response(newuri,gp,bp,doc)
@@ -188,7 +207,7 @@ class PROPFIND:
 
         This will simply format the propnames list.
         propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
-        
+
         """
         re=doc.createElement("D:response")
 
@@ -199,7 +218,7 @@ class PROPFIND:
         huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
         href.appendChild(huri)
         re.appendChild(href)
-        
+
         ps=doc.createElement("D:propstat")
         nsnum=0
 
@@ -214,7 +233,7 @@ class PROPFIND:
         for p in plist:
             pe=doc.createElement(nsp+":"+p)
             pr.appendChild(pe)
-        
+
         ps.appendChild(pr)
         re.appendChild(ps)
 
@@ -226,15 +245,16 @@ class PROPFIND:
         We differ between the good props and the bad ones for
         each generating an extra <propstat>-Node (for each error
         one, that means).
-        
+
         """
         re=doc.createElement("D:response")
         # append namespaces to response
         nsnum=0
         for nsname in self.namespaces:
-            re.setAttribute("xmlns:ns"+str(nsnum),nsname)
+            if nsname != 'DAV:':
+                re.setAttribute("xmlns:ns"+str(nsnum),nsname)
             nsnum=nsnum+1
-        
+
         # write href information
         uparts=urlparse.urlparse(uri)
         fileloc=uparts[2]
@@ -250,12 +270,18 @@ class PROPFIND:
 
         gp=doc.createElement("D:prop")
         for ns in good_props.keys():
-            ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+            if ns != 'DAV:':
+                ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+            else:
+                ns_prefix = 'D:'
             for p,v in good_props[ns].items():
 
                 pe=doc.createElement(ns_prefix+str(p))
                 if hasattr(v, '__class__') and v.__class__.__name__ == 'Element':
                     pe.appendChild(v)
+                elif isinstance(v, list):
+                    for val in v:
+                        pe.appendChild(val)
                 else:
                     if p=="resourcetype":
                         if v==1:
@@ -266,7 +292,7 @@ class PROPFIND:
                         pe.appendChild(ve)
 
                 gp.appendChild(pe)
-        
+
         ps.appendChild(gp)
         s=doc.createElement("D:status")
         t=doc.createTextNode("HTTP/1.1 200 OK")
@@ -285,12 +311,15 @@ class PROPFIND:
                 ps.appendChild(bp)
 
                 for ns in bad_props[ecode].keys():
-                    ns_prefix="ns"+str(self.namespaces.index(ns))+":"
-                
-                for p in bad_props[ecode][ns]:
-                    pe=doc.createElement(ns_prefix+str(p))
-                    bp.appendChild(pe)
-                
+                    if ns != 'DAV:':
+                        ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+                    else:
+                        ns_prefix = 'D:'
+
+                    for p in bad_props[ecode][ns]:
+                        pe=doc.createElement(ns_prefix+str(p))
+                        bp.appendChild(pe)
+
                 s=doc.createElement("D:status")
                 t=doc.createTextNode(utils.gen_estring(ecode))
                 s.appendChild(t)
@@ -307,7 +336,7 @@ class PROPFIND:
         which we found a value and the ones for which we
         only got an error, either because they haven't been
         found or the user is not allowed to read them.
-        
+
         """
         good_props={}
         bad_props={}
@@ -315,7 +344,6 @@ class PROPFIND:
         ddc = self._dataclass
         for (ns,plist) in self.proplist.items():
             good_props[ns]={}
-            bad_props={}
             for prop in plist:
                 ec = 0
                 try:
@@ -323,10 +351,10 @@ class PROPFIND:
                     good_props[ns][prop]=r
                 except DAV_Error, error_code:
                     ec=error_code[0]
-                
+
                 # ignore props with error_code if 0 (invisible)
                 if ec==0: continue
-                
+
                 if bad_props.has_key(ec):
                     if bad_props[ec].has_key(ns):
                         bad_props[ec][ns].append(prop)
diff --git a/DAV/propfind.py.orig b/DAV/propfind.py.orig
deleted file mode 100644
index 371d012..0000000
--- a/DAV/propfind.py.orig
+++ /dev/null
@@ -1,340 +0,0 @@
-#!/usr/bin/env python
-
-"""
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-"""
-
-
-from xml.dom import ext
-from xml.dom.Document import Document
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-
-class PROPFIND:
-    """ parse a propfind xml element and extract props 
-
-    It will set the following instance vars:
-
-    request_class   : ALLPROP | PROPNAME | PROP
-    proplist    : list of properties
-    nsmap       : map of namespaces
-
-    The list of properties will contain tuples of the form
-    (element name, ns_prefix, ns_uri)
-    
-    
-    """
-
-
-    def __init__(self,uri,dataclass,depth):
-        self.request_type=None
-        self.nsmap={}
-        self.proplist={}
-        self.default_ns=None
-        self.__dataclass=dataclass
-        self.__depth=str(depth)
-        self.__uri=uri
-        self.__has_body=None    # did we parse a body?
-
-    def read_propfind(self,xml_doc):
-        self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
-
-    def createResponse(self):
-        """ create the multistatus response 
-
-        This will be delegated to the specific method
-        depending on which request (allprop, propname, prop)
-        was found.
-
-        If we get a PROPNAME then we simply return the list with empty
-        values which we get from the interface class
-
-        If we get an ALLPROP we first get the list of properties and then
-        we do the same as with a PROP method.
-
-        """
-
-        df = None
-        if self.request_type==RT_ALLPROP:
-            df = self.create_allprop()
-        
-        if self.request_type==RT_PROPNAME:
-            df =  self.create_propname()
-        
-        if self.request_type==RT_PROP:
-            df = self.create_prop()
-        
-        if df != None:
-            return df
-
-        # no body means ALLPROP!
-        df = self.create_allprop()
-        return df
-    
-    def create_propname(self):
-        """ create a multistatus response for the prop names """
-        
-        dc=self.__dataclass
-        # create the document generator
-        doc = Document(None)
-        ms=doc.createElement("D:multistatus")
-        ms.setAttribute("xmlns:D","DAV:")
-        doc.appendChild(ms)
-
-        if self.__depth=="0":
-            pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames, doc)
-            ms.appendChild(re)
-
-        elif self.__depth=="1":
-            pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames, doc)
-            ms.appendChild(re)
-        
-        for newuri in dc.get_childs(self.__uri):
-            pnames=dc.get_propnames(newuri)
-            re=self.mk_propname_response(newuri,pnames, doc)
-            ms.appendChild(re)
-        # *** depth=="infinity"  
-
-        sfile=StringIO()
-        ext.PrettyPrint(doc,stream=sfile)
-        s=sfile.getvalue()
-        sfile.close()
-        return s
-
-    def create_allprop(self):
-        """ return a list of all properties """
-        self.proplist={}
-        self.namespaces=[]
-        for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
-            self.proplist[ns]=plist
-            self.namespaces.append(ns)
-        
-        return self.create_prop()
-
-    def create_prop(self):
-        """ handle a <prop> request
-
-        This will
-
-        1. set up the <multistatus>-Framework
-
-        2. read the property values for each URI 
-           (which is dependant on the Depth header)
-           This is done by the get_propvalues() method.
-        
-        3. For each URI call the append_result() method
-           to append the actual <result>-Tag to the result
-           document.
-
-        We differ between "good" properties, which have been
-        assigned a value by the interface class and "bad" 
-        properties, which resulted in an error, either 404
-        (Not Found) or 403 (Forbidden).
-
-        """
-
-
-        # create the document generator
-        doc = Document(None)
-        ms=doc.createElement("D:multistatus")
-        ms.setAttribute("xmlns:D","DAV:")
-        doc.appendChild(ms)
-
-        if self.__depth=="0":
-            gp,bp=self.get_propvalues(self.__uri)
-            res=self.mk_prop_response(self.__uri,gp,bp,doc)
-            ms.appendChild(res)
-        
-        elif self.__depth=="1":
-            gp,bp=self.get_propvalues(self.__uri)
-            res=self.mk_prop_response(self.__uri,gp,bp,doc)
-            ms.appendChild(res)
-        
-        for newuri in self.__dataclass.get_childs(self.__uri):
-            gp,bp=self.get_propvalues(newuri)
-            res=self.mk_prop_response(newuri,gp,bp,doc)
-            ms.appendChild(res)
-        
-        sfile=StringIO()
-        ext.PrettyPrint(doc,stream=sfile)
-        s=sfile.getvalue()
-        sfile.close()
-        return s
-
-
-    def mk_propname_response(self,uri,propnames,doc):
-        """ make a new <prop> result element for a PROPNAME request 
-
-        This will simply format the propnames list.
-        propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
-        
-        """
-        re=doc.createElement("D:response")
-
-        # write href information
-        uparts=urlparse.urlparse(uri)
-        fileloc=uparts[2]
-        href=doc.createElement("D:href")
-        huri=doc.createTextNode(urllib.quote(fileloc))
-        href.appendChild(huri)
-        re.appendChild(href)
-        
-        ps=doc.createElement("D:propstat")
-        nsnum=0
-
-        for ns,plist in propnames.items():
-            # write prop element
-            pr=doc.createElement("D:prop")
-            nsp="ns"+str(nsnum)
-            pr.setAttribute("xmlns:"+nsp,ns)
-            nsnum=nsnum+1
-
-        # write propertynames
-        for p in plist:
-            pe=doc.createElement(nsp+":"+p)
-            pr.appendChild(pe)
-        
-        ps.appendChild(pr)
-        re.appendChild(ps)
-
-        return re
-
-    def mk_prop_response(self,uri,good_props,bad_props,doc):
-        """ make a new <prop> result element 
-
-        We differ between the good props and the bad ones for
-        each generating an extra <propstat>-Node (for each error
-        one, that means).
-        
-        """
-        re=doc.createElement("D:response")
-        # append namespaces to response
-        nsnum=0
-        for nsname in self.namespaces:
-            re.setAttribute("xmlns:ns"+str(nsnum),nsname)
-            nsnum=nsnum+1
-        
-        # write href information
-        uparts=urlparse.urlparse(uri)
-        fileloc=uparts[2]
-        href=doc.createElement("D:href")
-        huri=doc.createTextNode(urllib.quote(fileloc))
-        href.appendChild(huri)
-        re.appendChild(href)
-
-        # write good properties
-        ps=doc.createElement("D:propstat")
-        if good_props:
-            re.appendChild(ps)
-
-        gp=doc.createElement("D:prop")
-        for ns in good_props.keys():
-            ns_prefix="ns"+str(self.namespaces.index(ns))+":"
-            for p,v in good_props[ns].items():
-                pe=doc.createElement(ns_prefix+str(p))
-                if p=="resourcetype":
-                    if v=="1":
-                        ve=doc.createElement("D:collection")
-                        pe.appendChild(ve)
-                else:
-                    ve=doc.createTextNode(str(v))
-                    pe.appendChild(ve)
-
-                gp.appendChild(pe)
-        
-        ps.appendChild(gp)
-        s=doc.createElement("D:status")
-        t=doc.createTextNode("HTTP/1.1 200 OK")
-        s.appendChild(t)
-        ps.appendChild(s)
-        re.appendChild(ps)
-
-        # now write the errors!
-        if len(bad_props.items()):
-
-            # write a propstat for each error code
-            for ecode in bad_props.keys():
-                ps=doc.createElement("D:propstat")
-                re.appendChild(ps)
-                bp=doc.createElement("D:prop")
-                ps.appendChild(bp)
-
-                for ns in bad_props[ecode].keys():
-                    ns_prefix="ns"+str(self.namespaces.index(ns))+":"
-                
-                for p in bad_props[ecode][ns]:
-                    pe=doc.createElement(ns_prefix+str(p))
-                    bp.appendChild(pe)
-                
-                s=doc.createElement("D:status")
-                t=doc.createTextNode(utils.gen_estring(ecode))
-                s.appendChild(t)
-                ps.appendChild(s)
-                re.appendChild(ps)
-
-        # return the new response element
-        return re
-
-    def get_propvalues(self,uri):
-        """ create lists of property values for an URI 
-
-        We create two lists for an URI: the properties for
-        which we found a value and the ones for which we
-        only got an error, either because they haven't been
-        found or the user is not allowed to read them.
-        
-        """
-        good_props={}
-        bad_props={}
-
-        ddc = self.__dataclass
-        for (ns,plist) in self.proplist.items():
-            good_props[ns]={}
-            bad_props={}
-            for prop in plist:
-                ec = 0
-                try:
-                    r=ddc.get_prop(uri,ns,prop)
-                    good_props[ns][prop]=str(r)
-                except DAV_Error, error_code:
-                    ec=error_code[0]
-                
-                # ignore props with error_code if 0 (invisible)
-                if ec==0: continue
-                
-                if bad_props.has_key(ec):
-                    if bad_props[ec].has_key(ns):
-                        bad_props[ec][ns].append(prop)
-                    else:
-                        bad_props[ec][ns]=[prop]
-                else:
-                    bad_props[ec]={ns:[prop]}
-
-        return good_props, bad_props
-
diff --git a/DAV/report.py b/DAV/report.py
index a12721d..39457b7 100644
--- a/DAV/report.py
+++ b/DAV/report.py
@@ -1,3 +1,19 @@
+#Copyright (c) 2009 Cedric Krier (cedric.krier at b2ck.com)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 from propfind import PROPFIND
 from xml.dom import minidom
diff --git a/DAV/status.py b/DAV/status.py
index f719987..4133b40 100644
--- a/DAV/status.py
+++ b/DAV/status.py
@@ -1,6 +1,23 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 """
 
-status codes for DAV services 
+status codes for DAV services
 
 
 """
diff --git a/DAV/utils.py b/DAV/utils.py
index 2aa1961..53da88d 100755
--- a/DAV/utils.py
+++ b/DAV/utils.py
@@ -1,3 +1,20 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 import time
 import re
 
@@ -10,7 +27,7 @@ from StringIO import StringIO
 from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
 from BaseHTTPServer import BaseHTTPRequestHandler
 
-VERSION = '0.9.3'
+VERSION = '0.9.4-dev'
 AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
 
 def gen_estring(ecode):
@@ -115,8 +132,9 @@ def get_parenturi(uri):
 
 def make_xmlresponse(result):
     """ construct a response from a dict of uri:error_code elements """
-    doc = minidom.getDOMImplementation().createDocument(None, "D:multistatus", None)
+    doc = minidom.getDOMImplementation().createDocument(None, "multistatus", None)
     doc.documentElement.setAttribute("xmlns:D","DAV:")
+    doc.documentElement.tagName = "D:multistatus"
 
     for el,ec in result.items():
         re=doc.createElementNS("DAV:","response")
diff --git a/DAVServer/config.ini b/DAVServer/config.ini
index f9740b3..c46187f 100644
--- a/DAVServer/config.ini
+++ b/DAVServer/config.ini
@@ -23,8 +23,12 @@ firstrun=0
 [DAV]
 
 # Verbose?
+# verbose enabled is like loglevel = INFO
 verbose = 1
 
+#log level : DEBUG, INFO, WARNING, ERROR, CRITICAL (Default is WARNING)
+#loglevel = WARNING
+
 # main directory 
 directory = /home/spamies/tmp
 
@@ -54,3 +58,9 @@ mimecheck = 1
 
 # webdav level (1 = webdav level 2)
 lockemulation = 1
+
+# internal features
+#chunked_http_response = 1
+#http_request_use_iterator = 0
+#http_response_use_iterator = 0
+
diff --git a/DAVServer/daemonize.py b/DAVServer/daemonize.py
index 84e527d..409fb3a 100644
--- a/DAVServer/daemonize.py
+++ b/DAVServer/daemonize.py
@@ -1,9 +1,30 @@
+#Copyright (c) 2005 Simon Pamies (s.pamies at banality.de)
+#Copyright (c) 2003 Clark Evans
+#Copyright (c) 2002 Noah Spurrier
+#Copyright (c) 2001 Juergen Hermann
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
+
 '''
     This module is used to fork the current process into a daemon.
-    Almost none of this is necessary (or advisable) if your daemon 
-    is being started by inetd. In that case, stdin, stdout and stderr are 
-    all set up for you to refer to the network connection, and the fork()s 
-    and session manipulation should not be done (to avoid confusing inetd). 
+    Almost none of this is necessary (or advisable) if your daemon
+    is being started by inetd. In that case, stdin, stdout and stderr are
+    all set up for you to refer to the network connection, and the fork()s
+    and session manipulation should not be done (to avoid confusing inetd).
     Only the chdir() and umask() steps remain as useful.
     References:
         UNIX Programming FAQ
@@ -36,26 +57,26 @@ def deamonize(stdout='/dev/null', stderr=None, stdin='/dev/null',
         may not appear in the order that you expect.
     '''
     # Do first fork.
-    try: 
-        pid = os.fork() 
+    try:
+        pid = os.fork()
         if pid > 0: sys.exit(0) # Exit first parent.
-    except OSError, e: 
+    except OSError, e:
         sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
         sys.exit(1)
-        
+
     # Decouple from parent environment.
-    os.chdir("/") 
-    os.umask(0) 
-    os.setsid() 
-    
+    os.chdir("/")
+    os.umask(0)
+    os.setsid()
+
     # Do second fork.
-    try: 
-        pid = os.fork() 
+    try:
+        pid = os.fork()
         if pid > 0: sys.exit(0) # Exit second parent.
-    except OSError, e: 
+    except OSError, e:
         sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
         sys.exit(1)
-    
+
     # Open file descriptors and print start message
     if not stderr: stderr = stdout
     si = file(stdin, 'r')
@@ -69,7 +90,7 @@ def deamonize(stdout='/dev/null', stderr=None, stdin='/dev/null',
     if sys.stdin.closed: sys.stdin = open('/dev/null', 'r')
     if sys.stdout.closed: sys.stdout = open('/dev/null', 'a+')
     if sys.stderr.closed: sys.stderr = open('/dev/null', 'a+')
-    
+
     # Redirect standard file descriptors.
     os.dup2(si.fileno(), sys.stdin.fileno())
     os.dup2(so.fileno(), sys.stdout.fileno())
@@ -84,7 +105,7 @@ def startstop(stdout='/dev/null', stderr=None, stdin='/dev/null',
             pf.close()
         except IOError:
             pid = None
-         
+
         if 'stop' == action or 'restart' == action:
             if not pid:
                 mess = "Could not stop, pid file '%s' missing.\n"
@@ -109,7 +130,7 @@ def startstop(stdout='/dev/null', stderr=None, stdin='/dev/null',
                   else:
                       print str(err)
                       sys.exit(1)
-        
+
         if 'start' == action:
             if pid:
                 mess = "Start aborted since pid file '%s' exists.\n"
diff --git a/DAVServer/fileauth.py b/DAVServer/fileauth.py
index 5668e50..f2a649d 100644
--- a/DAVServer/fileauth.py
+++ b/DAVServer/fileauth.py
@@ -1,55 +1,58 @@
-#!/usr/bin/env python
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 """
 Python WebDAV Server.
-Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 This is an example implementation of a DAVserver using the DAV package.
 
 """
 
+import logging
 
 from DAV.WebDAVServer import DAVRequestHandler
 from fshandler import FilesystemHandler
 import sys
 from DAV.dbconn import Mconn
 
+log = logging.getLogger()
+
 class DAVAuthHandler(DAVRequestHandler):
     """
     Provides authentication based on parameters. The calling
     class has to inject password and username into this.
     (Variables: auth_user and auth_pass)
     """
-    
+
     # Do not forget to set IFACE_CLASS by caller
     # ex.: IFACE_CLASS = FilesystemHandler('/tmp', 'http://localhost/')
     verbose = False
-    
+
     def _log(self, message):
         if self.verbose:
-            print >>sys.stderr, '>> (DAVAuthHandler) %s' % message
+            log.info(message)
 
     def get_userinfo(self,user,pw,command):
         """ authenticate user """
 
         if user == self._config.DAV.user and pw == self._config.DAV.password:
-            self._log('Successfully authenticated user %s' % user)
+            log.info('Successfully authenticated user %s' % user)
             return 1
 
-        self._log('Authentication failed for user %s' % user)
+        log.info('Authentication failed for user %s' % user)
         return 0
 
diff --git a/DAVServer/fshandler.py b/DAVServer/fshandler.py
index 8d464fb..70e366f 100644
--- a/DAVServer/fshandler.py
+++ b/DAVServer/fshandler.py
@@ -1,9 +1,28 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 import sys
 import urlparse
 import os
 import time
 from string import joinfields, split, lower
+import logging
+import types
+import shutil
 
 from DAV.constants import COLLECTION, OBJECT
 from DAV.errors import *
@@ -11,14 +30,43 @@ from DAV.iface import *
 
 from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
 
+log = logging.getLogger(__name__)
+
+BUFFER_SIZE = 128 * 1000 
 # include magic support to correctly determine mimetypes
 MAGIC_AVAILABLE = False
 try:
-    import magic
+    import mimetypes
     MAGIC_AVAILABLE = True
 except ImportError:
     pass
 
+class Resource(object):
+    # XXX this class is ugly
+    def __init__(self, fp, file_size):
+        self.__fp = fp
+        self.__file_size = file_size
+
+    def __len__(self):
+        return self.__file_size
+
+    def __iter__(self):
+        while 1:
+            data = self.__fp.read(BUFFER_SIZE)
+            if not data:
+                break
+            yield data
+            time.sleep(0.005)
+        self.__fp.close()
+
+    def read(self, length = 0):
+        if length == 0:
+            length = self.__file_size
+
+        data = self.__fp.read(length)
+        return data
+        
+
 class FilesystemHandler(dav_interface):
     """ 
     Model a filesystem for DAV
@@ -39,18 +87,14 @@ class FilesystemHandler(dav_interface):
 
         # should we be verbose?
         self.verbose = verbose
-        self._log('Initialized with %s-%s' % (directory, uri))
-
-    def _log(self, message):
-        if self.verbose:
-            print >>sys.stderr, '>> (FilesystemHandler) %s' % message
+        log.info('Initialized with %s-%s' % (directory, uri))
 
     def setDirectory(self, path):
         """ Sets the directory """
 
         if not os.path.isdir(path):
             raise Exception, '%s not must be a directory!' % path
-        
+
         self.directory = path
 
     def setBaseURI(self, uri):
@@ -79,7 +123,7 @@ class FilesystemHandler(dav_interface):
 
     def get_childs(self,uri):
         """ return the child objects as self.baseuris for the given URI """
-        
+
         fileloc=self.uri2local(uri)
         filelist=[]
 
@@ -89,34 +133,51 @@ class FilesystemHandler(dav_interface):
                     files=os.listdir(fileloc)
                 except:
                     raise DAV_NotFound
-                
+
                 for file in files:
                     newloc=os.path.join(fileloc,file)
                     filelist.append(self.local2uri(newloc))
-     
-                self._log('get_childs: Childs %s' % filelist)
+
+                log.info('get_childs: Childs %s' % filelist)
 
         return filelist
 
-    def get_data(self,uri):
+    def get_data(self,uri, range = None):
         """ return the content of an object """
         path=self.uri2local(uri)
         if os.path.exists(path):
             if os.path.isfile(path):
-                s=""
-                fp=open(path,"r")
-                while 1:
-                    a=fp.read()
-                    if not a: break
-                    s=s+a
-                fp.close()
-                self._log('Serving content of %s' % uri)
-                return s
+                file_size = os.path.getsize(path)
+                if range == None:
+                    fp=open(path,"r")
+                    log.info('Serving content of %s' % uri)
+                    return Resource(fp, file_size)
+                else:
+                    if range[0] == '':
+                        range[0] = 0
+                    else:
+                        range[0] = int(range[0])
+
+                    if range[1] == '':
+                        range[1] = file_size
+                    else:
+                        range[1] = int(range[1])
+
+                    if range[0] > file_size:
+                        raise DAV_Requested_Range_Not_Satisfiable
+
+                    if range[1] > file_size:
+                        range[1] = file_size
+
+                    fp=open(path,"r")
+                    fp.seek(range[0])
+                    log.info('Serving range %s -> %s content of %s' % (range[0], range[1], uri))
+                    return Resource(fp, range[1] - range[0])
             else:
                 # also raise an error for collections
                 # don't know what should happen then..
-                self._log('get_data: %s not found' % path)
-        
+                log.info('get_data: %s not found' % path)
+
         raise DAV_NotFound
 
     def _get_dav_resourcetype(self,uri):
@@ -129,7 +190,7 @@ class FilesystemHandler(dav_interface):
             return COLLECTION
 
         raise DAV_NotFound
-        
+
     def _get_dav_displayname(self,uri):
         raise DAV_Secret    # do not show
 
@@ -140,7 +201,7 @@ class FilesystemHandler(dav_interface):
             if os.path.isfile(path):
                 s=os.stat(path)
                 return str(s[6])
-        
+
         return '0'
 
     def get_lastmodified(self,uri):
@@ -173,7 +234,7 @@ class FilesystemHandler(dav_interface):
                         or self.mimecheck is False:
                     return 'application/octet-stream'
                 else:
-                    ret = magic.file(path)
+                    ret, encoding = mimetypes.guess_type(path)
 
                     # for non mimetype related result we
                     # simply return an appropriate type
@@ -190,16 +251,21 @@ class FilesystemHandler(dav_interface):
 
         raise DAV_NotFound, 'Could not find %s' % path
 
-    def put(self,uri,data,content_type=None):
+    def put(self, uri, data, content_type=None):
         """ put the object into the filesystem """
         path=self.uri2local(uri)
         try:
-            fp=open(path,"w+")
-            fp.write(data)
+            fp=open(path, "w+")
+            if isinstance(data, types.GeneratorType):
+                for d in data:
+                    fp.write(d)
+            else:
+                if data:
+                    fp.write(data)
             fp.close()
-            self._log('put: Created %s' % uri)
+            log.info('put: Created %s' % uri)
         except:
-            self._log('put: Could not create %s' % uri)
+            log.info('put: Could not create %s' % uri)
             raise DAV_Error, 424
 
         return None
@@ -222,11 +288,11 @@ class FilesystemHandler(dav_interface):
 
         # test, if we are allowed to create it
         try:
-            os.system("mkdir '%s'" % path)
-            self._log('mkcol: Created new collection %s' % path)
+            os.mkdir(path)
+            log.info('mkcol: Created new collection %s' % path)
             return 201
         except:
-            self._log('mkcol: Creation of %s denied' % path)
+            log.info('mkcol: Creation of %s denied' % path)
             raise DAV_Forbidden
 
     ### ?? should we do the handler stuff for DELETE, too ?
@@ -238,22 +304,27 @@ class FilesystemHandler(dav_interface):
         if not os.path.exists(path):
             raise DAV_NotFound
 
-        if not os.system("rmdir '%s'" %path):
-            return 204
-        else:
+        try:
+            shutil.rmtree(path)
+        except OSError:
             raise DAV_Forbidden # forbidden
+        
+        return 204
 
     def rm(self,uri):
         """ delete a normal resource """
         path=self.uri2local(uri)
         if not os.path.exists(path):
             raise DAV_NotFound
-        if not os.system("rm -f '%s'" %path):
-            return 204
-        else:
-            self._log('rm: Forbidden')
+
+        try:
+            os.unlink(path)
+        except OSError, ex:
+            log.info('rm: Forbidden (%s)' % ex)
             raise DAV_Forbidden # forbidden
 
+        return 204
+
     ###
     ### DELETE handlers (examples)
     ### (we use the predefined methods in davcmd instead of doing
@@ -261,12 +332,12 @@ class FilesystemHandler(dav_interface):
     ###
 
     def delone(self,uri):
-        """ delete a single resource 
+        """ delete a single resource
 
         You have to return a result dict of the form
         uri:error_code
         or None if everything's ok
-        
+
         """
         return delone(self,uri)
 
@@ -286,40 +357,14 @@ class FilesystemHandler(dav_interface):
     ###
 
     def moveone(self,src,dst,overwrite):
-        """ move one resource with Depth=0 
-
-        an alternative implementation would be
-
-        result_code=201
-        if overwrite: 
-            result_code=204
-            r=os.system("rm -f '%s'" %dst)
-            if r: return 412
-        r=os.system("mv '%s' '%s'" %(src,dst))
-        if r: return 412
-        return result_code
-       
-        (untested!). This would not use the davcmd functions
-        and thus can only detect errors directly on the root node.
+        """ move one resource with Depth=0
         """
+
         return moveone(self,src,dst,overwrite)
 
     def movetree(self,src,dst,overwrite):
         """ move a collection with Depth=infinity
-
-        an alternative implementation would be
-
-        result_code=201
-        if overwrite: 
-            result_code=204
-            r=os.system("rm -rf '%s'" %dst)
-            if r: return 412
-        r=os.system("mv '%s' '%s'" %(src,dst))
-        if r: return 412
-        return result_code
-       
-        (untested!). This would not use the davcmd functions
-        and thus can only detect errors directly on the root node"""
+        """
 
         return movetree(self,src,dst,overwrite)
 
@@ -328,40 +373,14 @@ class FilesystemHandler(dav_interface):
     ###
 
     def copyone(self,src,dst,overwrite):
-        """ copy one resource with Depth=0 
-
-        an alternative implementation would be
-
-        result_code=201
-        if overwrite: 
-            result_code=204
-            r=os.system("rm -f '%s'" %dst)
-            if r: return 412
-        r=os.system("cp '%s' '%s'" %(src,dst))
-        if r: return 412
-        return result_code
-       
-        (untested!). This would not use the davcmd functions
-        and thus can only detect errors directly on the root node.
+        """ copy one resource with Depth=0
         """
+
         return copyone(self,src,dst,overwrite)
 
     def copytree(self,src,dst,overwrite):
         """ copy a collection with Depth=infinity
-
-        an alternative implementation would be
-
-        result_code=201
-        if overwrite: 
-            result_code=204
-            r=os.system("rm -rf '%s'" %dst)
-            if r: return 412
-        r=os.system("cp -r '%s' '%s'" %(src,dst))
-        if r: return 412
-        return result_code
-       
-        (untested!). This would not use the davcmd functions
-        and thus can only detect errors directly on the root node"""
+        """
 
         return copytree(self,src,dst,overwrite)
 
@@ -375,15 +394,16 @@ class FilesystemHandler(dav_interface):
 
     def copy(self,src,dst):
         """ copy a resource from src to dst """
+
         srcfile=self.uri2local(src)
         dstfile=self.uri2local(dst)
         try:
-            os.system("cp '%s' '%s'" %(srcfile,dstfile))
-        except:
-            self._log('copy: forbidden')
+            shutil.copy(srcfile, dstfile)
+        except OSError:
+            log.info('copy: forbidden')
             raise DAV_Error, Forbidden
 
-    def copycol(self,src,dst):
+    def copycol(self, src, dst):
         """ copy a collection.
 
         As this is not recursive (the davserver recurses itself)
@@ -394,7 +414,6 @@ class FilesystemHandler(dav_interface):
 
         return self.mkcol(dst)
 
-
     def exists(self,uri):
         """ test if a resource exists """
         path=self.uri2local(uri)
@@ -409,4 +428,3 @@ class FilesystemHandler(dav_interface):
             return 1
         else:
             return 0
-
diff --git a/DAVServer/magic.py b/DAVServer/magic.py
deleted file mode 100644
index b95951b..0000000
--- a/DAVServer/magic.py
+++ /dev/null
@@ -1,1118 +0,0 @@
-#!/usr/bin/env python 
-'''
-magic.py
- determines a file type by its magic number
-
- (C)opyright 2000 Jason Petrone <jp_py at jsnp.net>
- All Rights Reserved
-
- Command Line Usage: running as `python magic.py file` will print
-                     a description of what 'file' is.
-
- Module Usage:
-     magic.whatis(data): when passed a string 'data' containing 
-                         binary or text data, a description of
-                         what the data is will be returned.
-
-     magic.file(filename): returns a description of what the file
-                           'filename' contains.
-'''
-
-import re, struct, string
-
-__version__ = '0.1'
-
-magic = [
-  [0L, 'leshort', '=', 1538L, 'application/x-alan-adventure-game'],
-  [0L, 'string', '=', 'TADS', 'application/x-tads-game'],
-  [0L, 'short', '=', 420L, 'application/x-executable-file'],
-  [0L, 'short', '=', 421L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 603L, 'application/x-executable-file'],
-  [0L, 'string', '=', 'Core\001', 'application/x-executable-file'],
-  [0L, 'string', '=', 'AMANDA: TAPESTART DATE', 'application/x-amanda-header'],
-  [0L, 'belong', '=', 1011L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 999L, 'application/x-library-file'],
-  [0L, 'belong', '=', 435L, 'video/mpeg'],
-  [0L, 'belong', '=', 442L, 'video/mpeg'],
-  [0L, 'beshort&0xfff0', '=', 65520L, 'audio/mpeg'],
-  [4L, 'leshort', '=', 44817L, 'video/fli'],
-  [4L, 'leshort', '=', 44818L, 'video/flc'],
-  [0L, 'string', '=', 'MOVI', 'video/x-sgi-movie'],
-  [4L, 'string', '=', 'moov', 'video/quicktime'],
-  [4L, 'string', '=', 'mdat', 'video/quicktime'],
-  [0L, 'long', '=', 100554L, 'application/x-apl-workspace'],
-  [0L, 'string', '=', 'FiLeStArTfIlEsTaRt', 'text/x-apple-binscii'],
-  [0L, 'string', '=', '\012GL', 'application/data'],
-  [0L, 'string', '=', 'v\377', 'application/data'],
-  [0L, 'string', '=', 'NuFile', 'application/data'],
-  [0L, 'string', '=', 'N\365F\351l\345', 'application/data'],
-  [0L, 'belong', '=', 333312L, 'application/data'],
-  [0L, 'belong', '=', 333319L, 'application/data'],
-  [257L, 'string', '=', 'ustar\000', 'application/x-tar'],
-  [257L, 'string', '=', 'ustar  \000', 'application/x-gtar'],
-  [0L, 'short', '=', 70707L, 'application/x-cpio'],
-  [0L, 'short', '=', 143561L, 'application/x-bcpio'],
-  [0L, 'string', '=', '070707', 'application/x-cpio'],
-  [0L, 'string', '=', '070701', 'application/x-cpio'],
-  [0L, 'string', '=', '070702', 'application/x-cpio'],
-  [0L, 'string', '=', '!<arch>\012debian', 'application/x-dpkg'],
-  [0L, 'long', '=', 177555L, 'application/x-ar'],
-  [0L, 'short', '=', 177555L, 'application/data'],
-  [0L, 'long', '=', 177545L, 'application/data'],
-  [0L, 'short', '=', 177545L, 'application/data'],
-  [0L, 'long', '=', 100554L, 'application/x-apl-workspace'],
-  [0L, 'string', '=', '<ar>', 'application/x-ar'],
-  [0L, 'string', '=', '!<arch>\012__________E', 'application/x-ar'],
-  [0L, 'string', '=', '-h-', 'application/data'],
-  [0L, 'string', '=', '!<arch>', 'application/x-ar'],
-  [0L, 'string', '=', '<ar>', 'application/x-ar'],
-  [0L, 'string', '=', '<ar>', 'application/x-ar'],
-  [0L, 'belong', '=', 1711210496L, 'application/x-ar'],
-  [0L, 'belong', '=', 1013019198L, 'application/x-ar'],
-  [0L, 'long', '=', 557605234L, 'application/x-ar'],
-  [0L, 'lelong', '=', 177555L, 'application/data'],
-  [0L, 'leshort', '=', 177555L, 'application/data'],
-  [0L, 'lelong', '=', 177545L, 'application/data'],
-  [0L, 'leshort', '=', 177545L, 'application/data'],
-  [0L, 'lelong', '=', 236525L, 'application/data'],
-  [0L, 'lelong', '=', 236526L, 'application/data'],
-  [0L, 'lelong&0x8080ffff', '=', 2074L, 'application/x-arc'],
-  [0L, 'lelong&0x8080ffff', '=', 2330L, 'application/x-arc'],
-  [0L, 'lelong&0x8080ffff', '=', 538L, 'application/x-arc'],
-  [0L, 'lelong&0x8080ffff', '=', 794L, 'application/x-arc'],
-  [0L, 'lelong&0x8080ffff', '=', 1050L, 'application/x-arc'],
-  [0L, 'lelong&0x8080ffff', '=', 1562L, 'application/x-arc'],
-  [0L, 'string', '=', '\032archive', 'application/data'],
-  [0L, 'leshort', '=', 60000L, 'application/x-arj'],
-  [0L, 'string', '=', 'HPAK', 'application/data'],
-  [0L, 'string', '=', '\351,\001JAM application/data', ''],
-  [2L, 'string', '=', '-lh0-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh1-', 'application/x-lha'],
-  [2L, 'string', '=', '-lz4-', 'application/x-lha'],
-  [2L, 'string', '=', '-lz5-', 'application/x-lha'],
-  [2L, 'string', '=', '-lzs-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh -', 'application/x-lha'],
-  [2L, 'string', '=', '-lhd-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh2-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh3-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh4-', 'application/x-lha'],
-  [2L, 'string', '=', '-lh5-', 'application/x-lha'],
-  [0L, 'string', '=', 'Rar!', 'application/x-rar'],
-  [0L, 'string', '=', 'SQSH', 'application/data'],
-  [0L, 'string', '=', 'UC2\032', 'application/data'],
-  [0L, 'string', '=', 'PK\003\004', 'application/zip'],
-  [20L, 'lelong', '=', 4257523676L, 'application/x-zoo'],
-  [10L, 'string', '=', '# This is a shell archive', 'application/x-shar'],
-  [0L, 'string', '=', '*STA', 'application/data'],
-  [0L, 'string', '=', '2278', 'application/data'],
-  [0L, 'beshort', '=', 560L, 'application/x-executable-file'],
-  [0L, 'beshort', '=', 561L, 'application/x-executable-file'],
-  [0L, 'string', '=', '\000\004\036\212\200', 'application/core'],
-  [0L, 'string', '=', '.snd', 'audio/basic'],
-  [0L, 'lelong', '=', 6583086L, 'audio/basic'],
-  [0L, 'string', '=', 'MThd', 'audio/midi'],
-  [0L, 'string', '=', 'CTMF', 'audio/x-cmf'],
-  [0L, 'string', '=', 'SBI', 'audio/x-sbi'],
-  [0L, 'string', '=', 'Creative Voice File', 'audio/x-voc'],
-  [0L, 'belong', '=', 1314148939L, 'audio/x-multitrack'],
-  [0L, 'string', '=', 'RIFF', 'audio/x-wav'],
-  [0L, 'string', '=', 'EMOD', 'audio/x-emod'],
-  [0L, 'belong', '=', 779248125L, 'audio/x-pn-realaudio'],
-  [0L, 'string', '=', 'MTM', 'audio/x-multitrack'],
-  [0L, 'string', '=', 'if', 'audio/x-669-mod'],
-  [0L, 'string', '=', 'FAR', 'audio/mod'],
-  [0L, 'string', '=', 'MAS_U', 'audio/x-multimate-mod'],
-  [44L, 'string', '=', 'SCRM', 'audio/x-st3-mod'],
-  [0L, 'string', '=', 'GF1PATCH110\000ID#000002\000', 'audio/x-gus-patch'],
-  [0L, 'string', '=', 'GF1PATCH100\000ID#000002\000', 'audio/x-gus-patch'],
-  [0L, 'string', '=', 'JN', 'audio/x-669-mod'],
-  [0L, 'string', '=', 'UN05', 'audio/x-mikmod-uni'],
-  [0L, 'string', '=', 'Extended Module:', 'audio/x-ft2-mod'],
-  [21L, 'string', '=', '!SCREAM!', 'audio/x-st2-mod'],
-  [1080L, 'string', '=', 'M.K.', 'audio/x-protracker-mod'],
-  [1080L, 'string', '=', 'M!K!', 'audio/x-protracker-mod'],
-  [1080L, 'string', '=', 'FLT4', 'audio/x-startracker-mod'],
-  [1080L, 'string', '=', '4CHN', 'audio/x-fasttracker-mod'],
-  [1080L, 'string', '=', '6CHN', 'audio/x-fasttracker-mod'],
-  [1080L, 'string', '=', '8CHN', 'audio/x-fasttracker-mod'],
-  [1080L, 'string', '=', 'CD81', 'audio/x-oktalyzer-mod'],
-  [1080L, 'string', '=', 'OKTA', 'audio/x-oktalyzer-mod'],
-  [1080L, 'string', '=', '16CN', 'audio/x-taketracker-mod'],
-  [1080L, 'string', '=', '32CN', 'audio/x-taketracker-mod'],
-  [0L, 'string', '=', 'TOC', 'audio/x-toc'],
-  [0L, 'short', '=', 3401L, 'application/x-executable-file'],
-  [0L, 'long', '=', 406L, 'application/x-executable-file'],
-  [0L, 'short', '=', 406L, 'application/x-executable-file'],
-  [0L, 'short', '=', 3001L, 'application/x-executable-file'],
-  [0L, 'lelong', '=', 314L, 'application/x-executable-file'],
-  [0L, 'string', '=', '//', 'text/cpp'],
-  [0L, 'string', '=', '\\\\1cw\\', 'application/data'],
-  [0L, 'string', '=', '\\\\1cw', 'application/data'],
-  [0L, 'belong&0xffffff00', '=', 2231440384L, 'application/data'],
-  [0L, 'belong&0xffffff00', '=', 2231487232L, 'application/data'],
-  [0L, 'short', '=', 575L, 'application/x-executable-file'],
-  [0L, 'short', '=', 577L, 'application/x-executable-file'],
-  [4L, 'string', '=', 'pipe', 'application/data'],
-  [4L, 'string', '=', 'prof', 'application/data'],
-  [0L, 'string', '=', ': shell', 'application/data'],
-  [0L, 'string', '=', '#!/bin/sh', 'application/x-sh'],
-  [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'],
-  [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'],
-  [0L, 'string', '=', '#!/bin/csh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'],
-  [0L, 'string', '=', '#!/bin/ksh', 'application/x-ksh'],
-  [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'],
-  [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'],
-  [0L, 'string', '=', '#!/bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#!/usr/local/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /usr/local/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#!/usr/local/bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'],
-  [0L, 'string', '=', '#!/usr/local/bin/zsh', 'application/x-zsh'],
-  [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'],
-  [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'],
-  [0L, 'string', '=', '#!/usr/local/bin/ash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'],
-  [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'],
-  [0L, 'string', '=', '#!/usr/local/bin/ae', 'text/script'],
-  [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'],
-  [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'],
-  [0L, 'string', '=', '#!/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/usr/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/usr/local/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/usr/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/usr/local/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', '#!/usr/bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'],
-  [0L, 'string', '=', 'BEGIN', 'application/x-awk'],
-  [0L, 'string', '=', '#!/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', 'eval "exec /bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#!/usr/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', 'eval "exec /usr/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#!/usr/local/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', 'eval "exec /usr/local/bin/perl', 'application/x-perl'],
-  [0L, 'string', '=', '#!/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /bin/python', 'application/x-python'],
-  [0L, 'string', '=', 'eval "exec /bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#!/usr/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'],
-  [0L, 'string', '=', 'eval "exec /usr/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#!/usr/local/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'],
-  [0L, 'string', '=', 'eval "exec /usr/local/bin/python', 'application/x-python'],
-  [0L, 'string', '=', '#!/usr/bin/env python', 'application/x-python'],
-  [0L, 'string', '=', '#! /usr/bin/env python', 'application/x-python'],
-  [0L, 'string', '=', '#!/bin/rc', 'text/script'],
-  [0L, 'string', '=', '#! /bin/rc', 'text/script'],
-  [0L, 'string', '=', '#! /bin/rc', 'text/script'],
-  [0L, 'string', '=', '#!/bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#!/usr/local/bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'],
-  [0L, 'string', '=', '#! /', 'text/script'],
-  [0L, 'string', '=', '#! /', 'text/script'],
-  [0L, 'string', '=', '#!/', 'text/script'],
-  [0L, 'string', '=', '#! text/script', ''],
-  [0L, 'string', '=', '\037\235', 'application/compress'],
-  [0L, 'string', '=', '\037\213', 'application/x-gzip'],
-  [0L, 'string', '=', '\037\036', 'application/data'],
-  [0L, 'short', '=', 17437L, 'application/data'],
-  [0L, 'short', '=', 8191L, 'application/data'],
-  [0L, 'string', '=', '\377\037', 'application/data'],
-  [0L, 'short', '=', 145405L, 'application/data'],
-  [0L, 'string', '=', 'BZh', 'application/x-bzip2'],
-  [0L, 'leshort', '=', 65398L, 'application/data'],
-  [0L, 'leshort', '=', 65142L, 'application/data'],
-  [0L, 'leshort', '=', 64886L, 'application/x-lzh'],
-  [0L, 'string', '=', '\037\237', 'application/data'],
-  [0L, 'string', '=', '\037\236', 'application/data'],
-  [0L, 'string', '=', '\037\240', 'application/data'],
-  [0L, 'string', '=', 'BZ', 'application/x-bzip'],
-  [0L, 'string', '=', '\211LZO\000\015\012\032\012', 'application/data'],
-  [0L, 'belong', '=', 507L, 'application/x-object-file'],
-  [0L, 'belong', '=', 513L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 515L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 517L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 70231L, 'application/core'],
-  [24L, 'belong', '=', 60011L, 'application/data'],
-  [24L, 'belong', '=', 60012L, 'application/data'],
-  [24L, 'belong', '=', 60013L, 'application/data'],
-  [24L, 'belong', '=', 60014L, 'application/data'],
-  [0L, 'belong', '=', 601L, 'application/x-object-file'],
-  [0L, 'belong', '=', 607L, 'application/data'],
-  [0L, 'belong', '=', 324508366L, 'application/x-gdbm'],
-  [0L, 'lelong', '=', 324508366L, 'application/x-gdbm'],
-  [0L, 'string', '=', 'GDBM', 'application/x-gdbm'],
-  [0L, 'belong', '=', 398689L, 'application/x-db'],
-  [0L, 'belong', '=', 340322L, 'application/x-db'],
-  [0L, 'string', '=', '<list>\012<protocol bbn-m', 'application/data'],
-  [0L, 'string', '=', 'diff text/x-patch', ''],
-  [0L, 'string', '=', '*** text/x-patch', ''],
-  [0L, 'string', '=', 'Only in text/x-patch', ''],
-  [0L, 'string', '=', 'Common subdirectories: text/x-patch', ''],
-  [0L, 'string', '=', '!<arch>\012________64E', 'application/data'],
-  [0L, 'leshort', '=', 387L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 392L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 399L, 'application/x-object-file'],
-  [0L, 'string', '=', '\377\377\177', 'application/data'],
-  [0L, 'string', '=', '\377\377|', 'application/data'],
-  [0L, 'string', '=', '\377\377~', 'application/data'],
-  [0L, 'string', '=', '\033c\033', 'application/data'],
-  [0L, 'long', '=', 4553207L, 'image/x11'],
-  [0L, 'string', '=', '!<PDF>!\012', 'application/x-prof'],
-  [0L, 'short', '=', 1281L, 'application/x-locale'],
-  [24L, 'belong', '=', 60012L, 'application/x-dump'],
-  [24L, 'belong', '=', 60011L, 'application/x-dump'],
-  [24L, 'lelong', '=', 60012L, 'application/x-dump'],
-  [24L, 'lelong', '=', 60011L, 'application/x-dump'],
-  [0L, 'string', '=', '\177ELF', 'application/x-executable-file'],
-  [0L, 'short', '=', 340L, 'application/data'],
-  [0L, 'short', '=', 341L, 'application/x-executable-file'],
-  [1080L, 'leshort', '=', 61267L, 'application/x-linux-ext2fs'],
-  [0L, 'string', '=', '\366\366\366\366', 'application/x-pc-floppy'],
-  [774L, 'beshort', '=', 55998L, 'application/data'],
-  [510L, 'leshort', '=', 43605L, 'application/data'],
-  [1040L, 'leshort', '=', 4991L, 'application/x-filesystem'],
-  [1040L, 'leshort', '=', 5007L, 'application/x-filesystem'],
-  [1040L, 'leshort', '=', 9320L, 'application/x-filesystem'],
-  [1040L, 'leshort', '=', 9336L, 'application/x-filesystem'],
-  [0L, 'string', '=', '-rom1fs-\000', 'application/x-filesystem'],
-  [395L, 'string', '=', 'OS/2', 'application/x-bootable'],
-  [0L, 'string', '=', 'FONT', 'font/x-vfont'],
-  [0L, 'short', '=', 436L, 'font/x-vfont'],
-  [0L, 'short', '=', 17001L, 'font/x-vfont'],
-  [0L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'],
-  [6L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'],
-  [0L, 'belong', '=', 4L, 'font/x-snf'],
-  [0L, 'lelong', '=', 4L, 'font/x-snf'],
-  [0L, 'string', '=', 'STARTFONT font/x-bdf', ''],
-  [0L, 'string', '=', '\001fcp', 'font/x-pcf'],
-  [0L, 'string', '=', 'D1.0\015', 'font/x-speedo'],
-  [0L, 'string', '=', 'flf', 'font/x-figlet'],
-  [0L, 'string', '=', 'flc', 'application/x-font'],
-  [0L, 'belong', '=', 335698201L, 'font/x-libgrx'],
-  [0L, 'belong', '=', 4282797902L, 'font/x-dos'],
-  [7L, 'belong', '=', 4540225L, 'font/x-dos'],
-  [7L, 'belong', '=', 5654852L, 'font/x-dos'],
-  [4098L, 'string', '=', 'DOSFONT', 'font/x-dos'],
-  [0L, 'string', '=', '<MakerFile', 'application/x-framemaker'],
-  [0L, 'string', '=', '<MIFFile', 'application/x-framemaker'],
-  [0L, 'string', '=', '<MakerDictionary', 'application/x-framemaker'],
-  [0L, 'string', '=', '<MakerScreenFont', 'font/x-framemaker'],
-  [0L, 'string', '=', '<MML', 'application/x-framemaker'],
-  [0L, 'string', '=', '<BookFile', 'application/x-framemaker'],
-  [0L, 'string', '=', '<Maker', 'application/x-framemaker'],
-  [0L, 'lelong&0377777777', '=', 41400407L, 'application/x-executable-file'],
-  [0L, 'lelong&0377777777', '=', 41400410L, 'application/x-executable-file'],
-  [0L, 'lelong&0377777777', '=', 41400413L, 'application/x-executable-file'],
-  [0L, 'lelong&0377777777', '=', 41400314L, 'application/x-executable-file'],
-  [7L, 'string', '=', '\357\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000', 'application/core'],
-  [0L, 'lelong', '=', 11421044151L, 'application/data'],
-  [0L, 'string', '=', 'GIMP Gradient', 'application/x-gimp-gradient'],
-  [0L, 'string', '=', 'gimp xcf', 'application/x-gimp-image'],
-  [20L, 'string', '=', 'GPAT', 'application/x-gimp-pattern'],
-  [20L, 'string', '=', 'GIMP', 'application/x-gimp-brush'],
-  [0L, 'string', '=', '\336\022\004\225', 'application/x-locale'],
-  [0L, 'string', '=', '\225\004\022\336', 'application/x-locale'],
-  [0L, 'beshort', '=', 627L, 'application/x-executable-file'],
-  [0L, 'beshort', '=', 624L, 'application/x-executable-file'],
-  [0L, 'string', '=', '\000\001\000\000\000', 'font/ttf'],
-  [0L, 'long', '=', 1203604016L, 'application/data'],
-  [0L, 'long', '=', 1702407010L, 'application/data'],
-  [0L, 'long', '=', 1003405017L, 'application/data'],
-  [0L, 'long', '=', 1602007412L, 'application/data'],
-  [0L, 'belong', '=', 34603270L, 'application/x-object-file'],
-  [0L, 'belong', '=', 34603271L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34603272L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34603275L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34603278L, 'application/x-library-file'],
-  [0L, 'belong', '=', 34603277L, 'application/x-library-file'],
-  [0L, 'belong', '=', 34865414L, 'application/x-object-file'],
-  [0L, 'belong', '=', 34865415L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34865416L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34865419L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34865422L, 'application/x-library-file'],
-  [0L, 'belong', '=', 34865421L, 'application/x-object-file'],
-  [0L, 'belong', '=', 34275590L, 'application/x-object-file'],
-  [0L, 'belong', '=', 34275591L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34275592L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34275595L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34275598L, 'application/x-library-file'],
-  [0L, 'belong', '=', 34275597L, 'application/x-library-file'],
-  [0L, 'belong', '=', 557605234L, 'application/x-ar'],
-  [0L, 'long', '=', 34078982L, 'application/x-executable-file'],
-  [0L, 'long', '=', 34078983L, 'application/x-executable-file'],
-  [0L, 'long', '=', 34078984L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34341128L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34341127L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34341131L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34341126L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34210056L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34210055L, 'application/x-executable-file'],
-  [0L, 'belong', '=', 34341134L, 'application/x-library-file'],
-  [0L, 'belong', '=', 34341133L, 'application/x-library-file'],
-  [0L, 'long', '=', 65381L, 'application/x-library-file'],
-  [0L, 'long', '=', 34275173L, 'application/x-library-file'],
-  [0L, 'long', '=', 34406245L, 'application/x-library-file'],
-  [0L, 'long', '=', 34144101L, 'application/x-library-file'],
-  [0L, 'long', '=', 22552998L, 'application/core'],
-  [0L, 'long', '=', 1302851304L, 'font/x-hp-windows'],
-  [0L, 'string', '=', 'Bitmapfile', 'image/unknown'],
-  [0L, 'string', '=', 'IMGfile', 'CIS image/unknown'],
-  [0L, 'long', '=', 34341132L, 'application/x-lisp'],
-  [0L, 'string', '=', 'msgcat01', 'application/x-locale'],
-  [0L, 'string', '=', 'HPHP48-', 'HP48 binary'],
-  [0L, 'string', '=', '%%HP:', 'HP48 text'],
-  [0L, 'beshort', '=', 200L, 'hp200 (68010) BSD'],
-  [0L, 'beshort', '=', 300L, 'hp300 (68020+68881) BSD'],
-  [0L, 'beshort', '=', 537L, '370 XA sysV executable'],
-  [0L, 'beshort', '=', 532L, '370 XA sysV pure executable'],
-  [0L, 'beshort', '=', 54001L, '370 sysV pure executable'],
-  [0L, 'beshort', '=', 55001L, '370 XA sysV pure executable'],
-  [0L, 'beshort', '=', 56401L, '370 sysV executable'],
-  [0L, 'beshort', '=', 57401L, '370 XA sysV executable'],
-  [0L, 'beshort', '=', 531L, 'SVR2 executable (Amdahl-UTS)'],
-  [0L, 'beshort', '=', 534L, 'SVR2 pure executable (Amdahl-UTS)'],
-  [0L, 'beshort', '=', 530L, 'SVR2 pure executable (USS/370)'],
-  [0L, 'beshort', '=', 535L, 'SVR2 executable (USS/370)'],
-  [0L, 'beshort', '=', 479L, 'executable (RISC System/6000 V3.1) or obj module'],
-  [0L, 'beshort', '=', 260L, 'shared library'],
-  [0L, 'beshort', '=', 261L, 'ctab data'],
-  [0L, 'beshort', '=', 65028L, 'structured file'],
-  [0L, 'string', '=', '0xabcdef', 'AIX message catalog'],
-  [0L, 'belong', '=', 505L, 'AIX compiled message catalog'],
-  [0L, 'string', '=', '<aiaff>', 'archive'],
-  [0L, 'string', '=', 'FORM', 'IFF data'],
-  [0L, 'string', '=', 'P1', 'image/x-portable-bitmap'],
-  [0L, 'string', '=', 'P2', 'image/x-portable-graymap'],
-  [0L, 'string', '=', 'P3', 'image/x-portable-pixmap'],
-  [0L, 'string', '=', 'P4', 'image/x-portable-bitmap'],
-  [0L, 'string', '=', 'P5', 'image/x-portable-graymap'],
-  [0L, 'string', '=', 'P6', 'image/x-portable-pixmap'],
-  [0L, 'string', '=', 'IIN1', 'image/tiff'],
-  [0L, 'string', '=', 'MM\000*', 'image/tiff'],
-  [0L, 'string', '=', 'II*\000', 'image/tiff'],
-  [0L, 'string', '=', '\211PNG', 'image/x-png'],
-  [1L, 'string', '=', 'PNG', 'image/x-png'],
-  [0L, 'string', '=', 'GIF8', 'image/gif'],
-  [0L, 'string', '=', '\361\000@\273', 'image/x-cmu-raster'],
-  [0L, 'string', '=', 'id=ImageMagick', 'MIFF image data'],
-  [0L, 'long', '=', 1123028772L, 'Artisan image data'],
-  [0L, 'string', '=', '#FIG', 'FIG image text'],
-  [0L, 'string', '=', 'ARF_BEGARF', 'PHIGS clear text archive'],
-  [0L, 'string', '=', '@(#)SunPHIGS', 'SunPHIGS'],
-  [0L, 'string', '=', 'GKSM', 'GKS Metafile'],
-  [0L, 'string', '=', 'BEGMF', 'clear text Computer Graphics Metafile'],
-  [0L, 'beshort&0xffe0', '=', 32L, 'binary Computer Graphics Metafile'],
-  [0L, 'beshort', '=', 12320L, 'character Computer Graphics Metafile'],
-  [0L, 'string', '=', 'yz', 'MGR bitmap, modern format, 8-bit aligned'],
-  [0L, 'string', '=', 'zz', 'MGR bitmap, old format, 1-bit deep, 16-bit aligned'],
-  [0L, 'string', '=', 'xz', 'MGR bitmap, old format, 1-bit deep, 32-bit aligned'],
-  [0L, 'string', '=', 'yx', 'MGR bitmap, modern format, squeezed'],
-  [0L, 'string', '=', '%bitmap\000', 'FBM image data'],
-  [1L, 'string', '=', 'PC Research, Inc', 'group 3 fax data'],
-  [0L, 'beshort', '=', 65496L, 'image/jpeg'],
-  [0L, 'string', '=', 'hsi1', 'image/x-jpeg-proprietary'],
-  [0L, 'string', '=', 'BM', 'image/x-bmp'],
-  [0L, 'string', '=', 'IC', 'image/x-ico'],
-  [0L, 'string', '=', 'PI', 'PC pointer image data'],
-  [0L, 'string', '=', 'CI', 'PC color icon data'],
-  [0L, 'string', '=', 'CP', 'PC color pointer image data'],
-  [0L, 'string', '=', '/* XPM */', 'X pixmap image text'],
-  [0L, 'leshort', '=', 52306L, 'RLE image data,'],
-  [0L, 'string', '=', 'Imagefile version-', 'iff image data'],
-  [0L, 'belong', '=', 1504078485L, 'x/x-image-sun-raster'],
-  [0L, 'beshort', '=', 474L, 'x/x-image-sgi'],
-  [0L, 'string', '=', 'IT01', 'FIT image data'],
-  [0L, 'string', '=', 'IT02', 'FIT image data'],
-  [2048L, 'string', '=', 'PCD_IPI', 'x/x-photo-cd-pack-file'],
-  [0L, 'string', '=', 'PCD_OPA', 'x/x-photo-cd-overfiew-file'],
-  [0L, 'string', '=', 'SIMPLE  =', 'FITS image data'],
-  [0L, 'string', '=', 'This is a BitMap file', 'Lisp Machine bit-array-file'],
-  [0L, 'string', '=', '!!', 'Bennet Yee\'s "face" format'],
-  [0L, 'beshort', '=', 4112L, 'PEX Binary Archive'],
-  [3000L, 'string', '=', 'Visio (TM) Drawing', '%s'],
-  [0L, 'leshort', '=', 502L, 'basic-16 executable'],
-  [0L, 'leshort', '=', 503L, 'basic-16 executable (TV)'],
-  [0L, 'leshort', '=', 510L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 511L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 512L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 522L, 'application/x-executable-file'],
-  [0L, 'leshort', '=', 514L, 'application/x-executable-file'],
-  [0L, 'string', '=', '\210OPS', 'Interleaf saved data'],
-  [0L, 'string', '=', '<!OPS', 'Interleaf document text'],
-  [4L, 'string', '=', 'pgscriptver', 'IslandWrite document'],
-  [13L, 'string', '=', 'DrawFile', 'IslandDraw document'],
-  [0L, 'leshort&0xFFFC', '=', 38400L, 'little endian ispell'],
-  [0L, 'beshort&0xFFFC', '=', 38400L, 'big endian ispell'],
-  [0L, 'belong', '=', 3405691582L, 'compiled Java class data,'],
-  [0L, 'beshort', '=', 44269L, 'Java serialization data'],
-  [0L, 'string', '=', 'KarmaRHD', 'Version Karma Data Structure Version'],
-  [0L, 'string', '=', 'lect', 'DEC SRC Virtual Paper Lectern file'],
-  [53L, 'string', '=', 'yyprevious', 'C program text (from lex)'],
-  [21L, 'string', '=', 'generated by flex', 'C program text (from flex)'],
-  [0L, 'string', '=', '%{', 'lex description text'],
-  [0L, 'short', '=', 32768L, 'lif file'],
-  [0L, 'lelong', '=', 6553863L, 'Linux/i386 impure executable (OMAGIC)'],
-  [0L, 'lelong', '=', 6553864L, 'Linux/i386 pure executable (NMAGIC)'],
-  [0L, 'lelong', '=', 6553867L, 'Linux/i386 demand-paged executable (ZMAGIC)'],
-  [0L, 'lelong', '=', 6553804L, 'Linux/i386 demand-paged executable (QMAGIC)'],
-  [0L, 'string', '=', '\007\001\000', 'Linux/i386 object file'],
-  [0L, 'string', '=', '\001\003\020\004', 'Linux-8086 impure executable'],
-  [0L, 'string', '=', '\001\003 \004', 'Linux-8086 executable'],
-  [0L, 'string', '=', '\243\206\001\000', 'Linux-8086 object file'],
-  [0L, 'string', '=', '\001\003\020\020', 'Minix-386 impure executable'],
-  [0L, 'string', '=', '\001\003 \020', 'Minix-386 executable'],
-  [0L, 'string', '=', '*nazgul*', 'Linux compiled message catalog'],
-  [216L, 'lelong', '=', 421L, 'Linux/i386 core file'],
-  [2L, 'string', '=', 'LILO', 'Linux/i386 LILO boot/chain loader'],
-  [0L, 'string', '=', '0.9', ''],
-  [0L, 'leshort', '=', 1078L, 'font/linux-psf'],
-  [4086L, 'string', '=', 'SWAP-SPACE', 'Linux/i386 swap file'],
-  [0L, 'leshort', '=', 387L, 'ECOFF alpha'],
-  [514L, 'string', '=', 'HdrS', 'Linux kernel'],
-  [0L, 'belong', '=', 3099592590L, 'Linux kernel'],
-  [0L, 'string', '=', 'Begin3', 'Linux Software Map entry text'],
-  [0L, 'string', '=', ';;', 'Lisp/Scheme program text'],
-  [0L, 'string', '=', '\012(', 'byte-compiled Emacs-Lisp program data'],
-  [0L, 'string', '=', ';ELC\023\000\000\000', 'byte-compiled Emacs-Lisp program data'],
-  [0L, 'string', '=', "(SYSTEM::VERSION '", 'CLISP byte-compiled Lisp program text'],
-  [0L, 'long', '=', 1886817234L, 'CLISP memory image data'],
-  [0L, 'long', '=', 3532355184L, 'CLISP memory image data, other endian'],
-  [0L, 'long', '=', 3725722773L, 'GNU-format message catalog data'],
-  [0L, 'long', '=', 2500072158L, 'GNU-format message catalog data'],
-  [0L, 'belong', '=', 3405691582L, 'mach-o fat file'],
-  [0L, 'belong', '=', 4277009102L, 'mach-o'],
-  [11L, 'string', '=', 'must be converted with BinHex', 'BinHex binary text'],
-  [0L, 'string', '=', 'SIT!', 'StuffIt Archive (data)'],
-  [65L, 'string', '=', 'SIT!', 'StuffIt Archive (rsrc + data)'],
-  [0L, 'string', '=', 'SITD', 'StuffIt Deluxe (data)'],
-  [65L, 'string', '=', 'SITD', 'StuffIt Deluxe (rsrc + data)'],
-  [0L, 'string', '=', 'Seg', 'StuffIt Deluxe Segment (data)'],
-  [65L, 'string', '=', 'Seg', 'StuffIt Deluxe Segment (rsrc + data)'],
-  [0L, 'string', '=', 'APPL', 'Macintosh Application (data)'],
-  [65L, 'string', '=', 'APPL', 'Macintosh Application (rsrc + data)'],
-  [0L, 'string', '=', 'zsys', 'Macintosh System File (data)'],
-  [65L, 'string', '=', 'zsys', 'Macintosh System File(rsrc + data)'],
-  [0L, 'string', '=', 'FNDR', 'Macintosh Finder (data)'],
-  [65L, 'string', '=', 'FNDR', 'Macintosh Finder(rsrc + data)'],
-  [0L, 'string', '=', 'libr', 'Macintosh Library (data)'],
-  [65L, 'string', '=', 'libr', 'Macintosh Library(rsrc + data)'],
-  [0L, 'string', '=', 'shlb', 'Macintosh Shared Library (data)'],
-  [65L, 'string', '=', 'shlb', 'Macintosh Shared Library(rsrc + data)'],
-  [0L, 'string', '=', 'cdev', 'Macintosh Control Panel (data)'],
-  [65L, 'string', '=', 'cdev', 'Macintosh Control Panel(rsrc + data)'],
-  [0L, 'string', '=', 'INIT', 'Macintosh Extension (data)'],
-  [65L, 'string', '=', 'INIT', 'Macintosh Extension(rsrc + data)'],
-  [0L, 'string', '=', 'FFIL', 'font/ttf'],
-  [65L, 'string', '=', 'FFIL', 'font/ttf'],
-  [0L, 'string', '=', 'LWFN', 'font/type1'],
-  [65L, 'string', '=', 'LWFN', 'font/type1'],
-  [0L, 'string', '=', 'PACT', 'Macintosh Compact Pro Archive (data)'],
-  [65L, 'string', '=', 'PACT', 'Macintosh Compact Pro Archive(rsrc + data)'],
-  [0L, 'string', '=', 'ttro', 'Macintosh TeachText File (data)'],
-  [65L, 'string', '=', 'ttro', 'Macintosh TeachText File(rsrc + data)'],
-  [0L, 'string', '=', 'TEXT', 'Macintosh TeachText File (data)'],
-  [65L, 'string', '=', 'TEXT', 'Macintosh TeachText File(rsrc + data)'],
-  [0L, 'string', '=', 'PDF', 'Macintosh PDF File (data)'],
-  [65L, 'string', '=', 'PDF', 'Macintosh PDF File(rsrc + data)'],
-  [0L, 'string', '=', '# Magic', 'magic text file for file(1) cmd'],
-  [0L, 'string', '=', 'Relay-Version:', 'old news text'],
-  [0L, 'string', '=', '#! rnews', 'batched news text'],
-  [0L, 'string', '=', 'N#! rnews', 'mailed, batched news text'],
-  [0L, 'string', '=', 'Forward to', 'mail forwarding text'],
-  [0L, 'string', '=', 'Pipe to', 'mail piping text'],
-  [0L, 'string', '=', 'Return-Path:', 'message/rfc822'],
-  [0L, 'string', '=', 'Path:', 'message/news'],
-  [0L, 'string', '=', 'Xref:', 'message/news'],
-  [0L, 'string', '=', 'From:', 'message/rfc822'],
-  [0L, 'string', '=', 'Article', 'message/news'],
-  [0L, 'string', '=', 'BABYL', 'message/x-gnu-rmail'],
-  [0L, 'string', '=', 'Received:', 'message/rfc822'],
-  [0L, 'string', '=', 'MIME-Version:', 'MIME entity text'],
-  [0L, 'string', '=', 'Content-Type: ', ''],
-  [0L, 'string', '=', 'Content-Type:', ''],
-  [0L, 'long', '=', 31415L, 'Mirage Assembler m.out executable'],
-  [0L, 'string', '=', '\311\304', 'ID tags data'],
-  [0L, 'string', '=', '\001\001\001\001', 'MMDF mailbox'],
-  [4L, 'string', '=', 'Research,', 'Digifax-G3-File'],
-  [0L, 'short', '=', 256L, 'raw G3 data, byte-padded'],
-  [0L, 'short', '=', 5120L, 'raw G3 data'],
-  [0L, 'string', '=', 'RMD1', 'raw modem data'],
-  [0L, 'string', '=', 'PVF1\012', 'portable voice format'],
-  [0L, 'string', '=', 'PVF2\012', 'portable voice format'],
-  [0L, 'beshort', '=', 520L, 'mc68k COFF'],
-  [0L, 'beshort', '=', 521L, 'mc68k executable (shared)'],
-  [0L, 'beshort', '=', 522L, 'mc68k executable (shared demand paged)'],
-  [0L, 'beshort', '=', 554L, '68K BCS executable'],
-  [0L, 'beshort', '=', 555L, '88K BCS executable'],
-  [0L, 'string', '=', 'S0', 'Motorola S-Record; binary data in text format'],
-  [0L, 'string', '=', '@echo off', 'MS-DOS batch file text'],
-  [128L, 'string', '=', 'PE\000\000', 'MS Windows PE'],
-  [0L, 'leshort', '=', 332L, 'MS Windows COFF Intel 80386 object file'],
-  [0L, 'leshort', '=', 358L, 'MS Windows COFF MIPS R4000 object file'],
-  [0L, 'leshort', '=', 388L, 'MS Windows COFF Alpha object file'],
-  [0L, 'leshort', '=', 616L, 'MS Windows COFF Motorola 68000 object file'],
-  [0L, 'leshort', '=', 496L, 'MS Windows COFF PowerPC object file'],
-  [0L, 'leshort', '=', 656L, 'MS Windows COFF PA-RISC object file'],
-  [0L, 'string', '=', 'MZ', 'application/x-ms-dos-executable'],
-  [0L, 'string', '=', 'LZ', 'MS-DOS executable (built-in)'],
-  [0L, 'string', '=', 'regf', 'Windows NT Registry file'],
-  [2080L, 'string', '=', 'Microsoft Word 6.0 Document', 'text/vnd.ms-word'],
-  [2080L, 'string', '=', 'Documento Microsoft Word 6', 'text/vnd.ms-word'],
-  [2112L, 'string', '=', 'MSWordDoc', 'text/vnd.ms-word'],
-  [0L, 'belong', '=', 834535424L, 'text/vnd.ms-word'],
-  [0L, 'string', '=', 'PO^Q`', 'text/vnd.ms-word'],
-  [2080L, 'string', '=', 'Microsoft Excel 5.0 Worksheet', 'application/vnd.ms-excel'],
-  [2114L, 'string', '=', 'Biff5', 'application/vnd.ms-excel'],
-  [0L, 'belong', '=', 6656L, 'Lotus 1-2-3'],
-  [0L, 'belong', '=', 512L, 'Lotus 1-2-3'],
-  [1L, 'string', '=', 'WPC', 'text/vnd.wordperfect'],
-  [0L, 'beshort', '=', 610L, 'Tower/XP rel 2 object'],
-  [0L, 'beshort', '=', 615L, 'Tower/XP rel 2 object'],
-  [0L, 'beshort', '=', 620L, 'Tower/XP rel 3 object'],
-  [0L, 'beshort', '=', 625L, 'Tower/XP rel 3 object'],
-  [0L, 'beshort', '=', 630L, 'Tower32/600/400 68020 object'],
-  [0L, 'beshort', '=', 640L, 'Tower32/800 68020'],
-  [0L, 'beshort', '=', 645L, 'Tower32/800 68010'],
-  [0L, 'lelong', '=', 407L, 'NetBSD little-endian object file'],
-  [0L, 'belong', '=', 407L, 'NetBSD big-endian object file'],
-  [0L, 'belong&0377777777', '=', 41400413L, 'NetBSD/i386 demand paged'],
-  [0L, 'belong&0377777777', '=', 41400410L, 'NetBSD/i386 pure'],
-  [0L, 'belong&0377777777', '=', 41400407L, 'NetBSD/i386'],
-  [0L, 'belong&0377777777', '=', 41400507L, 'NetBSD/i386 core'],
-  [0L, 'belong&0377777777', '=', 41600413L, 'NetBSD/m68k demand paged'],
-  [0L, 'belong&0377777777', '=', 41600410L, 'NetBSD/m68k pure'],
-  [0L, 'belong&0377777777', '=', 41600407L, 'NetBSD/m68k'],
-  [0L, 'belong&0377777777', '=', 41600507L, 'NetBSD/m68k core'],
-  [0L, 'belong&0377777777', '=', 42000413L, 'NetBSD/m68k4k demand paged'],
-  [0L, 'belong&0377777777', '=', 42000410L, 'NetBSD/m68k4k pure'],
-  [0L, 'belong&0377777777', '=', 42000407L, 'NetBSD/m68k4k'],
-  [0L, 'belong&0377777777', '=', 42000507L, 'NetBSD/m68k4k core'],
-  [0L, 'belong&0377777777', '=', 42200413L, 'NetBSD/ns32532 demand paged'],
-  [0L, 'belong&0377777777', '=', 42200410L, 'NetBSD/ns32532 pure'],
-  [0L, 'belong&0377777777', '=', 42200407L, 'NetBSD/ns32532'],
-  [0L, 'belong&0377777777', '=', 42200507L, 'NetBSD/ns32532 core'],
-  [0L, 'belong&0377777777', '=', 42400413L, 'NetBSD/sparc demand paged'],
-  [0L, 'belong&0377777777', '=', 42400410L, 'NetBSD/sparc pure'],
-  [0L, 'belong&0377777777', '=', 42400407L, 'NetBSD/sparc'],
-  [0L, 'belong&0377777777', '=', 42400507L, 'NetBSD/sparc core'],
-  [0L, 'belong&0377777777', '=', 42600413L, 'NetBSD/pmax demand paged'],
-  [0L, 'belong&0377777777', '=', 42600410L, 'NetBSD/pmax pure'],
-  [0L, 'belong&0377777777', '=', 42600407L, 'NetBSD/pmax'],
-  [0L, 'belong&0377777777', '=', 42600507L, 'NetBSD/pmax core'],
-  [0L, 'belong&0377777777', '=', 43000413L, 'NetBSD/vax demand paged'],
-  [0L, 'belong&0377777777', '=', 43000410L, 'NetBSD/vax pure'],
-  [0L, 'belong&0377777777', '=', 43000407L, 'NetBSD/vax'],
-  [0L, 'belong&0377777777', '=', 43000507L, 'NetBSD/vax core'],
-  [0L, 'lelong', '=', 459141L, 'ECOFF NetBSD/alpha binary'],
-  [0L, 'belong&0377777777', '=', 43200507L, 'NetBSD/alpha core'],
-  [0L, 'belong&0377777777', '=', 43400413L, 'NetBSD/mips demand paged'],
-  [0L, 'belong&0377777777', '=', 43400410L, 'NetBSD/mips pure'],
-  [0L, 'belong&0377777777', '=', 43400407L, 'NetBSD/mips'],
-  [0L, 'belong&0377777777', '=', 43400507L, 'NetBSD/mips core'],
-  [0L, 'belong&0377777777', '=', 43600413L, 'NetBSD/arm32 demand paged'],
-  [0L, 'belong&0377777777', '=', 43600410L, 'NetBSD/arm32 pure'],
-  [0L, 'belong&0377777777', '=', 43600407L, 'NetBSD/arm32'],
-  [0L, 'belong&0377777777', '=', 43600507L, 'NetBSD/arm32 core'],
-  [0L, 'string', '=', 'StartFontMetrics', 'font/x-sunos-news'],
-  [0L, 'string', '=', 'StartFont', 'font/x-sunos-news'],
-  [0L, 'belong', '=', 326773060L, 'font/x-sunos-news'],
-  [0L, 'belong', '=', 326773063L, 'font/x-sunos-news'],
-  [0L, 'belong', '=', 326773072L, 'font/x-sunos-news'],
-  [0L, 'belong', '=', 326773073L, 'font/x-sunos-news'],
-  [8L, 'belong', '=', 326773573L, 'font/x-sunos-news'],
-  [8L, 'belong', '=', 326773576L, 'font/x-sunos-news'],
-  [0L, 'string', '=', 'Octave-1-L', 'Octave binary data (little endian)'],
-  [0L, 'string', '=', 'Octave-1-B', 'Octave binary data (big endian)'],
-  [0L, 'string', '=', '\177OLF', 'OLF'],
-  [0L, 'beshort', '=', 34765L, 'OS9/6809 module:'],
-  [0L, 'beshort', '=', 19196L, 'OS9/68K module:'],
-  [0L, 'long', '=', 61374L, 'OSF/Rose object'],
-  [0L, 'short', '=', 565L, 'i386 COFF object'],
-  [0L, 'short', '=', 10775L, '"compact bitmap" format (Poskanzer)'],
-  [0L, 'string', '=', '%PDF-', 'PDF document'],
-  [0L, 'lelong', '=', 101555L, 'PDP-11 single precision APL workspace'],
-  [0L, 'lelong', '=', 101554L, 'PDP-11 double precision APL workspace'],
-  [0L, 'leshort', '=', 407L, 'PDP-11 executable'],
-  [0L, 'leshort', '=', 401L, 'PDP-11 UNIX/RT ldp'],
-  [0L, 'leshort', '=', 405L, 'PDP-11 old overlay'],
-  [0L, 'leshort', '=', 410L, 'PDP-11 pure executable'],
-  [0L, 'leshort', '=', 411L, 'PDP-11 separate I&D executable'],
-  [0L, 'leshort', '=', 437L, 'PDP-11 kernel overlay'],
-  [0L, 'beshort', '=', 39168L, 'PGP key public ring'],
-  [0L, 'beshort', '=', 38145L, 'PGP key security ring'],
-  [0L, 'beshort', '=', 38144L, 'PGP key security ring'],
-  [0L, 'beshort', '=', 42496L, 'PGP encrypted data'],
-  [0L, 'string', '=', '-----BEGIN PGP', 'PGP armored data'],
-  [0L, 'string', '=', '# PaCkAgE DaTaStReAm', 'pkg Datastream (SVR4)'],
-  [0L, 'short', '=', 601L, 'mumps avl global'],
-  [0L, 'short', '=', 602L, 'mumps blt global'],
-  [0L, 'string', '=', '%!', 'application/postscript'],
-  [0L, 'string', '=', '\004%!', 'application/postscript'],
-  [0L, 'belong', '=', 3318797254L, 'DOS EPS Binary File'],
-  [0L, 'string', '=', '*PPD-Adobe:', 'PPD file'],
-  [0L, 'string', '=', '\033%-12345X at PJL', 'HP Printer Job Language data'],
-  [0L, 'string', '=', '\033%-12345X at PJL', 'HP Printer Job Language data'],
-  [0L, 'string', '=', '\033E\033', 'image/x-pcl-hp'],
-  [0L, 'string', '=', '@document(', 'Imagen printer'],
-  [0L, 'string', '=', 'Rast', 'RST-format raster font data'],
-  [0L, 'belong&0xff00ffff', '=', 1442840576L, 'ps database'],
-  [0L, 'long', '=', 1351614727L, 'Pyramid 90x family executable'],
-  [0L, 'long', '=', 1351614728L, 'Pyramid 90x family pure executable'],
-  [0L, 'long', '=', 1351614731L, 'Pyramid 90x family demand paged pure executable'],
-  [0L, 'beshort', '=', 60843L, ''],
-  [0L, 'string', '=', '{\\\\rtf', 'Rich Text Format data,'],
-  [38L, 'string', '=', 'Spreadsheet', 'sc spreadsheet file'],
-  [8L, 'string', '=', '\001s SCCS', 'archive data'],
-  [0L, 'byte', '=', 46L, 'Sendmail frozen configuration'],
-  [0L, 'short', '=', 10012L, 'Sendmail frozen configuration'],
-  [0L, 'lelong', '=', 234L, 'BALANCE NS32000 .o'],
-  [0L, 'lelong', '=', 4330L, 'BALANCE NS32000 executable (0 @ 0)'],
-  [0L, 'lelong', '=', 8426L, 'BALANCE NS32000 executable (invalid @ 0)'],
-  [0L, 'lelong', '=', 12522L, 'BALANCE NS32000 standalone executable'],
-  [0L, 'leshort', '=', 4843L, 'SYMMETRY i386 .o'],
-  [0L, 'leshort', '=', 8939L, 'SYMMETRY i386 executable (0 @ 0)'],
-  [0L, 'leshort', '=', 13035L, 'SYMMETRY i386 executable (invalid @ 0)'],
-  [0L, 'leshort', '=', 17131L, 'SYMMETRY i386 standalone executable'],
-  [0L, 'string', '=', 'kbd!map', 'kbd map file'],
-  [0L, 'belong', '=', 407L, 'old SGI 68020 executable'],
-  [0L, 'belong', '=', 410L, 'old SGI 68020 pure executable'],
-  [0L, 'beshort', '=', 34661L, 'disk quotas file'],
-  [0L, 'beshort', '=', 1286L, 'IRIS Showcase file'],
-  [0L, 'beshort', '=', 550L, 'IRIS Showcase template'],
-  [0L, 'belong', '=', 1396917837L, 'IRIS Showcase file'],
-  [0L, 'belong', '=', 1413695053L, 'IRIS Showcase template'],
-  [0L, 'belong', '=', 3735927486L, 'IRIX Parallel Arena'],
-  [0L, 'beshort', '=', 352L, 'MIPSEB COFF executable'],
-  [0L, 'beshort', '=', 354L, 'MIPSEL COFF executable'],
-  [0L, 'beshort', '=', 24577L, 'MIPSEB-LE COFF executable'],
-  [0L, 'beshort', '=', 25089L, 'MIPSEL-LE COFF executable'],
-  [0L, 'beshort', '=', 355L, 'MIPSEB MIPS-II COFF executable'],
-  [0L, 'beshort', '=', 358L, 'MIPSEL MIPS-II COFF executable'],
-  [0L, 'beshort', '=', 25345L, 'MIPSEB-LE MIPS-II COFF executable'],
-  [0L, 'beshort', '=', 26113L, 'MIPSEL-LE MIPS-II COFF executable'],
-  [0L, 'beshort', '=', 320L, 'MIPSEB MIPS-III COFF executable'],
-  [0L, 'beshort', '=', 322L, 'MIPSEL MIPS-III COFF executable'],
-  [0L, 'beshort', '=', 16385L, 'MIPSEB-LE MIPS-III COFF executable'],
-  [0L, 'beshort', '=', 16897L, 'MIPSEL-LE MIPS-III COFF executable'],
-  [0L, 'beshort', '=', 384L, 'MIPSEB Ucode'],
-  [0L, 'beshort', '=', 386L, 'MIPSEL Ucode'],
-  [0L, 'belong', '=', 3735924144L, 'IRIX core dump'],
-  [0L, 'belong', '=', 3735924032L, 'IRIX 64-bit core dump'],
-  [0L, 'belong', '=', 3133063355L, 'IRIX N32 core dump'],
-  [0L, 'string', '=', 'CrshDump', 'IRIX vmcore dump of'],
-  [0L, 'string', '=', 'SGIAUDIT', 'SGI Audit file'],
-  [0L, 'string', '=', 'WNGZWZSC', 'Wingz compiled script'],
-  [0L, 'string', '=', 'WNGZWZSS', 'Wingz spreadsheet'],
-  [0L, 'string', '=', 'WNGZWZHP', 'Wingz help file'],
-  [0L, 'string', '=', '\\#Inventor', 'V IRIS Inventor 1.0 file'],
-  [0L, 'string', '=', '\\#Inventor', 'V2 Open Inventor 2.0 file'],
-  [0L, 'string', '=', 'glfHeadMagic();', 'GLF_TEXT'],
-  [4L, 'belong', '=', 1090584576L, 'GLF_BINARY_LSB_FIRST'],
-  [4L, 'belong', '=', 321L, 'GLF_BINARY_MSB_FIRST'],
-  [0L, 'string', '=', '<!DOCTYPE HTML', 'text/html'],
-  [0L, 'string', '=', '<!doctype html', 'text/html'],
-  [0L, 'string', '=', '<HEAD', 'text/html'],
-  [0L, 'string', '=', '<head', 'text/html'],
-  [0L, 'string', '=', '<TITLE', 'text/html'],
-  [0L, 'string', '=', '<title', 'text/html'],
-  [0L, 'string', '=', '<html', 'text/html'],
-  [0L, 'string', '=', '<HTML', 'text/html'],
-  [0L, 'string', '=', '<!DOCTYPE', 'exported SGML document text'],
-  [0L, 'string', '=', '<!doctype', 'exported SGML document text'],
-  [0L, 'string', '=', '<!SUBDOC', 'exported SGML subdocument text'],
-  [0L, 'string', '=', '<!subdoc', 'exported SGML subdocument text'],
-  [0L, 'string', '=', '<!--', 'exported SGML document text'],
-  [0L, 'string', '=', 'RTSS', 'NetMon capture file'],
-  [0L, 'string', '=', 'TRSNIFF data    \032', 'Sniffer capture file'],
-  [0L, 'string', '=', 'XCP\000', 'NetXRay capture file'],
-  [0L, 'ubelong', '=', 2712847316L, 'tcpdump capture file (big-endian)'],
-  [0L, 'ulelong', '=', 2712847316L, 'tcpdump capture file (little-endian)'],
-  [0L, 'string', '=', '<!SQ DTD>', 'Compiled SGML rules file'],
-  [0L, 'string', '=', '<!SQ A/E>', 'A/E SGML Document binary'],
-  [0L, 'string', '=', '<!SQ STS>', 'A/E SGML binary styles file'],
-  [0L, 'short', '=', 49374L, 'Compiled PSI (v1) data'],
-  [0L, 'short', '=', 49370L, 'Compiled PSI (v2) data'],
-  [0L, 'short', '=', 125252L, 'SoftQuad DESC or font file binary'],
-  [0L, 'string', '=', 'SQ BITMAP1', 'SoftQuad Raster Format text'],
-  [0L, 'string', '=', 'X SoftQuad', 'troff Context intermediate'],
-  [0L, 'belong&077777777', '=', 600413L, 'sparc demand paged'],
-  [0L, 'belong&077777777', '=', 600410L, 'sparc pure'],
-  [0L, 'belong&077777777', '=', 600407L, 'sparc'],
-  [0L, 'belong&077777777', '=', 400413L, 'mc68020 demand paged'],
-  [0L, 'belong&077777777', '=', 400410L, 'mc68020 pure'],
-  [0L, 'belong&077777777', '=', 400407L, 'mc68020'],
-  [0L, 'belong&077777777', '=', 200413L, 'mc68010 demand paged'],
-  [0L, 'belong&077777777', '=', 200410L, 'mc68010 pure'],
-  [0L, 'belong&077777777', '=', 200407L, 'mc68010'],
-  [0L, 'belong', '=', 407L, 'old sun-2 executable'],
-  [0L, 'belong', '=', 410L, 'old sun-2 pure executable'],
-  [0L, 'belong', '=', 413L, 'old sun-2 demand paged executable'],
-  [0L, 'belong', '=', 525398L, 'SunOS core file'],
-  [0L, 'long', '=', 4197695630L, 'SunPC 4.0 Hard Disk'],
-  [0L, 'string', '=', '#SUNPC_CONFIG', 'SunPC 4.0 Properties Values'],
-  [0L, 'string', '=', 'snoop', 'Snoop capture file'],
-  [36L, 'string', '=', 'acsp', 'Kodak Color Management System, ICC Profile'],
-  [0L, 'string', '=', '#!teapot\012xdr', 'teapot work sheet (XDR format)'],
-  [0L, 'string', '=', '\032\001', 'Compiled terminfo entry'],
-  [0L, 'short', '=', 433L, 'Curses screen image'],
-  [0L, 'short', '=', 434L, 'Curses screen image'],
-  [0L, 'string', '=', '\367\002', 'TeX DVI file'],
-  [0L, 'string', '=', '\367\203', 'font/x-tex'],
-  [0L, 'string', '=', '\367Y', 'font/x-tex'],
-  [0L, 'string', '=', '\367\312', 'font/x-tex'],
-  [0L, 'string', '=', 'This is TeX,', 'TeX transcript text'],
-  [0L, 'string', '=', 'This is METAFONT,', 'METAFONT transcript text'],
-  [2L, 'string', '=', '\000\021', 'font/x-tex-tfm'],
-  [2L, 'string', '=', '\000\022', 'font/x-tex-tfm'],
-  [0L, 'string', '=', '\\\\input\\', 'texinfo Texinfo source text'],
-  [0L, 'string', '=', 'This is Info file', 'GNU Info text'],
-  [0L, 'string', '=', '\\\\input', 'TeX document text'],
-  [0L, 'string', '=', '\\\\section', 'LaTeX document text'],
-  [0L, 'string', '=', '\\\\setlength', 'LaTeX document text'],
-  [0L, 'string', '=', '\\\\documentstyle', 'LaTeX document text'],
-  [0L, 'string', '=', '\\\\chapter', 'LaTeX document text'],
-  [0L, 'string', '=', '\\\\documentclass', 'LaTeX 2e document text'],
-  [0L, 'string', '=', '\\\\relax', 'LaTeX auxiliary file'],
-  [0L, 'string', '=', '\\\\contentsline', 'LaTeX table of contents'],
-  [0L, 'string', '=', '\\\\indexentry', 'LaTeX raw index file'],
-  [0L, 'string', '=', '\\\\begin{theindex}', 'LaTeX sorted index'],
-  [0L, 'string', '=', '\\\\glossaryentry', 'LaTeX raw glossary'],
-  [0L, 'string', '=', '\\\\begin{theglossary}', 'LaTeX sorted glossary'],
-  [0L, 'string', '=', 'This is makeindex', 'Makeindex log file'],
-  [0L, 'string', '=', '**TI82**', 'TI-82 Graphing Calculator'],
-  [0L, 'string', '=', '**TI83**', 'TI-83 Graphing Calculator'],
-  [0L, 'string', '=', '**TI85**', 'TI-85 Graphing Calculator'],
-  [0L, 'string', '=', '**TI92**', 'TI-92 Graphing Calculator'],
-  [0L, 'string', '=', '**TI80**', 'TI-80 Graphing Calculator File.'],
-  [0L, 'string', '=', '**TI81**', 'TI-81 Graphing Calculator File.'],
-  [0L, 'string', '=', 'TZif', 'timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000', 'old timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000', 'old timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\003\000', 'old timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004\000', 'old timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\005\000', 'old timezone data'],
-  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\000', 'old timezone data'],
-  [0L, 'string', '=', '.\\\\"', 'troff or preprocessor input text'],
-  [0L, 'string', '=', '\'\\\\"', 'troff or preprocessor input text'],
-  [0L, 'string', '=', '\'.\\\\"', 'troff or preprocessor input text'],
-  [0L, 'string', '=', '\\\\"', 'troff or preprocessor input text'],
-  [0L, 'string', '=', 'x T', 'ditroff text'],
-  [0L, 'string', '=', '@\357', 'very old (C/A/T) troff output data'],
-  [0L, 'string', '=', 'Interpress/Xerox', 'Xerox InterPress data'],
-  [0L, 'short', '=', 263L, 'unknown machine executable'],
-  [0L, 'short', '=', 264L, 'unknown pure executable'],
-  [0L, 'short', '=', 265L, 'PDP-11 separate I&D'],
-  [0L, 'short', '=', 267L, 'unknown pure executable'],
-  [0L, 'long', '=', 268L, 'unknown demand paged pure executable'],
-  [0L, 'long', '=', 269L, 'unknown demand paged pure executable'],
-  [0L, 'long', '=', 270L, 'unknown readable demand paged pure executable'],
-  [0L, 'string', '=', 'begin uuencoded', 'or xxencoded text'],
-  [0L, 'string', '=', 'xbtoa Begin', "btoa'd text"],
-  [0L, 'string', '=', '$\012ship', "ship'd binary text"],
-  [0L, 'string', '=', 'Decode the following with bdeco', 'bencoded News text'],
-  [11L, 'string', '=', 'must be converted with BinHex', 'BinHex binary text'],
-  [0L, 'short', '=', 610L, 'Perkin-Elmer executable'],
-  [0L, 'beshort', '=', 572L, 'amd 29k coff noprebar executable'],
-  [0L, 'beshort', '=', 1572L, 'amd 29k coff prebar executable'],
-  [0L, 'beshort', '=', 160007L, 'amd 29k coff archive'],
-  [6L, 'beshort', '=', 407L, 'unicos (cray) executable'],
-  [596L, 'string', '=', 'X\337\377\377', 'Ultrix core file'],
-  [0L, 'string', '=', 'Joy!peffpwpc', 'header for PowerPC PEF executable'],
-  [0L, 'lelong', '=', 101557L, 'VAX single precision APL workspace'],
-  [0L, 'lelong', '=', 101556L, 'VAX double precision APL workspace'],
-  [0L, 'lelong', '=', 407L, 'VAX executable'],
-  [0L, 'lelong', '=', 410L, 'VAX pure executable'],
-  [0L, 'lelong', '=', 413L, 'VAX demand paged pure executable'],
-  [0L, 'leshort', '=', 570L, 'VAX COFF executable'],
-  [0L, 'leshort', '=', 575L, 'VAX COFF pure executable'],
-  [0L, 'string', '=', 'LBLSIZE=', 'VICAR image data'],
-  [43L, 'string', '=', 'SFDU_LABEL', 'VICAR label file'],
-  [0L, 'short', '=', 21845L, 'VISX image file'],
-  [0L, 'string', '=', '\260\0000\000', 'VMS VAX executable'],
-  [0L, 'belong', '=', 50331648L, 'VMS Alpha executable'],
-  [1L, 'string', '=', 'WPC', '(Corel/WP)'],
-  [0L, 'string', '=', 'core', 'core file (Xenix)'],
-  [0L, 'byte', '=', 128L, '8086 relocatable (Microsoft)'],
-  [0L, 'leshort', '=', 65381L, 'x.out'],
-  [0L, 'leshort', '=', 518L, 'Microsoft a.out'],
-  [0L, 'leshort', '=', 320L, 'old Microsoft 8086 x.out'],
-  [0L, 'lelong', '=', 518L, 'b.out'],
-  [0L, 'leshort', '=', 1408L, 'XENIX 8086 relocatable or 80286 small model'],
-  [0L, 'long', '=', 59399L, 'object file (z8000 a.out)'],
-  [0L, 'long', '=', 59400L, 'pure object file (z8000 a.out)'],
-  [0L, 'long', '=', 59401L, 'separate object file (z8000 a.out)'],
-  [0L, 'long', '=', 59397L, 'overlay object file (z8000 a.out)'],
-  [0L, 'string', '=', 'ZyXEL\002', 'ZyXEL voice data'],
-]
-
-magicNumbers = []
-
-def strToNum(n):
-  val = 0
-  col = long(1)
-  if n[:1] == 'x': n = '0' + n
-  if n[:2] == '0x':
-    # hex
-    n = string.lower(n[2:])
-    while len(n) > 0:
-      l = n[len(n) - 1]
-      val = val + string.hexdigits.index(l) * col
-      col = col * 16
-      n = n[:len(n)-1]
-  elif n[0] == '\\':
-    # octal
-    n = n[1:]
-    while len(n) > 0:
-      l = n[len(n) - 1]
-      if ord(l) < 48 or ord(l) > 57: break
-      val = val + int(l) * col
-      col = col * 8
-      n = n[:len(n)-1]
-  else:
-    val = string.atol(n)
-  return val
-       
-def unescape(s):
-  # replace string escape sequences
-  while 1:
-    m = re.search(r'\\', s)
-    if not m: break
-    x = m.start()+1
-    if m.end() == len(s): 
-      # escaped space at end
-      s = s[:len(s)-1] + ' '
-    elif s[x:x+2] == '0x':
-      # hex ascii value
-      c = chr(strToNum(s[x:x+4]))
-      s = s[:x-1] + c + s[x+4:]
-    elif s[m.start()+1] == 'x':
-      # hex ascii value
-      c = chr(strToNum(s[x:x+3]))
-      s = s[:x-1] + c + s[x+3:]
-    elif ord(s[x]) > 47 and ord(s[x]) < 58:
-      # octal ascii value
-      end = x
-      while (ord(s[end]) > 47 and ord(s[end]) < 58):
-        end = end + 1
-        if end > len(s) - 1: break
-      c = chr(strToNum(s[x-1:end]))
-      s = s[:x-1] + c + s[end:]
-    elif s[x] == 'n':
-      # newline
-      s = s[:x-1] + '\n' + s[x+1:]
-    else:
-      break
-  return s
-
-class magicTest:
-  def __init__(self, offset, t, op, value, msg, mask = None):
-    if t.count('&') > 0:
-      mask = strToNum(t[t.index('&')+1:])  
-      t = t[:t.index('&')]
-    if type(offset) == type('a'):
-      self.offset = strToNum(offset)
-    else:
-      self.offset = offset
-    self.type = t
-    self.msg = msg
-    self.subTests = []
-    self.op = op
-    self.mask = mask
-    self.value = value
-      
-
-  def test(self, data):
-    if self.mask:
-      data = data & self.mask
-    if self.op == '=': 
-      if self.value == data: return self.msg
-    elif self.op ==  '<':
-      pass
-    elif self.op ==  '>':
-      pass
-    elif self.op ==  '&':
-      pass
-    elif self.op ==  '^':
-      pass
-    return None
-
-  def compare(self, data):
-    #print str([self.type, self.value, self.msg])
-    try: 
-      if self.type == 'string':
-        c = ''; s = ''
-        for i in range(0, len(self.value)+1):
-          if i + self.offset > len(data) - 1: break
-          s = s + c
-          [c] = struct.unpack('c', data[self.offset + i])
-        data = s
-      elif self.type == 'short':
-        [data] = struct.unpack('h', data[self.offset : self.offset + 2])
-      elif self.type == 'leshort':
-        [data] = struct.unpack('<h', data[self.offset : self.offset + 2])
-      elif self.type == 'beshort':
-        [data] = struct.unpack('>H', data[self.offset : self.offset + 2])
-      elif self.type == 'long':
-        [data] = struct.unpack('l', data[self.offset : self.offset + 4])
-      elif self.type == 'lelong':
-        [data] = struct.unpack('<l', data[self.offset : self.offset + 4])
-      elif self.type == 'belong':
-        [data] = struct.unpack('>l', data[self.offset : self.offset + 4])
-      else:
-        #print 'UNKNOWN TYPE: ' + self.type
-        pass
-    except:
-      return None
-  
-#    print str([self.msg, self.value, data])
-    return self.test(data)
-    
-
-def load(file):
-  global magicNumbers
-  lines = open(file).readlines()
-  last = { 0: None }
-  for line in lines:
-    if re.match(r'\s*#', line):
-      # comment
-      continue
-    else:
-      # split up by space delimiters, and remove trailing space
-      line = string.rstrip(line)
-      line = re.split(r'\s*', line)
-      if len(line) < 3:
-        # bad line
-        continue
-      offset = line[0]
-      type = line[1]
-      value = line[2]
-      level = 0
-      while offset[0] == '>':
-        # count the level of the type
-        level = level + 1
-        offset = offset[1:]
-      l = magicNumbers
-      if level > 0:
-        l = last[level - 1].subTests
-      if offset[0] == '(':
-        # don't handle indirect offsets just yet
-        print 'SKIPPING ' + string.join(list(line[3:]))
-        pass
-      elif offset[0] == '&':
-        # don't handle relative offsets just yet
-        print 'SKIPPING ' + string.join(list(line[3:]))
-        pass
-      else:
-        operands = ['=', '<', '>', '&']
-        if operands.count(value[0]) > 0:
-          # a comparison operator is specified
-          op = value[0] 
-          value = value[1:]
-        else:
-          print str([value, operands])
-          if len(value) >1 and value[0] == '\\' and operands.count(value[1]) >0:
-            # literal value that collides with operands is escaped
-            value = value[1:]
-          op = '='
-
-        mask = None
-        if type == 'string':
-          while 1:
-            value = unescape(value)
-            if value[len(value)-1] == ' ' and len(line) > 3:
-              # last value was an escaped space, join
-              value = value + line[3]
-              del line[3]
-            else:
-              break
-        else:
-          if value.count('&') != 0:
-            mask = value[(value.index('&') + 1):]
-            print 'MASK: ' + mask
-            value = value[:(value.index('&')+1)]
-          try: value = strToNum(value)
-          except: continue
-          msg = string.join(list(line[3:]))
-        new = magicTest(offset, type, op, value, msg, mask)
-        last[level] = new
-        l.append(new)
-
-def whatis(data):
-  for test in magicNumbers:
-     m = test.compare(data)
-     if m: return m
-  # no matching, magic number. is it binary or text?
-  for c in data:
-    if ord(c) > 128:
-      return 'data'
-  # its ASCII, now do text tests
-  if string.find('The', data, 0, 8192) > -1:
-    return 'English text'
-  if string.find('def', data, 0, 8192) > -1:
-    return 'Python Source'
-  return 'ASCII text'
-      
-    
-def file(file):
-  try:
-    return whatis(open(file, 'r').read(8192))
-  except Exception, e:
-    if str(e) == '[Errno 21] Is a directory':
-      return 'directory'
-    else:
-      raise e
-  
-
-#### BUILD DATA ####
-#load('mime-magic')
-#f = open('out', 'w')
-#for m in magicNumbers:
-#  f.write(str([m.offset, m.type, m.op, m.value, m.msg]) + ',\n')
-#f.close
-
-import sys
-for m in magic:
-  magicNumbers.append(magicTest(m[0], m[1], m[2], m[3], m[4]))
-
-if __name__ == '__main__':
-  import sys
-  for arg in sys.argv[1:]:
-    msg = file(arg)
-    if msg:
-      print arg + ': ' + msg
-    else:
-      print arg + ': unknown'
diff --git a/DAVServer/mysqlauth.py b/DAVServer/mysqlauth.py
index da6249c..ab2d7d4 100644
--- a/DAVServer/mysqlauth.py
+++ b/DAVServer/mysqlauth.py
@@ -1,3 +1,20 @@
+#Copyright (c) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
+
 from fileauth import DAVAuthHandler
 
 class MySQLAuthHandler(DAVAuthHandler):
@@ -18,7 +35,7 @@ class MySQLAuthHandler(DAVAuthHandler):
 
         qry="select * from %s.Users where User='%s' and Pass='%s'"%(Mysql.dbtable,user,pw)
         Auth=DB.execute(qry)
-        
+
         if len(Auth) == 1:
             can_write=Auth[0][3]
             if not can_write and not command in nowrite:
@@ -30,7 +47,7 @@ class MySQLAuthHandler(DAVAuthHandler):
         else:
             self._log('Authentication failed for user %s' % user)
             return 0
-            
+
         self._log('Authentication failed for user %s' % user)
         return 0
 
diff --git a/DAVServer/server.py b/DAVServer/server.py
index b070a5c..1fbe47e 100755
--- a/DAVServer/server.py
+++ b/DAVServer/server.py
@@ -1,29 +1,36 @@
 #!/usr/bin/env python
+#Copyright (c) 1999-2005 Christian Scholz (cs at comlounge.net)
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Library General Public
+#License as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later version.
+#
+#This library 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
+#Library General Public License for more details.
+#
+#You should have received a copy of the GNU Library General Public
+#License along with this library; if not, write to the Free
+#Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+#MA 02111-1307, USA
 
 """
 Python WebDAV Server.
-Copyright (C) 1999-2005 Christian Scholz (cs at comlounge.net)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 This is an example implementation of a DAVserver using the DAV package.
 
 """
 
 import getopt, sys, os
-import BaseHTTPServer
+import logging
+
+logging.basicConfig(level=logging.WARNING)
+log = logging.getLogger('pywebdav')
+
+from BaseHTTPServer import HTTPServer
+from SocketServer import ThreadingMixIn
 
 try:
     import DAV
@@ -41,6 +48,15 @@ from fshandler import FilesystemHandler
 from daemonize import startstop
 from DAV.INI_Parse import Configuration
 
+LEVELS = {'debug': logging.DEBUG,
+          'info': logging.INFO,
+          'warning': logging.WARNING,
+          'error': logging.ERROR,
+          'critical': logging.CRITICAL}
+
+class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
+    """Handle requests in a separate thread."""
+
 def runserver(
          port = 8008, host='localhost',
          directory='/tmp',
@@ -49,26 +65,26 @@ def runserver(
          user = '',
          password = '',
          handler = DAVAuthHandler,
-         server = BaseHTTPServer.HTTPServer):
+         server = ThreadedHTTPServer):
 
     directory = directory.strip()
     directory = directory.rstrip('/')
     host = host.strip()
-   
+
     if not os.path.isdir(directory):
-        print >>sys.stderr, '>> ERROR: %s is not a valid directory!' % directory
+        log.error('%s is not a valid directory!' % directory)
         return sys.exit(233)
-    
+
     # basic checks against wrong hosts
     if host.find('/') != -1 or host.find(':') != -1:
-        print >>sys.stderr, '>> ERROR: Malformed host %s' % host
+        log.error('Malformed host %s' % host)
         return sys.exit(233)
 
     # no root directory
     if directory == '/':
-        print >>sys.stderr, '>> ERROR: Root directory not allowed!'
+        log.error('Root directory not allowed!')
         sys.exit(233)
-        
+
     # dispatch directory and host to the filesystem handler
     # This handler is responsible from where to take the data
     handler.IFACE_CLASS = FilesystemHandler(directory, 'http://%s:%s/' % (host, port), verbose )
@@ -76,32 +92,27 @@ def runserver(
     # put some extra vars
     handler.verbose = verbose
     if noauth:
-        print >>sys.stderr, '>> ATTENTION: Authentication disabled!'
+        log.warning('Authentication disabled!')
         handler.DO_AUTH = False
 
-    print >>sys.stderr, '>> Serving data from %s' % directory
+    log.info('Serving data from %s' % directory)
 
-    if handler._config.DAV.lockemulation is False:
-        print >>sys.stderr, '>> Deactivated LOCK, UNLOCK (WebDAV level 2) support'
+    if handler._config.DAV.getboolean('lockemulation') is False:
+        log.info('Deactivated LOCK, UNLOCK (WebDAV level 2) support')
 
     handler.IFACE_CLASS.mimecheck = True
-    if handler._config.DAV.mimecheck is False:
+    if handler._config.DAV.getboolean('mimecheck') is False:
         handler.IFACE_CLASS.mimecheck = False
-        print >>sys.stderr, '>> Disabled mimetype sniffing (All files will have type application/octet-stream)'
-   
+        log.info('Disabled mimetype sniffing (All files will have type application/octet-stream)')
+
     # initialize server on specified port
     runner = server( (host, port), handler )
-    print >>sys.stderr, '>> Listening on %s (%i)' % (host, port)
-
-    if verbose:
-        print >>sys.stderr, '>> Verbose mode ON'
-
-    print ''
+    print('Listening on %s (%i)' % (host, port))
 
     try:
         runner.serve_forever()
     except KeyboardInterrupt:
-        print >>sys.stderr, '\n>> Killed by user'
+        log.info('Killed by user')
 
 usage = """PyWebDAV server (version %s)
 Standalone WebDAV server
@@ -144,10 +155,12 @@ Parameters:
                         stop    - Stop daemon
                         restart - Restart complete server
                         status  - Returns status of server
-                        
+
     -v, --verbose   Be verbose
+    -l, --loglevel  Select the log level : DEBUG, INFO, WARNING, ERROR, CRITICAL
+                    Default is WARNING
     -h, --help      Show this screen
-    
+
 Please send bug reports and feature requests to %s
 """ % (__version__, __author__)
 
@@ -157,6 +170,9 @@ def setupDummyConfig(**kw):
         def __init__(self, **kw):
             self.__dict__.update(**kw)
 
+        def getboolean(self, name):
+            return (str(getattr(self, name, 0)) in ('1', "yes", "true", "on", "True"))
+
     class DummyConfig:
         DAV = DummyConfigDAV(**kw)
 
@@ -177,17 +193,19 @@ def run():
     lockemulation = True
     configfile = ''
     mimecheck = True
-    
+    loglevel = 'warning'
+
     # parse commandline
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:M', 
-                ['host=', 'port=', 'directory=', 'user=', 'password=','daemon=', 'noauth', 'help', 'verbose', 
-                 'mysql', 'icounter=', 'config=', 'lockemu', 'nomime'])
+        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:Ml:',
+                ['host=', 'port=', 'directory=', 'user=', 'password=',
+                 'daemon=', 'noauth', 'help', 'verbose', 'mysql', 
+                 'icounter=', 'config=', 'lockemu', 'nomime', 'loglevel'])
     except getopt.GetoptError, e:
         print usage
         print '>>>> ERROR: %s' % str(e)
         sys.exit(2)
-    
+
     for o,a in opts:
         if o in ['-i', '--icounter']:
             counter = int(str(a).strip())
@@ -216,10 +234,13 @@ def run():
         if o in ['-v', '--verbose']:
             verbose = True
 
+        if o in ['-l', '--loglevel']:
+            loglevel = a.lower()
+
         if o in ['-h', '--help']:
             print usage
             sys.exit(2)
-    
+
         if o in ['-n', '--noauth']:
             noauth = True
 
@@ -233,13 +254,20 @@ def run():
             daemonize = True
             daemonaction = a
 
+    chunked_http_response = 1
+
+    # This feature are disabled because they are unstable
+    http_request_use_iterator = 0
+    http_response_use_iterator = 0
+
     conf = None
     if configfile != '':
-        print >>sys.stderr, 'Reading configuration from %s' % configfile
+        log.info('Reading configuration from %s' % configfile)
         conf = Configuration(configfile)
 
         dv = conf.DAV
         verbose = bool(int(dv.verbose))
+        loglevel = dv.get('loglevel', loglevel).lower()
         directory = dv.directory
         port = dv.port
         host = dv.host
@@ -253,6 +281,15 @@ def run():
         lockemulation = dv.lockemulation
         mimecheck = dv.mimecheck
 
+        if 'chunked_http_response' not in dv:
+            dv.set('chunked_http_response', chunked_http_response)
+
+        if 'http_request_use_iterator' not in dv:
+            dv.set('http_request_use_iterator', http_request_use_iterator)
+
+        if 'http_response_use_iterator' not in dv:
+            dv.set('http_response_use_iterator', http_response_use_iterator)
+
     else:
 
         _dc = { 'verbose' : verbose,
@@ -266,18 +303,27 @@ def run():
                 'daemonaction' : daemonaction,
                 'counter' : counter,
                 'lockemulation' : lockemulation,
-                'mimecheck' : mimecheck}
+                'mimecheck' : mimecheck,
+                'chunked_http_response': chunked_http_response,
+                'http_request_use_iterator': http_request_use_iterator,
+                'http_response_use_iterator': http_response_use_iterator
+                }
 
         conf = setupDummyConfig(**_dc)
 
+    if verbose and (LEVELS[loglevel] > LEVELS['info']):
+        loglevel = 'info'
+
+    logging.getLogger().setLevel(LEVELS[loglevel])
+
     if mysql == True and configfile == '':
-        print >>sys.stderr, '>> ERROR: You can only use MySQL with configuration file!'
+        log.error('You can only use MySQL with configuration file!')
         sys.exit(3)
 
     if daemonaction != 'stop':
-        print >>sys.stderr, 'Starting up PyWebDAV server (version %s)' % __version__
+        log.info('Starting up PyWebDAV server (version %s)' % __version__)
     else:
-        print >>sys.stderr, 'Stopping PyWebDAV server (version %s)' % __version__
+        log.info('Stopping PyWebDAV server (version %s)' % __version__)
 
     if not noauth and daemonaction not in ['status', 'stop']:
         if not user:
@@ -285,19 +331,23 @@ def run():
             print >>sys.stderr, '>> ERROR: No usable parameter specified!'
             print >>sys.stderr, '>> Example: davserver -D /home/files -n'
             sys.exit(3)
-  
+
     if daemonaction == 'status':
-        print >>sys.stdout, 'Checking for state...'
-   
+        log.info('Checking for state...')
+
     if type(port) == type(''):
         port = int(port.strip())
-   
+
+    log.info('chunked_http_response feature %s' % (conf.DAV.getboolean('chunked_http_response') and 'ON' or 'OFF' ))
+    log.info('http_request_use_iterator feature %s' % (conf.DAV.getboolean('http_request_use_iterator') and 'ON' or 'OFF' ))
+    log.info('http_response_use_iterator feature %s' % (conf.DAV.getboolean('http_response_use_iterator') and 'ON' or 'OFF' ))
+ 
     if daemonize:
 
         # check if pid file exists
         if os.path.exists('/tmp/pydav%s.pid' % counter) and daemonaction not in ['status', 'stop']:
-            print >>sys.stderr, \
-                  '>> ERROR: Found another instance! Either use -i to specifiy another instance number or remove /tmp/pydav%s.pid!' % counter
+            log.error(
+                  'Found another instance! Either use -i to specifiy another instance number or remove /tmp/pydav%s.pid!' % counter)
             sys.exit(3)
 
         startstop(stdout='/tmp/pydav%s.log' % counter, 
@@ -305,7 +355,7 @@ def run():
                     pidfile='/tmp/pydav%s.pid' % counter, 
                     startmsg='>> Started PyWebDAV (PID: %s)',
                     action=daemonaction)
-  
+
     # start now
     handler = DAVAuthHandler
     if mysql == True:
@@ -314,7 +364,8 @@ def run():
     # injecting options
     handler._config = conf
 
-    runserver(port, host, directory, verbose, noauth, user, password, handler=handler)
+    runserver(port, host, directory, verbose, noauth, user, password, 
+              handler=handler)
 
 if __name__ == '__main__':
     run()
diff --git a/PKG-INFO b/PKG-INFO
index a24cad4..946c108 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,19 +1,41 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.3
+Version: 0.9.4
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
 Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
-        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
-        Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+        WebDAV library for python.
+        
+        Consists of a *server* that is ready to run
+        Serve and the DAV package that provides WebDAV server(!) functionality.
+        
+        Currently supports
+        
+        * WebDAV level 1
+        * Level 2 (LOCK, UNLOCK)
+        * Experimental iterator support
+        
+        It plays nice with
+        
+        * Mac OS X Finder
+        * Windows Explorer
+        * iCal
+        * cadaver
+        * Nautilus
+        
+        This package does *not* provide client functionality.
+        
+        Installation
+        ============
         
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
-        This package does *not* provide client functionality.
+        Examples
+        ========
         
         Example (using easy_install)::
         
@@ -29,6 +51,242 @@ Description:
         
         For more information: http://code.google.com/p/pywebdav/
         
+        Changes
+        =======
+        
+        
+        0.9.4 (April 15 2010)
+        ---------------------
+        
+        Add somme configuration setting variable to enable/disable iterator and chunk support
+        [Stephane Klein]
+        
+        Removed os.system calls thus fixing issue 32
+        [Simon Pamies]
+        
+        Fixed issue 14
+        [Simon Pamies]
+        
+        Removed magic.py module - replaced with mimetypes module
+        [Simon Pamies]
+        
+        Print User-Agent information in log request.
+        [Stephane Klein]
+        
+        Fix issue 13 : return http 1.0 compatible response (not chunked) when request http version is 1.0
+        [cliff.wells]
+        
+        Enhance logging mechanism
+        [Stephane Klein]
+        
+        Fix issue 15 : I've error when I execute PUT action with Apple Finder client
+        [Stephane Klein]
+        
+        Fix issue 14 : config.ini boolean parameter reading issue
+        [Stephane Klein]
+        
+        0.9.3 (July 2 2009)
+        -------------------
+        
+        Setting WebDAV v2 as default because LOCK and UNLOCK seem
+        to be stable by now. -J parameter is ignored and will go away.
+        [Simon Pamies]
+        
+        Fix for PROPFIND to return *all* properties
+        [Cedric Krier]
+        
+        Fixed do_PUT initialisation
+        [Cedric Krier]
+        
+        Added REPORT support
+        [Cedric Krier]
+        
+        Added support for gzip encoding
+        [Cedric Krier]
+        
+        Fix for wrong --port option
+        [Martin Wendt]
+        
+        Handle paths correctly for Windows related env
+        [Martin Wendt]
+        
+        Included mimetype check for files
+        based on magic.py from Jason Petrone. Included
+        magic.py into this package. All magic.py code
+        (c) 2000 Jason Petrone. Included from
+        http://www.jsnp.net/code/magic.py.
+        [Joerg Friedrich, Simon Pamies]
+        
+        Status check not working when server is running
+        [Joerg Friedrich]
+        
+        Fixed wrong time formatting for Last-Modified
+        and creationdate (must follow RFC 822 and 3339)
+        [Cedric Krier]
+        
+        0.9.2 (May 11 2009)
+        -------------------
+        
+        Fixed COPY, MOVE, DELETE to support locked
+        resources
+        [Simon Pamies]
+        
+        Fixed PROPFIND to return 404 for non existing
+        objects and also reduce property bloat
+        [Simon Pamies]
+        
+        Implemented fully working LOCK and UNLOCK based
+        on in memory lock/token database. Now fully supports
+        cadaver and Mac OS X Finder.
+        [Simon Pamies]
+        
+        Fixed MKCOL answer to 201
+        [Jesus Cea]
+        
+        Fixed MSIE webdav headers
+        [Jesus Cea]
+        
+        Make propfind respect the depth from queries
+        [Cedric Krier]
+        
+        Add ETag in the header of GET. This is needed to implement
+        GroupDAV, CardDAV and CalDAV.
+        [Cedric Krier]
+        
+        Handle the "Expect 100-continue" header
+        [Cedric Krier]
+        
+        Remove debug statements and remove logging
+        [Cedric Krier]
+        
+        Use the Host header in baseuri if set.
+        [Cedric Krier]
+        
+        Adding If-Match on PUT and DELETE
+        [Cedric Krier]
+        
+        0.9.1 (May 4th 2009)
+        --------------------
+        
+        Restructured the structure a bit: Made server package
+        a real python package. Adapted error messages. Prepared
+        egg distribution.
+        [Simon Pamies]
+        
+        Fix for time formatting bug. Thanks to Ian Kallen
+        [Simon Pamies]
+        
+        Small fixes for WebDavServer (status not handled correctly) and
+        propfind (children are returned from a PROPFIND with "Depth: 0")
+        [Kjetil Irbekk]
+        
+        0.8 (Jul 15th 2008)
+        -------------------
+        
+        First try of an implementation of the LOCK and UNLOCK features.
+        Still very incomplete (read: very incomplete) and not working
+        in this version.
+        [Simon Pamies]
+        
+        Some code cleanups to prepare restructuring
+        [Simon Pamies]
+        
+        Port to minidom because PyXML isn't longer maintained
+        [Martin v. Loewis]
+        
+        utils.py: Makes use of DOMImplementation class to create a new xml document
+        Uses dom namespace features to create elements within DAV: namespace
+        [Stephane Bonhomme]
+        
+        davcmd.py: Missing an indent in loop on remove and copy operations on trees, the
+        effect was that only the last object was removed/copied : always leads
+        to a failure when copying collections.
+        [Stephane Bonhomme]
+        
+        propfind.py: missing a return at the end of the createResponse method (case of a
+        propfind without xml body, should act as a allprops).
+        [Stephane Bonhomme]
+        
+        0.7
+        ---
+        
+        Added MySQL auth support brought by Vince Spicer
+        Added INI file support also  introduced by Vince
+        Some minor bugfixes and integration changes.
+        Added instance counter to make multiple instances possible
+        Extended --help text a bit
+        [Simon Pamies]
+        
+        0.6
+        ---
+        
+        Added bugfixes for buggy Mac OS X Finder implementation
+        Finder tries to stat .DS_Store without checking if it exists
+        Cleaned up readme and install files
+        Moved license to extra file
+        Added distutils support
+        Refactored module layout
+        Refactored class and module names
+        Added commandline support
+        Added daemonize support
+        Added logging facilities
+        Added extended arguments
+        
+        some more things I can't remember
+        [Simon Pamies]
+        
+        Changes since 0.5.1
+        -------------------
+        Updated to work with latest 4Suite
+        
+        Changes since 0.5
+        -----------------
+        
+        added constants.py
+        data.py must now return COLLECTION or OBJECT when getting asked for
+        resourcetype. propfind.py will automatically generate the right xml
+        element.
+        <href> now only contains the path
+        changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
+        added DO_AUTH constant to AuthServer.py to control whether authentication
+        should be done or not.
+        added chunked responses in davserver.py
+        One step in order to get a server with keep-alive one day.
+        we now use 4DOM instead if PyDOM
+        the URI in a href is quoted
+        complete rewrite of the PROPFIND stuff:
+        error responses are now generated when a property if not found or not accessible
+        namespace handling is now better. We forget any prefix and create them ourselves later in the response.
+        added superclass iface.py in DAV/ in order to make implementing
+        interface classes easier. See data.py for how to use it.
+        Also note that the way data.py handles things might have changed from
+        the previous release (if you don't like it wait for 1.0!)
+        added functions to iface.py which format creationdate and lastmodified
+        implemented HEAD
+        
+        lots of bugfixes
+        
+        Changes since 0.3
+        -----------------
+        
+        removed hard coded base uri from davserver.py and replaced by
+        a reference to the dataclass. Added this to iface.py where you
+        have to define it in your subclass.
+        added davcmd.py which contains utility functions for copy and move
+        reimplemented DELETE and removed dependencies to pydom. move actual
+        delete method to davcmd.
+        implemented COPY
+        implemented MOVE
+        fixed bugs in errors.py, needs revisiting anyway..
+        URIs are now unquoted in davserver.py before being used
+        paths in data.py are quoted in system calls in order to support
+        blanks in pathnames (e.g. mkdir '%s' )
+        switched to exceptions when catching errors from the interface class
+        added exists() method to data.py
+        added more uri utility functions to utils.py
+        millenium bugfixes ;-)
+        
+        
 Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
 Platform: Unix
 Platform: Windows
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
index a24cad4..946c108 100644
--- a/PyWebDAV.egg-info/PKG-INFO
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -1,19 +1,41 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.3
+Version: 0.9.4
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
 Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
-        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
-        Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+        WebDAV library for python.
+        
+        Consists of a *server* that is ready to run
+        Serve and the DAV package that provides WebDAV server(!) functionality.
+        
+        Currently supports
+        
+        * WebDAV level 1
+        * Level 2 (LOCK, UNLOCK)
+        * Experimental iterator support
+        
+        It plays nice with
+        
+        * Mac OS X Finder
+        * Windows Explorer
+        * iCal
+        * cadaver
+        * Nautilus
+        
+        This package does *not* provide client functionality.
+        
+        Installation
+        ============
         
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
-        This package does *not* provide client functionality.
+        Examples
+        ========
         
         Example (using easy_install)::
         
@@ -29,6 +51,242 @@ Description:
         
         For more information: http://code.google.com/p/pywebdav/
         
+        Changes
+        =======
+        
+        
+        0.9.4 (April 15 2010)
+        ---------------------
+        
+        Add somme configuration setting variable to enable/disable iterator and chunk support
+        [Stephane Klein]
+        
+        Removed os.system calls thus fixing issue 32
+        [Simon Pamies]
+        
+        Fixed issue 14
+        [Simon Pamies]
+        
+        Removed magic.py module - replaced with mimetypes module
+        [Simon Pamies]
+        
+        Print User-Agent information in log request.
+        [Stephane Klein]
+        
+        Fix issue 13 : return http 1.0 compatible response (not chunked) when request http version is 1.0
+        [cliff.wells]
+        
+        Enhance logging mechanism
+        [Stephane Klein]
+        
+        Fix issue 15 : I've error when I execute PUT action with Apple Finder client
+        [Stephane Klein]
+        
+        Fix issue 14 : config.ini boolean parameter reading issue
+        [Stephane Klein]
+        
+        0.9.3 (July 2 2009)
+        -------------------
+        
+        Setting WebDAV v2 as default because LOCK and UNLOCK seem
+        to be stable by now. -J parameter is ignored and will go away.
+        [Simon Pamies]
+        
+        Fix for PROPFIND to return *all* properties
+        [Cedric Krier]
+        
+        Fixed do_PUT initialisation
+        [Cedric Krier]
+        
+        Added REPORT support
+        [Cedric Krier]
+        
+        Added support for gzip encoding
+        [Cedric Krier]
+        
+        Fix for wrong --port option
+        [Martin Wendt]
+        
+        Handle paths correctly for Windows related env
+        [Martin Wendt]
+        
+        Included mimetype check for files
+        based on magic.py from Jason Petrone. Included
+        magic.py into this package. All magic.py code
+        (c) 2000 Jason Petrone. Included from
+        http://www.jsnp.net/code/magic.py.
+        [Joerg Friedrich, Simon Pamies]
+        
+        Status check not working when server is running
+        [Joerg Friedrich]
+        
+        Fixed wrong time formatting for Last-Modified
+        and creationdate (must follow RFC 822 and 3339)
+        [Cedric Krier]
+        
+        0.9.2 (May 11 2009)
+        -------------------
+        
+        Fixed COPY, MOVE, DELETE to support locked
+        resources
+        [Simon Pamies]
+        
+        Fixed PROPFIND to return 404 for non existing
+        objects and also reduce property bloat
+        [Simon Pamies]
+        
+        Implemented fully working LOCK and UNLOCK based
+        on in memory lock/token database. Now fully supports
+        cadaver and Mac OS X Finder.
+        [Simon Pamies]
+        
+        Fixed MKCOL answer to 201
+        [Jesus Cea]
+        
+        Fixed MSIE webdav headers
+        [Jesus Cea]
+        
+        Make propfind respect the depth from queries
+        [Cedric Krier]
+        
+        Add ETag in the header of GET. This is needed to implement
+        GroupDAV, CardDAV and CalDAV.
+        [Cedric Krier]
+        
+        Handle the "Expect 100-continue" header
+        [Cedric Krier]
+        
+        Remove debug statements and remove logging
+        [Cedric Krier]
+        
+        Use the Host header in baseuri if set.
+        [Cedric Krier]
+        
+        Adding If-Match on PUT and DELETE
+        [Cedric Krier]
+        
+        0.9.1 (May 4th 2009)
+        --------------------
+        
+        Restructured the structure a bit: Made server package
+        a real python package. Adapted error messages. Prepared
+        egg distribution.
+        [Simon Pamies]
+        
+        Fix for time formatting bug. Thanks to Ian Kallen
+        [Simon Pamies]
+        
+        Small fixes for WebDavServer (status not handled correctly) and
+        propfind (children are returned from a PROPFIND with "Depth: 0")
+        [Kjetil Irbekk]
+        
+        0.8 (Jul 15th 2008)
+        -------------------
+        
+        First try of an implementation of the LOCK and UNLOCK features.
+        Still very incomplete (read: very incomplete) and not working
+        in this version.
+        [Simon Pamies]
+        
+        Some code cleanups to prepare restructuring
+        [Simon Pamies]
+        
+        Port to minidom because PyXML isn't longer maintained
+        [Martin v. Loewis]
+        
+        utils.py: Makes use of DOMImplementation class to create a new xml document
+        Uses dom namespace features to create elements within DAV: namespace
+        [Stephane Bonhomme]
+        
+        davcmd.py: Missing an indent in loop on remove and copy operations on trees, the
+        effect was that only the last object was removed/copied : always leads
+        to a failure when copying collections.
+        [Stephane Bonhomme]
+        
+        propfind.py: missing a return at the end of the createResponse method (case of a
+        propfind without xml body, should act as a allprops).
+        [Stephane Bonhomme]
+        
+        0.7
+        ---
+        
+        Added MySQL auth support brought by Vince Spicer
+        Added INI file support also  introduced by Vince
+        Some minor bugfixes and integration changes.
+        Added instance counter to make multiple instances possible
+        Extended --help text a bit
+        [Simon Pamies]
+        
+        0.6
+        ---
+        
+        Added bugfixes for buggy Mac OS X Finder implementation
+        Finder tries to stat .DS_Store without checking if it exists
+        Cleaned up readme and install files
+        Moved license to extra file
+        Added distutils support
+        Refactored module layout
+        Refactored class and module names
+        Added commandline support
+        Added daemonize support
+        Added logging facilities
+        Added extended arguments
+        
+        some more things I can't remember
+        [Simon Pamies]
+        
+        Changes since 0.5.1
+        -------------------
+        Updated to work with latest 4Suite
+        
+        Changes since 0.5
+        -----------------
+        
+        added constants.py
+        data.py must now return COLLECTION or OBJECT when getting asked for
+        resourcetype. propfind.py will automatically generate the right xml
+        element.
+        <href> now only contains the path
+        changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
+        added DO_AUTH constant to AuthServer.py to control whether authentication
+        should be done or not.
+        added chunked responses in davserver.py
+        One step in order to get a server with keep-alive one day.
+        we now use 4DOM instead if PyDOM
+        the URI in a href is quoted
+        complete rewrite of the PROPFIND stuff:
+        error responses are now generated when a property if not found or not accessible
+        namespace handling is now better. We forget any prefix and create them ourselves later in the response.
+        added superclass iface.py in DAV/ in order to make implementing
+        interface classes easier. See data.py for how to use it.
+        Also note that the way data.py handles things might have changed from
+        the previous release (if you don't like it wait for 1.0!)
+        added functions to iface.py which format creationdate and lastmodified
+        implemented HEAD
+        
+        lots of bugfixes
+        
+        Changes since 0.3
+        -----------------
+        
+        removed hard coded base uri from davserver.py and replaced by
+        a reference to the dataclass. Added this to iface.py where you
+        have to define it in your subclass.
+        added davcmd.py which contains utility functions for copy and move
+        reimplemented DELETE and removed dependencies to pydom. move actual
+        delete method to davcmd.
+        implemented COPY
+        implemented MOVE
+        fixed bugs in errors.py, needs revisiting anyway..
+        URIs are now unquoted in davserver.py before being used
+        paths in data.py are quoted in system calls in order to support
+        blanks in pathnames (e.g. mkdir '%s' )
+        switched to exceptions when catching errors from the interface class
+        added exists() method to data.py
+        added more uri utility functions to utils.py
+        millenium bugfixes ;-)
+        
+        
 Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
 Platform: Unix
 Platform: Windows
diff --git a/PyWebDAV.egg-info/SOURCES.txt b/PyWebDAV.egg-info/SOURCES.txt
index 75f83ea..e80cab5 100644
--- a/PyWebDAV.egg-info/SOURCES.txt
+++ b/PyWebDAV.egg-info/SOURCES.txt
@@ -2,6 +2,7 @@ MANIFEST.in
 README
 VERSION
 ez_setup.py
+setup.cfg
 setup.py
 DAV/AuthServer.py
 DAV/BufferingHTTPServer.py
@@ -18,7 +19,6 @@ DAV/errors.py
 DAV/iface.py
 DAV/locks.py
 DAV/propfind.py
-DAV/propfind.py.orig
 DAV/report.py
 DAV/status.py
 DAV/utils.py
@@ -27,7 +27,6 @@ DAVServer/config.ini
 DAVServer/daemonize.py
 DAVServer/fileauth.py
 DAVServer/fshandler.py
-DAVServer/magic.py
 DAVServer/mysqlauth.py
 DAVServer/server.py
 PyWebDAV.egg-info/PKG-INFO
@@ -38,6 +37,7 @@ PyWebDAV.egg-info/not-zip-safe
 PyWebDAV.egg-info/top_level.txt
 doc/ARCHITECTURE
 doc/Changes
+doc/Changes.rej
 doc/INSTALL
 doc/LICENSE
 doc/TODO
diff --git a/VERSION b/VERSION
index 965065d..a602fc9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.3
+0.9.4
diff --git a/doc/Changes b/doc/Changes
index 2c53012..85a6769 100644
--- a/doc/Changes
+++ b/doc/Changes
@@ -1,151 +1,183 @@
 
+0.9.4 (April 15 2010)
+---------------------
+  
+Add somme configuration setting variable to enable/disable iterator and chunk support
+[Stephane Klein]
+
+Removed os.system calls thus fixing issue 32
+[Simon Pamies]
+
+Fixed issue 14
+[Simon Pamies]
+
+Removed magic.py module - replaced with mimetypes module
+[Simon Pamies]
+
+Print User-Agent information in log request.
+[Stephane Klein]
+
+Fix issue 13 : return http 1.0 compatible response (not chunked) when request http version is 1.0
+[cliff.wells]
+
+Enhance logging mechanism
+[Stephane Klein]
+
+Fix issue 15 : I've error when I execute PUT action with Apple Finder client
+[Stephane Klein]
+
+Fix issue 14 : config.ini boolean parameter reading issue
+[Stephane Klein]
+
 0.9.3 (July 2 2009)
---
+-------------------
 
-- Setting WebDAV v2 as default because LOCK and UNLOCK seem
-  to be stable by now. -J parameter is ignored and will go away.
-  [Simon Pamies]
+Setting WebDAV v2 as default because LOCK and UNLOCK seem
+to be stable by now. -J parameter is ignored and will go away.
+[Simon Pamies]
 
-- Fix for PROPFIND to return *all* properties
-  [Cedric Krier]
+Fix for PROPFIND to return *all* properties
+[Cedric Krier]
 
-- Fixed do_PUT initialisation
-  [Cedric Krier]
+Fixed do_PUT initialisation
+[Cedric Krier]
 
-- Added REPORT support
-  [Cedric Krier]
+Added REPORT support
+[Cedric Krier]
 
-- Added support for gzip encoding
-  [Cedric Krier]
+Added support for gzip encoding
+[Cedric Krier]
 
-- Fix for wrong --port option
-  [Martin Wendt]
+Fix for wrong --port option
+[Martin Wendt]
 
-- Handle paths correctly for Windows related env
-  [Martin Wendt]
+Handle paths correctly for Windows related env
+[Martin Wendt]
 
-- Included mimetype check for files
-  based on magic.py from Jason Petrone. Included
-  magic.py into this package. All magic.py code
-  (c) 2000 Jason Petrone. Included from
-  http://www.jsnp.net/code/magic.py.
-  [Joerg Friedrich, Simon Pamies]
+Included mimetype check for files
+based on magic.py from Jason Petrone. Included
+magic.py into this package. All magic.py code
+(c) 2000 Jason Petrone. Included from
+http://www.jsnp.net/code/magic.py.
+[Joerg Friedrich, Simon Pamies]
 
-- Status check not working when server is running
-  [Joerg Friedrich]
+Status check not working when server is running
+[Joerg Friedrich]
 
-- Fixed wrong time formatting for Last-Modified
-  and creationdate (must follow RFC 822 and 3339)
-  [Cedric Krier]
+Fixed wrong time formatting for Last-Modified
+and creationdate (must follow RFC 822 and 3339)
+[Cedric Krier]
 
 0.9.2 (May 11 2009)
---
+-------------------
 
-- Fixed COPY, MOVE, DELETE to support locked
-  resources
-  [Simon Pamies]
+Fixed COPY, MOVE, DELETE to support locked
+resources
+[Simon Pamies]
 
-- Fixed PROPFIND to return 404 for non existing
-  objects and also reduce property bloat
-  [Simon Pamies]
+Fixed PROPFIND to return 404 for non existing
+objects and also reduce property bloat
+[Simon Pamies]
 
-- Implemented fully working LOCK and UNLOCK based
-  on in memory lock/token database. Now fully supports
-  cadaver and Mac OS X Finder.
-  [Simon Pamies]
+Implemented fully working LOCK and UNLOCK based
+on in memory lock/token database. Now fully supports
+cadaver and Mac OS X Finder.
+[Simon Pamies]
 
-- Fixed MKCOL answer to 201
-  [Jesus Cea]
+Fixed MKCOL answer to 201
+[Jesus Cea]
 
-- Fixed MSIE webdav headers
-  [Jesus Cea]
+Fixed MSIE webdav headers
+[Jesus Cea]
 
-- Make propfind respect the depth from queries
-  [Cedric Krier]
+Make propfind respect the depth from queries
+[Cedric Krier]
 
-- Add ETag in the header of GET. This is needed to implement 
-  GroupDAV, CardDAV and CalDAV.
-  [Cedric Krier]
+Add ETag in the header of GET. This is needed to implement 
+GroupDAV, CardDAV and CalDAV.
+[Cedric Krier]
 
-- Handle the "Expect 100-continue" header
-  [Cedric Krier]
+Handle the "Expect 100-continue" header
+[Cedric Krier]
 
-- Remove debug statements and remove logging
-  [Cedric Krier]
+Remove debug statements and remove logging
+[Cedric Krier]
 
-- Use the Host header in baseuri if set.
-  [Cedric Krier]
+Use the Host header in baseuri if set.
+[Cedric Krier]
 
-- Adding If-Match on PUT and DELETE
-  [Cedric Krier]
+Adding If-Match on PUT and DELETE
+[Cedric Krier]
 
 0.9.1 (May 4th 2009)
---
+--------------------
 
-- Restructured the structure a bit: Made server package
-  a real python package. Adapted error messages. Prepared
-  egg distribution.
-  [Simon Pamies]
+Restructured the structure a bit: Made server package
+a real python package. Adapted error messages. Prepared
+egg distribution.
+[Simon Pamies]
 
-- Fix for time formatting bug. Thanks to Ian Kallen
-  [Simon Pamies]
+Fix for time formatting bug. Thanks to Ian Kallen
+[Simon Pamies]
 
-- Small fixes for WebDavServer (status not handled correctly) and
-  propfind (children are returned from a PROPFIND with "Depth: 0")
-  [Kjetil Ørbekk]
+Small fixes for WebDavServer (status not handled correctly) and
+propfind (children are returned from a PROPFIND with "Depth: 0")
+[Kjetil Irbekk]
 
 0.8 (Jul 15th 2008)
----
+-------------------
 
-- First try of an implementation of the LOCK and UNLOCK features.
-  Still very incomplete (read: very incomplete) and not working
-  in this version.
-  [Simon Pamies]
+First try of an implementation of the LOCK and UNLOCK features.
+Still very incomplete (read: very incomplete) and not working
+in this version.
+[Simon Pamies]
 
-- Some code cleanups to prepare restructuring
-  [Simon Pamies]
+Some code cleanups to prepare restructuring
+[Simon Pamies]
 
-- Port to minidom because PyXML isn't longer maintained
-  [Martin v. Loewis]
+Port to minidom because PyXML isn't longer maintained
+[Martin v. Loewis]
 
-- utils.py: Makes use of DOMImplementation class to create a new xml document
-  Uses dom namespace features to create elements within DAV: namespace
-  [Stephane Bonhomme]
+utils.py: Makes use of DOMImplementation class to create a new xml document
+Uses dom namespace features to create elements within DAV: namespace
+[Stephane Bonhomme]
 
-- davcmd.py: Missing an indent in loop on remove and copy operations on trees, the
-  effect was that only the last object was removed/copied : always leads
-  to a failure when copying collections.
-  [Stephane Bonhomme]
+davcmd.py: Missing an indent in loop on remove and copy operations on trees, the
+effect was that only the last object was removed/copied : always leads
+to a failure when copying collections.
+[Stephane Bonhomme]
 
-- propfind.py: missing a return at the end of the createResponse method (case of a
-  propfind without xml body, should act as a allprops).
-  [Stephane Bonhomme]
+propfind.py: missing a return at the end of the createResponse method (case of a
+propfind without xml body, should act as a allprops).
+[Stephane Bonhomme]
 
 0.7
 ---
 
-- Added MySQL auth support brought by Vince Spicer
-- Added INI file support also  introduced by Vince
-- Some minor bugfixes and integration changes.
-- Added instance counter to make multiple instances possible
-- Extended --help text a bit
+Added MySQL auth support brought by Vince Spicer
+Added INI file support also  introduced by Vince
+Some minor bugfixes and integration changes.
+Added instance counter to make multiple instances possible
+Extended --help text a bit
+[Simon Pamies]
 
 0.6
 ---
 
-- Added bugfixes for buggy Mac OS X Finder implementation
-  Finder tries to stat .DS_Store without checking if it exists
-- Cleaned up readme and install files
-- Moved license to extra file
-- Added distutils support
-- Refactored module layout
-- Refactored class and module names
-- Added commandline support
-- Added daemonize support
-- Added logging facilities
-- Added extended arguments
-
-- some more things I can't remember (spamsch)
+Added bugfixes for buggy Mac OS X Finder implementation
+Finder tries to stat .DS_Store without checking if it exists
+Cleaned up readme and install files
+Moved license to extra file
+Added distutils support
+Refactored module layout
+Refactored class and module names
+Added commandline support
+Added daemonize support
+Added logging facilities
+Added extended arguments
+
+some more things I can't remember
+[Simon Pamies]
 
 Changes since 0.5.1
 -------------------
@@ -154,49 +186,46 @@ Updated to work with latest 4Suite
 Changes since 0.5
 -----------------
 
-- added constants.py 
-- data.py must now return COLLECTION or OBJECT when getting asked for
-  resourcetype. propfind.py will automatically generate the right xml 
-  element.
-- <href> now only contains the path
-- changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
-- added DO_AUTH constant to AuthServer.py to control whether authentication
-  should be done or not.
-- added chunked responses in davserver.py
-  One step in order to get a server with keep-alive one day.
-- we now use 4DOM instead if PyDOM
-- the URI in a href is quoted
-- complete rewrite of the PROPFIND stuff:
-    - error responses are now generated when a property if not found
-      or not accessible
-    - namespace handling is now better. We forget any prefix and
-      create them ourselves later in the response.
-- added superclass iface.py in DAV/ in order to make implementing
-  interface classes easier. See data.py for how to use it.
-  Also note that the way data.py handles things might have changed from
-  the previous release (if you don't like it wait for 1.0!)
-- added functions to iface.py which format creationdate and lastmodified
-- implemented HEAD
-
-- lots of bugfixes
-
+added constants.py 
+data.py must now return COLLECTION or OBJECT when getting asked for
+resourcetype. propfind.py will automatically generate the right xml 
+element.
+<href> now only contains the path
+changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
+added DO_AUTH constant to AuthServer.py to control whether authentication
+should be done or not.
+added chunked responses in davserver.py
+One step in order to get a server with keep-alive one day.
+we now use 4DOM instead if PyDOM
+the URI in a href is quoted
+complete rewrite of the PROPFIND stuff:
+error responses are now generated when a property if not found or not accessible
+namespace handling is now better. We forget any prefix and create them ourselves later in the response.
+added superclass iface.py in DAV/ in order to make implementing
+interface classes easier. See data.py for how to use it.
+Also note that the way data.py handles things might have changed from
+the previous release (if you don't like it wait for 1.0!)
+added functions to iface.py which format creationdate and lastmodified
+implemented HEAD
+
+lots of bugfixes
 
 Changes since 0.3
 -----------------
 
-- removed hard coded base uri from davserver.py and replaced by
-  a reference to the dataclass. Added this to iface.py where you
-  have to define it in your subclass.
-- added davcmd.py which contains utility functions for copy and move
-- reimplemented DELETE and removed dependencies to pydom. move actual
-  delete method to davcmd.
-- implemented COPY
-- implemented MOVE
-- fixed bugs in errors.py, needs revisiting anyway..
-- URIs are now unquoted in davserver.py before being used
-- paths in data.py are quoted in system calls in order to support
-  blanks in pathnames (e.g. mkdir '%s' )
-- switched to exceptions when catching errors from the interface class
-- added exists() method to data.py
-- added more uri utility functions to utils.py
-- millenium bugfixes ;-)
+removed hard coded base uri from davserver.py and replaced by
+a reference to the dataclass. Added this to iface.py where you
+have to define it in your subclass.
+added davcmd.py which contains utility functions for copy and move
+reimplemented DELETE and removed dependencies to pydom. move actual
+delete method to davcmd.
+implemented COPY
+implemented MOVE
+fixed bugs in errors.py, needs revisiting anyway..
+URIs are now unquoted in davserver.py before being used
+paths in data.py are quoted in system calls in order to support
+blanks in pathnames (e.g. mkdir '%s' )
+switched to exceptions when catching errors from the interface class
+added exists() method to data.py
+added more uri utility functions to utils.py
+millenium bugfixes ;-)
diff --git a/doc/Changes.rej b/doc/Changes.rej
new file mode 100644
index 0000000..6d7fc72
--- /dev/null
+++ b/doc/Changes.rej
@@ -0,0 +1,34 @@
+***************
+*** 1,4 ****
+- - Enhance logging mechanism
+    [Stephane Klein]
+  
+  - Fix issue 15 : I've error when I execute PUT action with Apple Finder client
+--- 1,27 ----
++ - Add somme configuration setting variable to enable/disable iterator and chunk support
++   [Stephane Klein]
++ 
++ - "log_request" is called after action (like Apache and other server), not before action
++   [Stephane Klein]
++ 
++ - Fix issue 23 : PyWebDAV need to use iterator to avoid over memory consuption
++   [Stephane Klein]
++ 
++ - Fix issue 22 : pywebdav need handle "Range" header information 
++                  in do_GET request
++   [Stephane Klein]
++ 
++ - Fix issue 21 : Add thread support
++   [Stephane Klein]
++ 
++ - Print User-Agent information in log request.
++   [Stephane Klein]
++ 
++ - Fix issue 13 : return http 1.0 compatible response (not chunked) when 
++                  request http version is 1.0
++   [cliff.wells]
++ 
++ - Fix issue 18 : Enhance logging mechanism
+    [Stephane Klein]
+  
+  - Fix issue 15 : I've error when I execute PUT action with Apple Finder client
diff --git a/doc/INSTALL b/doc/INSTALL
index 9e167e1..db61802 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -7,7 +7,6 @@ How to install python WebDAV server
       Windows seems to work
       
     + Python >=2.4.x
-    + PyXML
 
 2. Run setup.py
 
diff --git a/doc/LICENSE b/doc/LICENSE
index 40f4b8a..161a3d1 100644
--- a/doc/LICENSE
+++ b/doc/LICENSE
@@ -1,9 +1,8 @@
-
 		  GNU LIBRARY GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
  Copyright (C) 1991 Free Software Foundation, Inc.
-    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -437,7 +436,7 @@ DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
 

-           How to Apply These Terms to Your New Libraries
+     Appendix: How to Apply These Terms to Your New Libraries
 
   If you develop a new library, and you want it to be of the greatest
 possible use to the public, we recommend making it free software that
@@ -465,7 +464,8 @@ convey the exclusion of warranty; and each file should have at least the
 
     You should have received a copy of the GNU Library General Public
     License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, USA
 
 Also add information on how to contact you by electronic and paper mail.
 
@@ -478,3 +478,5 @@ necessary.  Here is a sample; alter the names:
 
   <signature of Ty Coon>, 1 April 1990
   Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/doc/TODO b/doc/TODO
index fa66e2a..b36f8e2 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -6,7 +6,10 @@ GENERAL
 	- News
 	- TODO list
 	- Changes
-	- Name
+    - Name
+
+- use a better solution than DAV/INI_Parse.py [Stephane Klein]
+- create WSGI server version [Stephane Klein]
 
 MOVE
 ----
@@ -34,3 +37,9 @@ PROPPATCH
 to be done (not that important as only DAV props are supported right now which
 you cannot change)
 
+
+TEST
+----
+
+- create some unit test
+- maybe use davclient package to make the tests
diff --git a/setup.py b/setup.py
index f6149c5..6357c73 100644
--- a/setup.py
+++ b/setup.py
@@ -11,14 +11,38 @@ from setuptools import setup
 VERSION = open('VERSION', 'r').read()
 VERSION = VERSION.replace('\n', '')
 
+CHANGES = open('doc/Changes', 'r').read()
+
 DOC = """
-WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
-Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+WebDAV library for python. 
+
+Consists of a *server* that is ready to run
+Serve and the DAV package that provides WebDAV server(!) functionality.
+
+Currently supports 
+
+    * WebDAV level 1
+    * Level 2 (LOCK, UNLOCK)
+    * Experimental iterator support
+
+It plays nice with
+
+    * Mac OS X Finder
+    * Windows Explorer
+    * iCal
+    * cadaver
+    * Nautilus
+
+This package does *not* provide client functionality.
+
+Installation
+============
 
 After installation of this package you will have a new script in you $PYTHON/bin directory called
 *davserver*. This serves as the main entry point to the server.
 
-This package does *not* provide client functionality.
+Examples
+========
 
 Example (using easy_install)::
 
@@ -33,7 +57,12 @@ Example (unpacking file locally)::
     davserver -D /tmp -n
 
 For more information: http://code.google.com/p/pywebdav/
-"""
+
+Changes
+=======
+
+%s
+""" % CHANGES
 
 from distutils.core import setup
 setup(name='PyWebDAV',
commit 2208a4a06c1cea9e4e745341e01b08882429be1b
Author: Daniel Baumann <daniel at debian.org>
Date:   Tue Jul 7 16:30:09 2009 +0200

    Adding upstream version 0.9.3.

diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
index fdfa564..bbc60ec 100644
--- a/DAV/WebDAVServer.py
+++ b/DAV/WebDAVServer.py
@@ -43,6 +43,7 @@ import urllib
 import random
 
 from propfind import PROPFIND
+from report import REPORT
 from delete import DELETE
 from davcopy import COPY
 from davmove import MOVE
@@ -53,6 +54,8 @@ from errors import *
 
 from constants import DAV_VERSION_1, DAV_VERSION_2
 from locks import LockManager
+import gzip
+import StringIO
 
 class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     """Simple DAV request handler with 
@@ -64,6 +67,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     - PROPFIND
     - PROPPATCH
     - MKCOL
+    - REPORT
 
     experimental
     - LOCK
@@ -75,6 +79,7 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     """
 
     server_version = "DAV/" + __version__
+    encode_threshold = 1400 # common MTU
 
     ### utility functions
     def _log(self, message):
@@ -92,6 +97,16 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
             self.send_header(a,v)
         
         if DATA:
+            if 'gzip' in self.headers.get('Accept-Encoding', '').split(',') \
+                    and len(DATA) > self.encode_threshold:
+                buffer = StringIO.StringIO()
+                output = gzip.GzipFile(mode='wb', fileobj=buffer)
+                output.write(DATA)
+                output.close()
+                buffer.seek(0)
+                DATA = buffer.getvalue()
+                self.send_header('Content-Encoding', 'gzip')
+
             self.send_header('Content-Length', len(DATA))
             self.send_header('Content-Type', ctype)
         else:
@@ -110,6 +125,17 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         self.send_header("Connection", "close")
         self.send_header("Transfer-Encoding", "chunked")
         self.send_header('Date', rfc1123_date())
+
+        if 'gzip' in self.headers.get('Accept-Encoding', '').split(',') \
+                and len(DATA) > self.encode_threshold:
+                buffer = StringIO.StringIO()
+                output = gzip.GzipFile(mode='wb', fileobj=buffer)
+                output.write(DATA)
+                output.close()
+                buffer.seek(0)
+                DATA = buffer.getvalue()
+                self.send_header('Content-Encoding', 'gzip')
+
         self.end_headers()
 
         self._append(hex(len(DATA))[2:]+"\r\n")
@@ -231,6 +257,30 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
         self.send_body_chunks(DATA, '207','Multi-Status','Multiple responses')
 
+    def do_REPORT(self):
+        """ Query properties on defined resource. """
+
+        dc = self.IFACE_CLASS
+
+        # read the body containing the xml request
+        # iff there is no body then this is an ALLPROP request
+        body = None
+        if self.headers.has_key('Content-Length'):
+            l = self.headers['Content-Length']
+            body = self.rfile.read(atoi(l))
+
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
+
+        rp = REPORT(uri, dc, self.headers.get('Depth', 'infinity'), body)
+
+        try:
+            DATA = '%s\n' % rp.createResponse()
+        except DAV_Error, (ec,dd):
+            return self.send_status(ec)
+
+        self.send_body_chunks(DATA, '207','Multi-Status','Multiple responses')
+
     def do_MKCOL(self):
         """ create a new collection """
 
@@ -308,6 +358,8 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
 
     def do_PUT(self):
         dc=self.IFACE_CLASS
+        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri=urllib.unquote(uri)
 
         # Handle If-Match
         if self.headers.has_key('If-Match'):
@@ -364,8 +416,6 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
         if self.headers.has_key("Content-Length"):
             l=self.headers['Content-Length']
             body=self.rfile.read(atoi(l))
-        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
-        uri=urllib.unquote(uri)
 
         # locked resources are not allowed to be overwritten
         if self._l_isLocked(uri):
diff --git a/DAV/constants.py b/DAV/constants.py
index 2b1d796..4eb99b9 100644
--- a/DAV/constants.py
+++ b/DAV/constants.py
@@ -14,7 +14,7 @@ RT_PROP=3
 DAV_VERSION_1 = {
         'version' : '1',
         'options' : 
-        'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE'
+        'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, REPORT'
 }
 
 DAV_VERSION_2 = {
diff --git a/DAV/davcopy.py b/DAV/davcopy.py
index d9e819d..726a0f1 100644
--- a/DAV/davcopy.py
+++ b/DAV/davcopy.py
@@ -1,26 +1,3 @@
-#!/usr/bin/env python
-
-"""
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-"""
-
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
diff --git a/DAV/davmove.py b/DAV/davmove.py
index 78c1e64..e0ea082 100644
--- a/DAV/davmove.py
+++ b/DAV/davmove.py
@@ -1,27 +1,3 @@
-#!/usr/bin/env python
-
-"""
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-"""
-
-
 import sys
 import string
 import urlparse
diff --git a/DAV/delete.py b/DAV/delete.py
index 4f3e4eb..15c880d 100644
--- a/DAV/delete.py
+++ b/DAV/delete.py
@@ -1,26 +1,3 @@
-#!/usr/bin/env python
-
-"""
-
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-"""
 import os
 import string
 import urllib
diff --git a/DAV/iface.py b/DAV/iface.py
index 5f3bda7..ad78ce7 100644
--- a/DAV/iface.py
+++ b/DAV/iface.py
@@ -72,7 +72,7 @@ class dav_interface:
             prefix=self.M_NS[ns]
         else:
             raise DAV_NotFound
-        mname=prefix+"_"+propname
+        mname=prefix+"_"+propname.replace('-', '_')
         try:
                 m=getattr(self,mname)
                 r=m(uri)
@@ -132,13 +132,13 @@ class dav_interface:
         """ return the creationdate of a resource """
         d=self.get_creationdate(uri)
         # format it
-        return time.strftime("%Y-%m-%dT%H:%M:%SZ",time.localtime(d))
+        return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(d))
 
     def _get_dav_getlastmodified(self,uri):
         """ return the last modified date of a resource """
         d=self.get_lastmodified(uri)
         # format it
-        return time.strftime("%a, %d %b %Y %H:%M:%S %Z",time.localtime(d))
+        return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(d))
 
 
     ###
diff --git a/DAV/propfind.py b/DAV/propfind.py
index 4c68cd6..da56db6 100644
--- a/DAV/propfind.py
+++ b/DAV/propfind.py
@@ -32,17 +32,17 @@ class PROPFIND:
         self.nsmap={}
         self.proplist={}
         self.default_ns=None
-        self.__dataclass=dataclass
-        self.__depth=str(depth)
-        self.__uri=uri
-        self.__has_body=None    # did we parse a body?
+        self._dataclass=dataclass
+        self._depth=str(depth)
+        self._uri=uri.rstrip('/')
+        self._has_body=None    # did we parse a body?
 
         if dataclass.verbose:
             print >>sys.stderr, 'PROPFIND: Depth is %s, URI is %s' % (depth, uri)
 
         if body:
             self.request_type, self.proplist, self.namespaces = utils.parse_propfind(body)
-            self.__has_body = True
+            self._has_body = True
 
     def createResponse(self):
         """ Create the multistatus response 
@@ -60,7 +60,7 @@ class PROPFIND:
         """
 
         # check if resource exists
-        if not self.__dataclass.exists(self.__uri):
+        if not self._dataclass.exists(self._uri):
             raise DAV_NotFound
 
         df = None
@@ -83,29 +83,37 @@ class PROPFIND:
     def create_propname(self):
         """ create a multistatus response for the prop names """
         
-        dc=self.__dataclass
+        dc=self._dataclass
         # create the document generator
         doc = domimpl.createDocument(None, "multistatus", None)
         ms = doc.documentElement
         ms.setAttribute("xmlns:D", "DAV:")
         ms.tagName = 'D:multistatus'
 
-        if self.__depth=="0":
-            pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames, doc)
+        if self._depth=="0":
+            pnames=dc.get_propnames(self._uri)
+            re=self.mk_propname_response(self._uri,pnames, doc)
             ms.appendChild(re)
 
-        elif self.__depth=="1":
-            pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames, doc)
+        elif self._depth=="1":
+            pnames=dc.get_propnames(self._uri)
+            re=self.mk_propname_response(self._uri,pnames, doc)
             ms.appendChild(re)
         
-            for newuri in dc.get_childs(self.__uri):
+            for newuri in dc.get_childs(self._uri):
                 pnames=dc.get_propnames(newuri)
                 re=self.mk_propname_response(newuri,pnames, doc)
                 ms.appendChild(re)
-
-        # *** depth=="infinity"  
+        elif self._depth=='infinity':
+            uri_list = [self._uri]
+            while uri_list:
+                uri = uri_list.pop()
+                pnames=dc.get_propnames(uri)
+                re=self.mk_propname_response(uri,pnames, doc)
+                ms.appendChild(re)
+                uri_childs = self._dataclass.get_childs(uri)
+                if uri_childs:
+                    uri_list.extend(uri_childs)
 
         return doc.toxml(encoding="utf-8")
 
@@ -113,7 +121,7 @@ class PROPFIND:
         """ return a list of all properties """
         self.proplist={}
         self.namespaces=[]
-        for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
+        for ns,plist in self._dataclass.get_propnames(self._uri).items():
             self.proplist[ns]=plist
             self.namespaces.append(ns)
         
@@ -148,20 +156,30 @@ class PROPFIND:
         ms.setAttribute("xmlns:D", "DAV:")
         ms.tagName = 'D:multistatus'
 
-        if self.__depth=="0":
-            gp,bp=self.get_propvalues(self.__uri)
-            res=self.mk_prop_response(self.__uri,gp,bp,doc)
+        if self._depth=="0":
+            gp,bp=self.get_propvalues(self._uri)
+            res=self.mk_prop_response(self._uri,gp,bp,doc)
             ms.appendChild(res)
         
-        elif self.__depth=="1":
-            gp,bp=self.get_propvalues(self.__uri)
-            res=self.mk_prop_response(self.__uri,gp,bp,doc)
+        elif self._depth=="1":
+            gp,bp=self.get_propvalues(self._uri)
+            res=self.mk_prop_response(self._uri,gp,bp,doc)
             ms.appendChild(res)
         
-            for newuri in self.__dataclass.get_childs(self.__uri):
+            for newuri in self._dataclass.get_childs(self._uri):
                 gp,bp=self.get_propvalues(newuri)
                 res=self.mk_prop_response(newuri,gp,bp,doc)
                 ms.appendChild(res)
+        elif self._depth=='infinity':
+            uri_list = [self._uri]
+            while uri_list:
+                uri = uri_list.pop()
+                gp,bp=self.get_propvalues(uri)
+                res=self.mk_prop_response(uri,gp,bp,doc)
+                ms.appendChild(res)
+                uri_childs = self._dataclass.get_childs(uri)
+                if uri_childs:
+                    uri_list.extend(uri_childs)
 
         return doc.toxml(encoding="utf-8")
 
@@ -240,11 +258,11 @@ class PROPFIND:
                     pe.appendChild(v)
                 else:
                     if p=="resourcetype":
-                        if v=="1":
+                        if v==1:
                             ve=doc.createElement("D:collection")
                             pe.appendChild(ve)
                     else:
-                        ve=doc.createTextNode(str(v))
+                        ve=doc.createTextNode(v)
                         pe.appendChild(ve)
 
                 gp.appendChild(pe)
@@ -294,7 +312,7 @@ class PROPFIND:
         good_props={}
         bad_props={}
 
-        ddc = self.__dataclass
+        ddc = self._dataclass
         for (ns,plist) in self.proplist.items():
             good_props[ns]={}
             bad_props={}
@@ -302,11 +320,7 @@ class PROPFIND:
                 ec = 0
                 try:
                     r=ddc.get_prop(uri,ns,prop)
-
-                    # support for element returns
-                    if hasattr(r, '__class__') and r.__class__.__name__ == 'Element':
-                        good_props[ns][prop]=r
-                    else: good_props[ns][prop]=str(r)
+                    good_props[ns][prop]=r
                 except DAV_Error, error_code:
                     ec=error_code[0]
                 
diff --git a/DAV/report.py b/DAV/report.py
new file mode 100644
index 0000000..a12721d
--- /dev/null
+++ b/DAV/report.py
@@ -0,0 +1,122 @@
+
+from propfind import PROPFIND
+from xml.dom import minidom
+domimpl = minidom.getDOMImplementation()
+from utils import get_parenturi
+
+
+class REPORT(PROPFIND):
+
+    def __init__(self, uri, dataclass, depth, body):
+        PROPFIND.__init__(self, uri, dataclass, depth, body)
+
+        doc = minidom.parseString(body)
+
+        self.filter = doc.documentElement
+
+    def create_propname(self):
+        """ create a multistatus response for the prop names """
+
+        dc=self._dataclass
+        # create the document generator
+        doc = domimpl.createDocument(None, "multistatus", None)
+        ms = doc.documentElement
+        ms.setAttribute("xmlns:D", "DAV:")
+        ms.tagName = 'D:multistatus'
+
+        if self._depth=="0":
+            if self._uri in self._dataclass.get_childs(get_parenturi(self._uri),
+                    self.filter):
+                pnames=dc.get_propnames(self._uri)
+                re=self.mk_propname_response(self._uri,pnames, doc)
+                ms.appendChild(re)
+
+        elif self._depth=="1":
+            if self._uri in self._dataclass.get_childs(get_parenturi(self._uri),
+                    self.filter):
+                pnames=dc.get_propnames(self._uri)
+                re=self.mk_propname_response(self._uri,pnames, doc)
+                ms.appendChild(re)
+
+            for newuri in dc.get_childs(self._uri, self.filter):
+                pnames=dc.get_propnames(newuri)
+                re=self.mk_propname_response(newuri,pnames, doc)
+                ms.appendChild(re)
+        elif self._depth=='infinity':
+            uri_list = [self._uri]
+            while uri_list:
+                uri = uri_list.pop()
+                if uri in self._dataclass.get_childs(get_parenturi(uri),
+                        self.filter):
+                    pnames=dc.get_propnames(uri)
+                    re=self.mk_propname_response(uri,pnames, doc)
+                    ms.appendChild(re)
+                uri_childs = self._dataclass.get_childs(uri)
+                if uri_childs:
+                    uri_list.extend(uri_childs)
+
+        return doc.toxml(encoding="utf-8")
+
+    def create_prop(self):
+        """ handle a <prop> request
+
+        This will
+
+        1. set up the <multistatus>-Framework
+
+        2. read the property values for each URI 
+           (which is dependant on the Depth header)
+           This is done by the get_propvalues() method.
+
+        3. For each URI call the append_result() method
+           to append the actual <result>-Tag to the result
+           document.
+
+        We differ between "good" properties, which have been
+        assigned a value by the interface class and "bad" 
+        properties, which resulted in an error, either 404
+        (Not Found) or 403 (Forbidden).
+
+        """
+
+
+        # create the document generator
+        doc = domimpl.createDocument(None, "multistatus", None)
+        ms = doc.documentElement
+        ms.setAttribute("xmlns:D", "DAV:")
+        ms.tagName = 'D:multistatus'
+
+        if self._depth=="0":
+            if self._uri in self._dataclass.get_childs(get_parenturi(self._uri),
+                    self.filter):
+                gp,bp=self.get_propvalues(self._uri)
+                res=self.mk_prop_response(self._uri,gp,bp,doc)
+                ms.appendChild(res)
+
+        elif self._depth=="1":
+            if self._uri in self._dataclass.get_childs(get_parenturi(self._uri),
+                    self.filter):
+                gp,bp=self.get_propvalues(self._uri)
+                res=self.mk_prop_response(self._uri,gp,bp,doc)
+                ms.appendChild(res)
+
+            for newuri in self._dataclass.get_childs(self._uri, self.filter):
+                gp,bp=self.get_propvalues(newuri)
+                res=self.mk_prop_response(newuri,gp,bp,doc)
+                ms.appendChild(res)
+        elif self._depth=='infinity':
+            uri_list = [self._uri]
+            while uri_list:
+                uri = uri_list.pop()
+                if uri in self._dataclass.get_childs(get_parenturi(uri),
+                        self.filter):
+                    gp,bp=self.get_propvalues(uri)
+                    res=self.mk_prop_response(uri,gp,bp,doc)
+                    ms.appendChild(res)
+                uri_childs = self._dataclass.get_childs(uri)
+                if uri_childs:
+                    uri_list.extend(uri_childs)
+
+        return doc.toxml(encoding="utf-8")
+
+
diff --git a/DAV/utils.py b/DAV/utils.py
index 8150e5b..2aa1961 100755
--- a/DAV/utils.py
+++ b/DAV/utils.py
@@ -10,7 +10,7 @@ from StringIO import StringIO
 from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
 from BaseHTTPServer import BaseHTTPRequestHandler
 
-VERSION = '0.9.2'
+VERSION = '0.9.3'
 AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
 
 def gen_estring(ecode):
@@ -38,17 +38,17 @@ def parse_propfind(xml_doc):
         request_type = RT_PROPNAME
     else:
         request_type = RT_PROP
-        e = doc.getElementsByTagNameNS("DAV:", "prop")
-        for e in e[0].childNodes:
-            if e.nodeType != minidom.Node.ELEMENT_NODE:
-                continue
-            ns = e.namespaceURI
-            ename = e.localName
-            if props.has_key(ns):
-                props[ns].append(ename)
-            else:
-                props[ns]=[ename]
-                namespaces.append(ns)
+        for i in doc.getElementsByTagNameNS("DAV:", "prop"):
+            for e in i.childNodes:
+                if e.nodeType != minidom.Node.ELEMENT_NODE:
+                    continue
+                ns = e.namespaceURI
+                ename = e.localName
+                if props.has_key(ns):
+                    props[ns].append(ename)
+                else:
+                    props[ns]=[ename]
+                    namespaces.append(ns)
 
     return request_type,props,namespaces
 
diff --git a/DAVServer/config.ini b/DAVServer/config.ini
index 47b0d60..f9740b3 100644
--- a/DAVServer/config.ini
+++ b/DAVServer/config.ini
@@ -48,3 +48,9 @@ daemonaction = start
 
 # instance counter
 counter = 0
+
+# mimetypes support
+mimecheck = 1
+
+# webdav level (1 = webdav level 2)
+lockemulation = 1
diff --git a/DAVServer/fshandler.py b/DAVServer/fshandler.py
index df6adaa..8d464fb 100644
--- a/DAVServer/fshandler.py
+++ b/DAVServer/fshandler.py
@@ -11,6 +11,14 @@ from DAV.iface import *
 
 from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
 
+# include magic support to correctly determine mimetypes
+MAGIC_AVAILABLE = False
+try:
+    import magic
+    MAGIC_AVAILABLE = True
+except ImportError:
+    pass
+
 class FilesystemHandler(dav_interface):
     """ 
     Model a filesystem for DAV
@@ -56,13 +64,14 @@ class FilesystemHandler(dav_interface):
         uparts=urlparse.urlparse(uri)
         fileloc=uparts[2][1:]
         filename=os.path.join(self.directory,fileloc)
+        filename=os.path.normpath(filename)
         return filename
 
     def local2uri(self,filename):
         """ map local filename to self.baseuri """
 
-        pnum=len(split(self.directory,"/"))
-        parts=split(filename,"/")[pnum:]
+        pnum=len(split(self.directory.replace("\\","/"),"/"))
+        parts=split(filename.replace("\\","/"),"/")[pnum:]
         sparts="/"+joinfields(parts,"/")
         uri=urlparse.urljoin(self.baseuri,sparts)
         return uri
@@ -122,7 +131,6 @@ class FilesystemHandler(dav_interface):
         raise DAV_NotFound
         
     def _get_dav_displayname(self,uri):
-        #return uri
         raise DAV_Secret    # do not show
 
     def _get_dav_getcontentlength(self,uri):
@@ -161,7 +169,22 @@ class FilesystemHandler(dav_interface):
         path=self.uri2local(uri)
         if os.path.exists(path):
             if os.path.isfile(path):
-                return "application/octet-stream"
+                if MAGIC_AVAILABLE is False \
+                        or self.mimecheck is False:
+                    return 'application/octet-stream'
+                else:
+                    ret = magic.file(path)
+
+                    # for non mimetype related result we
+                    # simply return an appropriate type
+                    if ret.find('/')==-1:
+                        if ret.find('text')>=0:
+                            return 'text/plain'
+                        else:
+                            return 'application/octet-stream'
+                    else:
+                        return ret
+
             elif os.path.isdir(path):
                 return "httpd/unix-directory"
 
diff --git a/DAVServer/magic.py b/DAVServer/magic.py
new file mode 100644
index 0000000..b95951b
--- /dev/null
+++ b/DAVServer/magic.py
@@ -0,0 +1,1118 @@
+#!/usr/bin/env python 
+'''
+magic.py
+ determines a file type by its magic number
+
+ (C)opyright 2000 Jason Petrone <jp_py at jsnp.net>
+ All Rights Reserved
+
+ Command Line Usage: running as `python magic.py file` will print
+                     a description of what 'file' is.
+
+ Module Usage:
+     magic.whatis(data): when passed a string 'data' containing 
+                         binary or text data, a description of
+                         what the data is will be returned.
+
+     magic.file(filename): returns a description of what the file
+                           'filename' contains.
+'''
+
+import re, struct, string
+
+__version__ = '0.1'
+
+magic = [
+  [0L, 'leshort', '=', 1538L, 'application/x-alan-adventure-game'],
+  [0L, 'string', '=', 'TADS', 'application/x-tads-game'],
+  [0L, 'short', '=', 420L, 'application/x-executable-file'],
+  [0L, 'short', '=', 421L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 603L, 'application/x-executable-file'],
+  [0L, 'string', '=', 'Core\001', 'application/x-executable-file'],
+  [0L, 'string', '=', 'AMANDA: TAPESTART DATE', 'application/x-amanda-header'],
+  [0L, 'belong', '=', 1011L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 999L, 'application/x-library-file'],
+  [0L, 'belong', '=', 435L, 'video/mpeg'],
+  [0L, 'belong', '=', 442L, 'video/mpeg'],
+  [0L, 'beshort&0xfff0', '=', 65520L, 'audio/mpeg'],
+  [4L, 'leshort', '=', 44817L, 'video/fli'],
+  [4L, 'leshort', '=', 44818L, 'video/flc'],
+  [0L, 'string', '=', 'MOVI', 'video/x-sgi-movie'],
+  [4L, 'string', '=', 'moov', 'video/quicktime'],
+  [4L, 'string', '=', 'mdat', 'video/quicktime'],
+  [0L, 'long', '=', 100554L, 'application/x-apl-workspace'],
+  [0L, 'string', '=', 'FiLeStArTfIlEsTaRt', 'text/x-apple-binscii'],
+  [0L, 'string', '=', '\012GL', 'application/data'],
+  [0L, 'string', '=', 'v\377', 'application/data'],
+  [0L, 'string', '=', 'NuFile', 'application/data'],
+  [0L, 'string', '=', 'N\365F\351l\345', 'application/data'],
+  [0L, 'belong', '=', 333312L, 'application/data'],
+  [0L, 'belong', '=', 333319L, 'application/data'],
+  [257L, 'string', '=', 'ustar\000', 'application/x-tar'],
+  [257L, 'string', '=', 'ustar  \000', 'application/x-gtar'],
+  [0L, 'short', '=', 70707L, 'application/x-cpio'],
+  [0L, 'short', '=', 143561L, 'application/x-bcpio'],
+  [0L, 'string', '=', '070707', 'application/x-cpio'],
+  [0L, 'string', '=', '070701', 'application/x-cpio'],
+  [0L, 'string', '=', '070702', 'application/x-cpio'],
+  [0L, 'string', '=', '!<arch>\012debian', 'application/x-dpkg'],
+  [0L, 'long', '=', 177555L, 'application/x-ar'],
+  [0L, 'short', '=', 177555L, 'application/data'],
+  [0L, 'long', '=', 177545L, 'application/data'],
+  [0L, 'short', '=', 177545L, 'application/data'],
+  [0L, 'long', '=', 100554L, 'application/x-apl-workspace'],
+  [0L, 'string', '=', '<ar>', 'application/x-ar'],
+  [0L, 'string', '=', '!<arch>\012__________E', 'application/x-ar'],
+  [0L, 'string', '=', '-h-', 'application/data'],
+  [0L, 'string', '=', '!<arch>', 'application/x-ar'],
+  [0L, 'string', '=', '<ar>', 'application/x-ar'],
+  [0L, 'string', '=', '<ar>', 'application/x-ar'],
+  [0L, 'belong', '=', 1711210496L, 'application/x-ar'],
+  [0L, 'belong', '=', 1013019198L, 'application/x-ar'],
+  [0L, 'long', '=', 557605234L, 'application/x-ar'],
+  [0L, 'lelong', '=', 177555L, 'application/data'],
+  [0L, 'leshort', '=', 177555L, 'application/data'],
+  [0L, 'lelong', '=', 177545L, 'application/data'],
+  [0L, 'leshort', '=', 177545L, 'application/data'],
+  [0L, 'lelong', '=', 236525L, 'application/data'],
+  [0L, 'lelong', '=', 236526L, 'application/data'],
+  [0L, 'lelong&0x8080ffff', '=', 2074L, 'application/x-arc'],
+  [0L, 'lelong&0x8080ffff', '=', 2330L, 'application/x-arc'],
+  [0L, 'lelong&0x8080ffff', '=', 538L, 'application/x-arc'],
+  [0L, 'lelong&0x8080ffff', '=', 794L, 'application/x-arc'],
+  [0L, 'lelong&0x8080ffff', '=', 1050L, 'application/x-arc'],
+  [0L, 'lelong&0x8080ffff', '=', 1562L, 'application/x-arc'],
+  [0L, 'string', '=', '\032archive', 'application/data'],
+  [0L, 'leshort', '=', 60000L, 'application/x-arj'],
+  [0L, 'string', '=', 'HPAK', 'application/data'],
+  [0L, 'string', '=', '\351,\001JAM application/data', ''],
+  [2L, 'string', '=', '-lh0-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh1-', 'application/x-lha'],
+  [2L, 'string', '=', '-lz4-', 'application/x-lha'],
+  [2L, 'string', '=', '-lz5-', 'application/x-lha'],
+  [2L, 'string', '=', '-lzs-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh -', 'application/x-lha'],
+  [2L, 'string', '=', '-lhd-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh2-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh3-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh4-', 'application/x-lha'],
+  [2L, 'string', '=', '-lh5-', 'application/x-lha'],
+  [0L, 'string', '=', 'Rar!', 'application/x-rar'],
+  [0L, 'string', '=', 'SQSH', 'application/data'],
+  [0L, 'string', '=', 'UC2\032', 'application/data'],
+  [0L, 'string', '=', 'PK\003\004', 'application/zip'],
+  [20L, 'lelong', '=', 4257523676L, 'application/x-zoo'],
+  [10L, 'string', '=', '# This is a shell archive', 'application/x-shar'],
+  [0L, 'string', '=', '*STA', 'application/data'],
+  [0L, 'string', '=', '2278', 'application/data'],
+  [0L, 'beshort', '=', 560L, 'application/x-executable-file'],
+  [0L, 'beshort', '=', 561L, 'application/x-executable-file'],
+  [0L, 'string', '=', '\000\004\036\212\200', 'application/core'],
+  [0L, 'string', '=', '.snd', 'audio/basic'],
+  [0L, 'lelong', '=', 6583086L, 'audio/basic'],
+  [0L, 'string', '=', 'MThd', 'audio/midi'],
+  [0L, 'string', '=', 'CTMF', 'audio/x-cmf'],
+  [0L, 'string', '=', 'SBI', 'audio/x-sbi'],
+  [0L, 'string', '=', 'Creative Voice File', 'audio/x-voc'],
+  [0L, 'belong', '=', 1314148939L, 'audio/x-multitrack'],
+  [0L, 'string', '=', 'RIFF', 'audio/x-wav'],
+  [0L, 'string', '=', 'EMOD', 'audio/x-emod'],
+  [0L, 'belong', '=', 779248125L, 'audio/x-pn-realaudio'],
+  [0L, 'string', '=', 'MTM', 'audio/x-multitrack'],
+  [0L, 'string', '=', 'if', 'audio/x-669-mod'],
+  [0L, 'string', '=', 'FAR', 'audio/mod'],
+  [0L, 'string', '=', 'MAS_U', 'audio/x-multimate-mod'],
+  [44L, 'string', '=', 'SCRM', 'audio/x-st3-mod'],
+  [0L, 'string', '=', 'GF1PATCH110\000ID#000002\000', 'audio/x-gus-patch'],
+  [0L, 'string', '=', 'GF1PATCH100\000ID#000002\000', 'audio/x-gus-patch'],
+  [0L, 'string', '=', 'JN', 'audio/x-669-mod'],
+  [0L, 'string', '=', 'UN05', 'audio/x-mikmod-uni'],
+  [0L, 'string', '=', 'Extended Module:', 'audio/x-ft2-mod'],
+  [21L, 'string', '=', '!SCREAM!', 'audio/x-st2-mod'],
+  [1080L, 'string', '=', 'M.K.', 'audio/x-protracker-mod'],
+  [1080L, 'string', '=', 'M!K!', 'audio/x-protracker-mod'],
+  [1080L, 'string', '=', 'FLT4', 'audio/x-startracker-mod'],
+  [1080L, 'string', '=', '4CHN', 'audio/x-fasttracker-mod'],
+  [1080L, 'string', '=', '6CHN', 'audio/x-fasttracker-mod'],
+  [1080L, 'string', '=', '8CHN', 'audio/x-fasttracker-mod'],
+  [1080L, 'string', '=', 'CD81', 'audio/x-oktalyzer-mod'],
+  [1080L, 'string', '=', 'OKTA', 'audio/x-oktalyzer-mod'],
+  [1080L, 'string', '=', '16CN', 'audio/x-taketracker-mod'],
+  [1080L, 'string', '=', '32CN', 'audio/x-taketracker-mod'],
+  [0L, 'string', '=', 'TOC', 'audio/x-toc'],
+  [0L, 'short', '=', 3401L, 'application/x-executable-file'],
+  [0L, 'long', '=', 406L, 'application/x-executable-file'],
+  [0L, 'short', '=', 406L, 'application/x-executable-file'],
+  [0L, 'short', '=', 3001L, 'application/x-executable-file'],
+  [0L, 'lelong', '=', 314L, 'application/x-executable-file'],
+  [0L, 'string', '=', '//', 'text/cpp'],
+  [0L, 'string', '=', '\\\\1cw\\', 'application/data'],
+  [0L, 'string', '=', '\\\\1cw', 'application/data'],
+  [0L, 'belong&0xffffff00', '=', 2231440384L, 'application/data'],
+  [0L, 'belong&0xffffff00', '=', 2231487232L, 'application/data'],
+  [0L, 'short', '=', 575L, 'application/x-executable-file'],
+  [0L, 'short', '=', 577L, 'application/x-executable-file'],
+  [4L, 'string', '=', 'pipe', 'application/data'],
+  [4L, 'string', '=', 'prof', 'application/data'],
+  [0L, 'string', '=', ': shell', 'application/data'],
+  [0L, 'string', '=', '#!/bin/sh', 'application/x-sh'],
+  [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'],
+  [0L, 'string', '=', '#! /bin/sh', 'application/x-sh'],
+  [0L, 'string', '=', '#!/bin/csh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /bin/csh', 'application/x-csh'],
+  [0L, 'string', '=', '#!/bin/ksh', 'application/x-ksh'],
+  [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'],
+  [0L, 'string', '=', '#! /bin/ksh', 'application/x-ksh'],
+  [0L, 'string', '=', '#!/bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#!/usr/local/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /usr/local/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#!/usr/local/bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#! /usr/local/bin/tcsh', 'application/x-csh'],
+  [0L, 'string', '=', '#!/usr/local/bin/zsh', 'application/x-zsh'],
+  [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'],
+  [0L, 'string', '=', '#! /usr/local/bin/zsh', 'application/x-zsh'],
+  [0L, 'string', '=', '#!/usr/local/bin/ash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'],
+  [0L, 'string', '=', '#! /usr/local/bin/ash', 'application/x-zsh'],
+  [0L, 'string', '=', '#!/usr/local/bin/ae', 'text/script'],
+  [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'],
+  [0L, 'string', '=', '#! /usr/local/bin/ae', 'text/script'],
+  [0L, 'string', '=', '#!/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/usr/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/usr/local/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/local/bin/nawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/usr/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/usr/local/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/local/bin/gawk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', '#!/usr/bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', '#! /usr/bin/awk', 'application/x-awk'],
+  [0L, 'string', '=', 'BEGIN', 'application/x-awk'],
+  [0L, 'string', '=', '#!/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', 'eval "exec /bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#!/usr/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /usr/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', 'eval "exec /usr/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#!/usr/local/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#! /usr/local/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', 'eval "exec /usr/local/bin/perl', 'application/x-perl'],
+  [0L, 'string', '=', '#!/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /bin/python', 'application/x-python'],
+  [0L, 'string', '=', 'eval "exec /bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#!/usr/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /usr/bin/python', 'application/x-python'],
+  [0L, 'string', '=', 'eval "exec /usr/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#!/usr/local/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#! /usr/local/bin/python', 'application/x-python'],
+  [0L, 'string', '=', 'eval "exec /usr/local/bin/python', 'application/x-python'],
+  [0L, 'string', '=', '#!/usr/bin/env python', 'application/x-python'],
+  [0L, 'string', '=', '#! /usr/bin/env python', 'application/x-python'],
+  [0L, 'string', '=', '#!/bin/rc', 'text/script'],
+  [0L, 'string', '=', '#! /bin/rc', 'text/script'],
+  [0L, 'string', '=', '#! /bin/rc', 'text/script'],
+  [0L, 'string', '=', '#!/bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#!/usr/local/bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /usr/local/bin/bash', 'application/x-sh'],
+  [0L, 'string', '=', '#! /', 'text/script'],
+  [0L, 'string', '=', '#! /', 'text/script'],
+  [0L, 'string', '=', '#!/', 'text/script'],
+  [0L, 'string', '=', '#! text/script', ''],
+  [0L, 'string', '=', '\037\235', 'application/compress'],
+  [0L, 'string', '=', '\037\213', 'application/x-gzip'],
+  [0L, 'string', '=', '\037\036', 'application/data'],
+  [0L, 'short', '=', 17437L, 'application/data'],
+  [0L, 'short', '=', 8191L, 'application/data'],
+  [0L, 'string', '=', '\377\037', 'application/data'],
+  [0L, 'short', '=', 145405L, 'application/data'],
+  [0L, 'string', '=', 'BZh', 'application/x-bzip2'],
+  [0L, 'leshort', '=', 65398L, 'application/data'],
+  [0L, 'leshort', '=', 65142L, 'application/data'],
+  [0L, 'leshort', '=', 64886L, 'application/x-lzh'],
+  [0L, 'string', '=', '\037\237', 'application/data'],
+  [0L, 'string', '=', '\037\236', 'application/data'],
+  [0L, 'string', '=', '\037\240', 'application/data'],
+  [0L, 'string', '=', 'BZ', 'application/x-bzip'],
+  [0L, 'string', '=', '\211LZO\000\015\012\032\012', 'application/data'],
+  [0L, 'belong', '=', 507L, 'application/x-object-file'],
+  [0L, 'belong', '=', 513L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 515L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 517L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 70231L, 'application/core'],
+  [24L, 'belong', '=', 60011L, 'application/data'],
+  [24L, 'belong', '=', 60012L, 'application/data'],
+  [24L, 'belong', '=', 60013L, 'application/data'],
+  [24L, 'belong', '=', 60014L, 'application/data'],
+  [0L, 'belong', '=', 601L, 'application/x-object-file'],
+  [0L, 'belong', '=', 607L, 'application/data'],
+  [0L, 'belong', '=', 324508366L, 'application/x-gdbm'],
+  [0L, 'lelong', '=', 324508366L, 'application/x-gdbm'],
+  [0L, 'string', '=', 'GDBM', 'application/x-gdbm'],
+  [0L, 'belong', '=', 398689L, 'application/x-db'],
+  [0L, 'belong', '=', 340322L, 'application/x-db'],
+  [0L, 'string', '=', '<list>\012<protocol bbn-m', 'application/data'],
+  [0L, 'string', '=', 'diff text/x-patch', ''],
+  [0L, 'string', '=', '*** text/x-patch', ''],
+  [0L, 'string', '=', 'Only in text/x-patch', ''],
+  [0L, 'string', '=', 'Common subdirectories: text/x-patch', ''],
+  [0L, 'string', '=', '!<arch>\012________64E', 'application/data'],
+  [0L, 'leshort', '=', 387L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 392L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 399L, 'application/x-object-file'],
+  [0L, 'string', '=', '\377\377\177', 'application/data'],
+  [0L, 'string', '=', '\377\377|', 'application/data'],
+  [0L, 'string', '=', '\377\377~', 'application/data'],
+  [0L, 'string', '=', '\033c\033', 'application/data'],
+  [0L, 'long', '=', 4553207L, 'image/x11'],
+  [0L, 'string', '=', '!<PDF>!\012', 'application/x-prof'],
+  [0L, 'short', '=', 1281L, 'application/x-locale'],
+  [24L, 'belong', '=', 60012L, 'application/x-dump'],
+  [24L, 'belong', '=', 60011L, 'application/x-dump'],
+  [24L, 'lelong', '=', 60012L, 'application/x-dump'],
+  [24L, 'lelong', '=', 60011L, 'application/x-dump'],
+  [0L, 'string', '=', '\177ELF', 'application/x-executable-file'],
+  [0L, 'short', '=', 340L, 'application/data'],
+  [0L, 'short', '=', 341L, 'application/x-executable-file'],
+  [1080L, 'leshort', '=', 61267L, 'application/x-linux-ext2fs'],
+  [0L, 'string', '=', '\366\366\366\366', 'application/x-pc-floppy'],
+  [774L, 'beshort', '=', 55998L, 'application/data'],
+  [510L, 'leshort', '=', 43605L, 'application/data'],
+  [1040L, 'leshort', '=', 4991L, 'application/x-filesystem'],
+  [1040L, 'leshort', '=', 5007L, 'application/x-filesystem'],
+  [1040L, 'leshort', '=', 9320L, 'application/x-filesystem'],
+  [1040L, 'leshort', '=', 9336L, 'application/x-filesystem'],
+  [0L, 'string', '=', '-rom1fs-\000', 'application/x-filesystem'],
+  [395L, 'string', '=', 'OS/2', 'application/x-bootable'],
+  [0L, 'string', '=', 'FONT', 'font/x-vfont'],
+  [0L, 'short', '=', 436L, 'font/x-vfont'],
+  [0L, 'short', '=', 17001L, 'font/x-vfont'],
+  [0L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'],
+  [6L, 'string', '=', '%!PS-AdobeFont-1.0', 'font/type1'],
+  [0L, 'belong', '=', 4L, 'font/x-snf'],
+  [0L, 'lelong', '=', 4L, 'font/x-snf'],
+  [0L, 'string', '=', 'STARTFONT font/x-bdf', ''],
+  [0L, 'string', '=', '\001fcp', 'font/x-pcf'],
+  [0L, 'string', '=', 'D1.0\015', 'font/x-speedo'],
+  [0L, 'string', '=', 'flf', 'font/x-figlet'],
+  [0L, 'string', '=', 'flc', 'application/x-font'],
+  [0L, 'belong', '=', 335698201L, 'font/x-libgrx'],
+  [0L, 'belong', '=', 4282797902L, 'font/x-dos'],
+  [7L, 'belong', '=', 4540225L, 'font/x-dos'],
+  [7L, 'belong', '=', 5654852L, 'font/x-dos'],
+  [4098L, 'string', '=', 'DOSFONT', 'font/x-dos'],
+  [0L, 'string', '=', '<MakerFile', 'application/x-framemaker'],
+  [0L, 'string', '=', '<MIFFile', 'application/x-framemaker'],
+  [0L, 'string', '=', '<MakerDictionary', 'application/x-framemaker'],
+  [0L, 'string', '=', '<MakerScreenFont', 'font/x-framemaker'],
+  [0L, 'string', '=', '<MML', 'application/x-framemaker'],
+  [0L, 'string', '=', '<BookFile', 'application/x-framemaker'],
+  [0L, 'string', '=', '<Maker', 'application/x-framemaker'],
+  [0L, 'lelong&0377777777', '=', 41400407L, 'application/x-executable-file'],
+  [0L, 'lelong&0377777777', '=', 41400410L, 'application/x-executable-file'],
+  [0L, 'lelong&0377777777', '=', 41400413L, 'application/x-executable-file'],
+  [0L, 'lelong&0377777777', '=', 41400314L, 'application/x-executable-file'],
+  [7L, 'string', '=', '\357\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000', 'application/core'],
+  [0L, 'lelong', '=', 11421044151L, 'application/data'],
+  [0L, 'string', '=', 'GIMP Gradient', 'application/x-gimp-gradient'],
+  [0L, 'string', '=', 'gimp xcf', 'application/x-gimp-image'],
+  [20L, 'string', '=', 'GPAT', 'application/x-gimp-pattern'],
+  [20L, 'string', '=', 'GIMP', 'application/x-gimp-brush'],
+  [0L, 'string', '=', '\336\022\004\225', 'application/x-locale'],
+  [0L, 'string', '=', '\225\004\022\336', 'application/x-locale'],
+  [0L, 'beshort', '=', 627L, 'application/x-executable-file'],
+  [0L, 'beshort', '=', 624L, 'application/x-executable-file'],
+  [0L, 'string', '=', '\000\001\000\000\000', 'font/ttf'],
+  [0L, 'long', '=', 1203604016L, 'application/data'],
+  [0L, 'long', '=', 1702407010L, 'application/data'],
+  [0L, 'long', '=', 1003405017L, 'application/data'],
+  [0L, 'long', '=', 1602007412L, 'application/data'],
+  [0L, 'belong', '=', 34603270L, 'application/x-object-file'],
+  [0L, 'belong', '=', 34603271L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34603272L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34603275L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34603278L, 'application/x-library-file'],
+  [0L, 'belong', '=', 34603277L, 'application/x-library-file'],
+  [0L, 'belong', '=', 34865414L, 'application/x-object-file'],
+  [0L, 'belong', '=', 34865415L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34865416L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34865419L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34865422L, 'application/x-library-file'],
+  [0L, 'belong', '=', 34865421L, 'application/x-object-file'],
+  [0L, 'belong', '=', 34275590L, 'application/x-object-file'],
+  [0L, 'belong', '=', 34275591L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34275592L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34275595L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34275598L, 'application/x-library-file'],
+  [0L, 'belong', '=', 34275597L, 'application/x-library-file'],
+  [0L, 'belong', '=', 557605234L, 'application/x-ar'],
+  [0L, 'long', '=', 34078982L, 'application/x-executable-file'],
+  [0L, 'long', '=', 34078983L, 'application/x-executable-file'],
+  [0L, 'long', '=', 34078984L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34341128L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34341127L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34341131L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34341126L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34210056L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34210055L, 'application/x-executable-file'],
+  [0L, 'belong', '=', 34341134L, 'application/x-library-file'],
+  [0L, 'belong', '=', 34341133L, 'application/x-library-file'],
+  [0L, 'long', '=', 65381L, 'application/x-library-file'],
+  [0L, 'long', '=', 34275173L, 'application/x-library-file'],
+  [0L, 'long', '=', 34406245L, 'application/x-library-file'],
+  [0L, 'long', '=', 34144101L, 'application/x-library-file'],
+  [0L, 'long', '=', 22552998L, 'application/core'],
+  [0L, 'long', '=', 1302851304L, 'font/x-hp-windows'],
+  [0L, 'string', '=', 'Bitmapfile', 'image/unknown'],
+  [0L, 'string', '=', 'IMGfile', 'CIS image/unknown'],
+  [0L, 'long', '=', 34341132L, 'application/x-lisp'],
+  [0L, 'string', '=', 'msgcat01', 'application/x-locale'],
+  [0L, 'string', '=', 'HPHP48-', 'HP48 binary'],
+  [0L, 'string', '=', '%%HP:', 'HP48 text'],
+  [0L, 'beshort', '=', 200L, 'hp200 (68010) BSD'],
+  [0L, 'beshort', '=', 300L, 'hp300 (68020+68881) BSD'],
+  [0L, 'beshort', '=', 537L, '370 XA sysV executable'],
+  [0L, 'beshort', '=', 532L, '370 XA sysV pure executable'],
+  [0L, 'beshort', '=', 54001L, '370 sysV pure executable'],
+  [0L, 'beshort', '=', 55001L, '370 XA sysV pure executable'],
+  [0L, 'beshort', '=', 56401L, '370 sysV executable'],
+  [0L, 'beshort', '=', 57401L, '370 XA sysV executable'],
+  [0L, 'beshort', '=', 531L, 'SVR2 executable (Amdahl-UTS)'],
+  [0L, 'beshort', '=', 534L, 'SVR2 pure executable (Amdahl-UTS)'],
+  [0L, 'beshort', '=', 530L, 'SVR2 pure executable (USS/370)'],
+  [0L, 'beshort', '=', 535L, 'SVR2 executable (USS/370)'],
+  [0L, 'beshort', '=', 479L, 'executable (RISC System/6000 V3.1) or obj module'],
+  [0L, 'beshort', '=', 260L, 'shared library'],
+  [0L, 'beshort', '=', 261L, 'ctab data'],
+  [0L, 'beshort', '=', 65028L, 'structured file'],
+  [0L, 'string', '=', '0xabcdef', 'AIX message catalog'],
+  [0L, 'belong', '=', 505L, 'AIX compiled message catalog'],
+  [0L, 'string', '=', '<aiaff>', 'archive'],
+  [0L, 'string', '=', 'FORM', 'IFF data'],
+  [0L, 'string', '=', 'P1', 'image/x-portable-bitmap'],
+  [0L, 'string', '=', 'P2', 'image/x-portable-graymap'],
+  [0L, 'string', '=', 'P3', 'image/x-portable-pixmap'],
+  [0L, 'string', '=', 'P4', 'image/x-portable-bitmap'],
+  [0L, 'string', '=', 'P5', 'image/x-portable-graymap'],
+  [0L, 'string', '=', 'P6', 'image/x-portable-pixmap'],
+  [0L, 'string', '=', 'IIN1', 'image/tiff'],
+  [0L, 'string', '=', 'MM\000*', 'image/tiff'],
+  [0L, 'string', '=', 'II*\000', 'image/tiff'],
+  [0L, 'string', '=', '\211PNG', 'image/x-png'],
+  [1L, 'string', '=', 'PNG', 'image/x-png'],
+  [0L, 'string', '=', 'GIF8', 'image/gif'],
+  [0L, 'string', '=', '\361\000@\273', 'image/x-cmu-raster'],
+  [0L, 'string', '=', 'id=ImageMagick', 'MIFF image data'],
+  [0L, 'long', '=', 1123028772L, 'Artisan image data'],
+  [0L, 'string', '=', '#FIG', 'FIG image text'],
+  [0L, 'string', '=', 'ARF_BEGARF', 'PHIGS clear text archive'],
+  [0L, 'string', '=', '@(#)SunPHIGS', 'SunPHIGS'],
+  [0L, 'string', '=', 'GKSM', 'GKS Metafile'],
+  [0L, 'string', '=', 'BEGMF', 'clear text Computer Graphics Metafile'],
+  [0L, 'beshort&0xffe0', '=', 32L, 'binary Computer Graphics Metafile'],
+  [0L, 'beshort', '=', 12320L, 'character Computer Graphics Metafile'],
+  [0L, 'string', '=', 'yz', 'MGR bitmap, modern format, 8-bit aligned'],
+  [0L, 'string', '=', 'zz', 'MGR bitmap, old format, 1-bit deep, 16-bit aligned'],
+  [0L, 'string', '=', 'xz', 'MGR bitmap, old format, 1-bit deep, 32-bit aligned'],
+  [0L, 'string', '=', 'yx', 'MGR bitmap, modern format, squeezed'],
+  [0L, 'string', '=', '%bitmap\000', 'FBM image data'],
+  [1L, 'string', '=', 'PC Research, Inc', 'group 3 fax data'],
+  [0L, 'beshort', '=', 65496L, 'image/jpeg'],
+  [0L, 'string', '=', 'hsi1', 'image/x-jpeg-proprietary'],
+  [0L, 'string', '=', 'BM', 'image/x-bmp'],
+  [0L, 'string', '=', 'IC', 'image/x-ico'],
+  [0L, 'string', '=', 'PI', 'PC pointer image data'],
+  [0L, 'string', '=', 'CI', 'PC color icon data'],
+  [0L, 'string', '=', 'CP', 'PC color pointer image data'],
+  [0L, 'string', '=', '/* XPM */', 'X pixmap image text'],
+  [0L, 'leshort', '=', 52306L, 'RLE image data,'],
+  [0L, 'string', '=', 'Imagefile version-', 'iff image data'],
+  [0L, 'belong', '=', 1504078485L, 'x/x-image-sun-raster'],
+  [0L, 'beshort', '=', 474L, 'x/x-image-sgi'],
+  [0L, 'string', '=', 'IT01', 'FIT image data'],
+  [0L, 'string', '=', 'IT02', 'FIT image data'],
+  [2048L, 'string', '=', 'PCD_IPI', 'x/x-photo-cd-pack-file'],
+  [0L, 'string', '=', 'PCD_OPA', 'x/x-photo-cd-overfiew-file'],
+  [0L, 'string', '=', 'SIMPLE  =', 'FITS image data'],
+  [0L, 'string', '=', 'This is a BitMap file', 'Lisp Machine bit-array-file'],
+  [0L, 'string', '=', '!!', 'Bennet Yee\'s "face" format'],
+  [0L, 'beshort', '=', 4112L, 'PEX Binary Archive'],
+  [3000L, 'string', '=', 'Visio (TM) Drawing', '%s'],
+  [0L, 'leshort', '=', 502L, 'basic-16 executable'],
+  [0L, 'leshort', '=', 503L, 'basic-16 executable (TV)'],
+  [0L, 'leshort', '=', 510L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 511L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 512L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 522L, 'application/x-executable-file'],
+  [0L, 'leshort', '=', 514L, 'application/x-executable-file'],
+  [0L, 'string', '=', '\210OPS', 'Interleaf saved data'],
+  [0L, 'string', '=', '<!OPS', 'Interleaf document text'],
+  [4L, 'string', '=', 'pgscriptver', 'IslandWrite document'],
+  [13L, 'string', '=', 'DrawFile', 'IslandDraw document'],
+  [0L, 'leshort&0xFFFC', '=', 38400L, 'little endian ispell'],
+  [0L, 'beshort&0xFFFC', '=', 38400L, 'big endian ispell'],
+  [0L, 'belong', '=', 3405691582L, 'compiled Java class data,'],
+  [0L, 'beshort', '=', 44269L, 'Java serialization data'],
+  [0L, 'string', '=', 'KarmaRHD', 'Version Karma Data Structure Version'],
+  [0L, 'string', '=', 'lect', 'DEC SRC Virtual Paper Lectern file'],
+  [53L, 'string', '=', 'yyprevious', 'C program text (from lex)'],
+  [21L, 'string', '=', 'generated by flex', 'C program text (from flex)'],
+  [0L, 'string', '=', '%{', 'lex description text'],
+  [0L, 'short', '=', 32768L, 'lif file'],
+  [0L, 'lelong', '=', 6553863L, 'Linux/i386 impure executable (OMAGIC)'],
+  [0L, 'lelong', '=', 6553864L, 'Linux/i386 pure executable (NMAGIC)'],
+  [0L, 'lelong', '=', 6553867L, 'Linux/i386 demand-paged executable (ZMAGIC)'],
+  [0L, 'lelong', '=', 6553804L, 'Linux/i386 demand-paged executable (QMAGIC)'],
+  [0L, 'string', '=', '\007\001\000', 'Linux/i386 object file'],
+  [0L, 'string', '=', '\001\003\020\004', 'Linux-8086 impure executable'],
+  [0L, 'string', '=', '\001\003 \004', 'Linux-8086 executable'],
+  [0L, 'string', '=', '\243\206\001\000', 'Linux-8086 object file'],
+  [0L, 'string', '=', '\001\003\020\020', 'Minix-386 impure executable'],
+  [0L, 'string', '=', '\001\003 \020', 'Minix-386 executable'],
+  [0L, 'string', '=', '*nazgul*', 'Linux compiled message catalog'],
+  [216L, 'lelong', '=', 421L, 'Linux/i386 core file'],
+  [2L, 'string', '=', 'LILO', 'Linux/i386 LILO boot/chain loader'],
+  [0L, 'string', '=', '0.9', ''],
+  [0L, 'leshort', '=', 1078L, 'font/linux-psf'],
+  [4086L, 'string', '=', 'SWAP-SPACE', 'Linux/i386 swap file'],
+  [0L, 'leshort', '=', 387L, 'ECOFF alpha'],
+  [514L, 'string', '=', 'HdrS', 'Linux kernel'],
+  [0L, 'belong', '=', 3099592590L, 'Linux kernel'],
+  [0L, 'string', '=', 'Begin3', 'Linux Software Map entry text'],
+  [0L, 'string', '=', ';;', 'Lisp/Scheme program text'],
+  [0L, 'string', '=', '\012(', 'byte-compiled Emacs-Lisp program data'],
+  [0L, 'string', '=', ';ELC\023\000\000\000', 'byte-compiled Emacs-Lisp program data'],
+  [0L, 'string', '=', "(SYSTEM::VERSION '", 'CLISP byte-compiled Lisp program text'],
+  [0L, 'long', '=', 1886817234L, 'CLISP memory image data'],
+  [0L, 'long', '=', 3532355184L, 'CLISP memory image data, other endian'],
+  [0L, 'long', '=', 3725722773L, 'GNU-format message catalog data'],
+  [0L, 'long', '=', 2500072158L, 'GNU-format message catalog data'],
+  [0L, 'belong', '=', 3405691582L, 'mach-o fat file'],
+  [0L, 'belong', '=', 4277009102L, 'mach-o'],
+  [11L, 'string', '=', 'must be converted with BinHex', 'BinHex binary text'],
+  [0L, 'string', '=', 'SIT!', 'StuffIt Archive (data)'],
+  [65L, 'string', '=', 'SIT!', 'StuffIt Archive (rsrc + data)'],
+  [0L, 'string', '=', 'SITD', 'StuffIt Deluxe (data)'],
+  [65L, 'string', '=', 'SITD', 'StuffIt Deluxe (rsrc + data)'],
+  [0L, 'string', '=', 'Seg', 'StuffIt Deluxe Segment (data)'],
+  [65L, 'string', '=', 'Seg', 'StuffIt Deluxe Segment (rsrc + data)'],
+  [0L, 'string', '=', 'APPL', 'Macintosh Application (data)'],
+  [65L, 'string', '=', 'APPL', 'Macintosh Application (rsrc + data)'],
+  [0L, 'string', '=', 'zsys', 'Macintosh System File (data)'],
+  [65L, 'string', '=', 'zsys', 'Macintosh System File(rsrc + data)'],
+  [0L, 'string', '=', 'FNDR', 'Macintosh Finder (data)'],
+  [65L, 'string', '=', 'FNDR', 'Macintosh Finder(rsrc + data)'],
+  [0L, 'string', '=', 'libr', 'Macintosh Library (data)'],
+  [65L, 'string', '=', 'libr', 'Macintosh Library(rsrc + data)'],
+  [0L, 'string', '=', 'shlb', 'Macintosh Shared Library (data)'],
+  [65L, 'string', '=', 'shlb', 'Macintosh Shared Library(rsrc + data)'],
+  [0L, 'string', '=', 'cdev', 'Macintosh Control Panel (data)'],
+  [65L, 'string', '=', 'cdev', 'Macintosh Control Panel(rsrc + data)'],
+  [0L, 'string', '=', 'INIT', 'Macintosh Extension (data)'],
+  [65L, 'string', '=', 'INIT', 'Macintosh Extension(rsrc + data)'],
+  [0L, 'string', '=', 'FFIL', 'font/ttf'],
+  [65L, 'string', '=', 'FFIL', 'font/ttf'],
+  [0L, 'string', '=', 'LWFN', 'font/type1'],
+  [65L, 'string', '=', 'LWFN', 'font/type1'],
+  [0L, 'string', '=', 'PACT', 'Macintosh Compact Pro Archive (data)'],
+  [65L, 'string', '=', 'PACT', 'Macintosh Compact Pro Archive(rsrc + data)'],
+  [0L, 'string', '=', 'ttro', 'Macintosh TeachText File (data)'],
+  [65L, 'string', '=', 'ttro', 'Macintosh TeachText File(rsrc + data)'],
+  [0L, 'string', '=', 'TEXT', 'Macintosh TeachText File (data)'],
+  [65L, 'string', '=', 'TEXT', 'Macintosh TeachText File(rsrc + data)'],
+  [0L, 'string', '=', 'PDF', 'Macintosh PDF File (data)'],
+  [65L, 'string', '=', 'PDF', 'Macintosh PDF File(rsrc + data)'],
+  [0L, 'string', '=', '# Magic', 'magic text file for file(1) cmd'],
+  [0L, 'string', '=', 'Relay-Version:', 'old news text'],
+  [0L, 'string', '=', '#! rnews', 'batched news text'],
+  [0L, 'string', '=', 'N#! rnews', 'mailed, batched news text'],
+  [0L, 'string', '=', 'Forward to', 'mail forwarding text'],
+  [0L, 'string', '=', 'Pipe to', 'mail piping text'],
+  [0L, 'string', '=', 'Return-Path:', 'message/rfc822'],
+  [0L, 'string', '=', 'Path:', 'message/news'],
+  [0L, 'string', '=', 'Xref:', 'message/news'],
+  [0L, 'string', '=', 'From:', 'message/rfc822'],
+  [0L, 'string', '=', 'Article', 'message/news'],
+  [0L, 'string', '=', 'BABYL', 'message/x-gnu-rmail'],
+  [0L, 'string', '=', 'Received:', 'message/rfc822'],
+  [0L, 'string', '=', 'MIME-Version:', 'MIME entity text'],
+  [0L, 'string', '=', 'Content-Type: ', ''],
+  [0L, 'string', '=', 'Content-Type:', ''],
+  [0L, 'long', '=', 31415L, 'Mirage Assembler m.out executable'],
+  [0L, 'string', '=', '\311\304', 'ID tags data'],
+  [0L, 'string', '=', '\001\001\001\001', 'MMDF mailbox'],
+  [4L, 'string', '=', 'Research,', 'Digifax-G3-File'],
+  [0L, 'short', '=', 256L, 'raw G3 data, byte-padded'],
+  [0L, 'short', '=', 5120L, 'raw G3 data'],
+  [0L, 'string', '=', 'RMD1', 'raw modem data'],
+  [0L, 'string', '=', 'PVF1\012', 'portable voice format'],
+  [0L, 'string', '=', 'PVF2\012', 'portable voice format'],
+  [0L, 'beshort', '=', 520L, 'mc68k COFF'],
+  [0L, 'beshort', '=', 521L, 'mc68k executable (shared)'],
+  [0L, 'beshort', '=', 522L, 'mc68k executable (shared demand paged)'],
+  [0L, 'beshort', '=', 554L, '68K BCS executable'],
+  [0L, 'beshort', '=', 555L, '88K BCS executable'],
+  [0L, 'string', '=', 'S0', 'Motorola S-Record; binary data in text format'],
+  [0L, 'string', '=', '@echo off', 'MS-DOS batch file text'],
+  [128L, 'string', '=', 'PE\000\000', 'MS Windows PE'],
+  [0L, 'leshort', '=', 332L, 'MS Windows COFF Intel 80386 object file'],
+  [0L, 'leshort', '=', 358L, 'MS Windows COFF MIPS R4000 object file'],
+  [0L, 'leshort', '=', 388L, 'MS Windows COFF Alpha object file'],
+  [0L, 'leshort', '=', 616L, 'MS Windows COFF Motorola 68000 object file'],
+  [0L, 'leshort', '=', 496L, 'MS Windows COFF PowerPC object file'],
+  [0L, 'leshort', '=', 656L, 'MS Windows COFF PA-RISC object file'],
+  [0L, 'string', '=', 'MZ', 'application/x-ms-dos-executable'],
+  [0L, 'string', '=', 'LZ', 'MS-DOS executable (built-in)'],
+  [0L, 'string', '=', 'regf', 'Windows NT Registry file'],
+  [2080L, 'string', '=', 'Microsoft Word 6.0 Document', 'text/vnd.ms-word'],
+  [2080L, 'string', '=', 'Documento Microsoft Word 6', 'text/vnd.ms-word'],
+  [2112L, 'string', '=', 'MSWordDoc', 'text/vnd.ms-word'],
+  [0L, 'belong', '=', 834535424L, 'text/vnd.ms-word'],
+  [0L, 'string', '=', 'PO^Q`', 'text/vnd.ms-word'],
+  [2080L, 'string', '=', 'Microsoft Excel 5.0 Worksheet', 'application/vnd.ms-excel'],
+  [2114L, 'string', '=', 'Biff5', 'application/vnd.ms-excel'],
+  [0L, 'belong', '=', 6656L, 'Lotus 1-2-3'],
+  [0L, 'belong', '=', 512L, 'Lotus 1-2-3'],
+  [1L, 'string', '=', 'WPC', 'text/vnd.wordperfect'],
+  [0L, 'beshort', '=', 610L, 'Tower/XP rel 2 object'],
+  [0L, 'beshort', '=', 615L, 'Tower/XP rel 2 object'],
+  [0L, 'beshort', '=', 620L, 'Tower/XP rel 3 object'],
+  [0L, 'beshort', '=', 625L, 'Tower/XP rel 3 object'],
+  [0L, 'beshort', '=', 630L, 'Tower32/600/400 68020 object'],
+  [0L, 'beshort', '=', 640L, 'Tower32/800 68020'],
+  [0L, 'beshort', '=', 645L, 'Tower32/800 68010'],
+  [0L, 'lelong', '=', 407L, 'NetBSD little-endian object file'],
+  [0L, 'belong', '=', 407L, 'NetBSD big-endian object file'],
+  [0L, 'belong&0377777777', '=', 41400413L, 'NetBSD/i386 demand paged'],
+  [0L, 'belong&0377777777', '=', 41400410L, 'NetBSD/i386 pure'],
+  [0L, 'belong&0377777777', '=', 41400407L, 'NetBSD/i386'],
+  [0L, 'belong&0377777777', '=', 41400507L, 'NetBSD/i386 core'],
+  [0L, 'belong&0377777777', '=', 41600413L, 'NetBSD/m68k demand paged'],
+  [0L, 'belong&0377777777', '=', 41600410L, 'NetBSD/m68k pure'],
+  [0L, 'belong&0377777777', '=', 41600407L, 'NetBSD/m68k'],
+  [0L, 'belong&0377777777', '=', 41600507L, 'NetBSD/m68k core'],
+  [0L, 'belong&0377777777', '=', 42000413L, 'NetBSD/m68k4k demand paged'],
+  [0L, 'belong&0377777777', '=', 42000410L, 'NetBSD/m68k4k pure'],
+  [0L, 'belong&0377777777', '=', 42000407L, 'NetBSD/m68k4k'],
+  [0L, 'belong&0377777777', '=', 42000507L, 'NetBSD/m68k4k core'],
+  [0L, 'belong&0377777777', '=', 42200413L, 'NetBSD/ns32532 demand paged'],
+  [0L, 'belong&0377777777', '=', 42200410L, 'NetBSD/ns32532 pure'],
+  [0L, 'belong&0377777777', '=', 42200407L, 'NetBSD/ns32532'],
+  [0L, 'belong&0377777777', '=', 42200507L, 'NetBSD/ns32532 core'],
+  [0L, 'belong&0377777777', '=', 42400413L, 'NetBSD/sparc demand paged'],
+  [0L, 'belong&0377777777', '=', 42400410L, 'NetBSD/sparc pure'],
+  [0L, 'belong&0377777777', '=', 42400407L, 'NetBSD/sparc'],
+  [0L, 'belong&0377777777', '=', 42400507L, 'NetBSD/sparc core'],
+  [0L, 'belong&0377777777', '=', 42600413L, 'NetBSD/pmax demand paged'],
+  [0L, 'belong&0377777777', '=', 42600410L, 'NetBSD/pmax pure'],
+  [0L, 'belong&0377777777', '=', 42600407L, 'NetBSD/pmax'],
+  [0L, 'belong&0377777777', '=', 42600507L, 'NetBSD/pmax core'],
+  [0L, 'belong&0377777777', '=', 43000413L, 'NetBSD/vax demand paged'],
+  [0L, 'belong&0377777777', '=', 43000410L, 'NetBSD/vax pure'],
+  [0L, 'belong&0377777777', '=', 43000407L, 'NetBSD/vax'],
+  [0L, 'belong&0377777777', '=', 43000507L, 'NetBSD/vax core'],
+  [0L, 'lelong', '=', 459141L, 'ECOFF NetBSD/alpha binary'],
+  [0L, 'belong&0377777777', '=', 43200507L, 'NetBSD/alpha core'],
+  [0L, 'belong&0377777777', '=', 43400413L, 'NetBSD/mips demand paged'],
+  [0L, 'belong&0377777777', '=', 43400410L, 'NetBSD/mips pure'],
+  [0L, 'belong&0377777777', '=', 43400407L, 'NetBSD/mips'],
+  [0L, 'belong&0377777777', '=', 43400507L, 'NetBSD/mips core'],
+  [0L, 'belong&0377777777', '=', 43600413L, 'NetBSD/arm32 demand paged'],
+  [0L, 'belong&0377777777', '=', 43600410L, 'NetBSD/arm32 pure'],
+  [0L, 'belong&0377777777', '=', 43600407L, 'NetBSD/arm32'],
+  [0L, 'belong&0377777777', '=', 43600507L, 'NetBSD/arm32 core'],
+  [0L, 'string', '=', 'StartFontMetrics', 'font/x-sunos-news'],
+  [0L, 'string', '=', 'StartFont', 'font/x-sunos-news'],
+  [0L, 'belong', '=', 326773060L, 'font/x-sunos-news'],
+  [0L, 'belong', '=', 326773063L, 'font/x-sunos-news'],
+  [0L, 'belong', '=', 326773072L, 'font/x-sunos-news'],
+  [0L, 'belong', '=', 326773073L, 'font/x-sunos-news'],
+  [8L, 'belong', '=', 326773573L, 'font/x-sunos-news'],
+  [8L, 'belong', '=', 326773576L, 'font/x-sunos-news'],
+  [0L, 'string', '=', 'Octave-1-L', 'Octave binary data (little endian)'],
+  [0L, 'string', '=', 'Octave-1-B', 'Octave binary data (big endian)'],
+  [0L, 'string', '=', '\177OLF', 'OLF'],
+  [0L, 'beshort', '=', 34765L, 'OS9/6809 module:'],
+  [0L, 'beshort', '=', 19196L, 'OS9/68K module:'],
+  [0L, 'long', '=', 61374L, 'OSF/Rose object'],
+  [0L, 'short', '=', 565L, 'i386 COFF object'],
+  [0L, 'short', '=', 10775L, '"compact bitmap" format (Poskanzer)'],
+  [0L, 'string', '=', '%PDF-', 'PDF document'],
+  [0L, 'lelong', '=', 101555L, 'PDP-11 single precision APL workspace'],
+  [0L, 'lelong', '=', 101554L, 'PDP-11 double precision APL workspace'],
+  [0L, 'leshort', '=', 407L, 'PDP-11 executable'],
+  [0L, 'leshort', '=', 401L, 'PDP-11 UNIX/RT ldp'],
+  [0L, 'leshort', '=', 405L, 'PDP-11 old overlay'],
+  [0L, 'leshort', '=', 410L, 'PDP-11 pure executable'],
+  [0L, 'leshort', '=', 411L, 'PDP-11 separate I&D executable'],
+  [0L, 'leshort', '=', 437L, 'PDP-11 kernel overlay'],
+  [0L, 'beshort', '=', 39168L, 'PGP key public ring'],
+  [0L, 'beshort', '=', 38145L, 'PGP key security ring'],
+  [0L, 'beshort', '=', 38144L, 'PGP key security ring'],
+  [0L, 'beshort', '=', 42496L, 'PGP encrypted data'],
+  [0L, 'string', '=', '-----BEGIN PGP', 'PGP armored data'],
+  [0L, 'string', '=', '# PaCkAgE DaTaStReAm', 'pkg Datastream (SVR4)'],
+  [0L, 'short', '=', 601L, 'mumps avl global'],
+  [0L, 'short', '=', 602L, 'mumps blt global'],
+  [0L, 'string', '=', '%!', 'application/postscript'],
+  [0L, 'string', '=', '\004%!', 'application/postscript'],
+  [0L, 'belong', '=', 3318797254L, 'DOS EPS Binary File'],
+  [0L, 'string', '=', '*PPD-Adobe:', 'PPD file'],
+  [0L, 'string', '=', '\033%-12345X at PJL', 'HP Printer Job Language data'],
+  [0L, 'string', '=', '\033%-12345X at PJL', 'HP Printer Job Language data'],
+  [0L, 'string', '=', '\033E\033', 'image/x-pcl-hp'],
+  [0L, 'string', '=', '@document(', 'Imagen printer'],
+  [0L, 'string', '=', 'Rast', 'RST-format raster font data'],
+  [0L, 'belong&0xff00ffff', '=', 1442840576L, 'ps database'],
+  [0L, 'long', '=', 1351614727L, 'Pyramid 90x family executable'],
+  [0L, 'long', '=', 1351614728L, 'Pyramid 90x family pure executable'],
+  [0L, 'long', '=', 1351614731L, 'Pyramid 90x family demand paged pure executable'],
+  [0L, 'beshort', '=', 60843L, ''],
+  [0L, 'string', '=', '{\\\\rtf', 'Rich Text Format data,'],
+  [38L, 'string', '=', 'Spreadsheet', 'sc spreadsheet file'],
+  [8L, 'string', '=', '\001s SCCS', 'archive data'],
+  [0L, 'byte', '=', 46L, 'Sendmail frozen configuration'],
+  [0L, 'short', '=', 10012L, 'Sendmail frozen configuration'],
+  [0L, 'lelong', '=', 234L, 'BALANCE NS32000 .o'],
+  [0L, 'lelong', '=', 4330L, 'BALANCE NS32000 executable (0 @ 0)'],
+  [0L, 'lelong', '=', 8426L, 'BALANCE NS32000 executable (invalid @ 0)'],
+  [0L, 'lelong', '=', 12522L, 'BALANCE NS32000 standalone executable'],
+  [0L, 'leshort', '=', 4843L, 'SYMMETRY i386 .o'],
+  [0L, 'leshort', '=', 8939L, 'SYMMETRY i386 executable (0 @ 0)'],
+  [0L, 'leshort', '=', 13035L, 'SYMMETRY i386 executable (invalid @ 0)'],
+  [0L, 'leshort', '=', 17131L, 'SYMMETRY i386 standalone executable'],
+  [0L, 'string', '=', 'kbd!map', 'kbd map file'],
+  [0L, 'belong', '=', 407L, 'old SGI 68020 executable'],
+  [0L, 'belong', '=', 410L, 'old SGI 68020 pure executable'],
+  [0L, 'beshort', '=', 34661L, 'disk quotas file'],
+  [0L, 'beshort', '=', 1286L, 'IRIS Showcase file'],
+  [0L, 'beshort', '=', 550L, 'IRIS Showcase template'],
+  [0L, 'belong', '=', 1396917837L, 'IRIS Showcase file'],
+  [0L, 'belong', '=', 1413695053L, 'IRIS Showcase template'],
+  [0L, 'belong', '=', 3735927486L, 'IRIX Parallel Arena'],
+  [0L, 'beshort', '=', 352L, 'MIPSEB COFF executable'],
+  [0L, 'beshort', '=', 354L, 'MIPSEL COFF executable'],
+  [0L, 'beshort', '=', 24577L, 'MIPSEB-LE COFF executable'],
+  [0L, 'beshort', '=', 25089L, 'MIPSEL-LE COFF executable'],
+  [0L, 'beshort', '=', 355L, 'MIPSEB MIPS-II COFF executable'],
+  [0L, 'beshort', '=', 358L, 'MIPSEL MIPS-II COFF executable'],
+  [0L, 'beshort', '=', 25345L, 'MIPSEB-LE MIPS-II COFF executable'],
+  [0L, 'beshort', '=', 26113L, 'MIPSEL-LE MIPS-II COFF executable'],
+  [0L, 'beshort', '=', 320L, 'MIPSEB MIPS-III COFF executable'],
+  [0L, 'beshort', '=', 322L, 'MIPSEL MIPS-III COFF executable'],
+  [0L, 'beshort', '=', 16385L, 'MIPSEB-LE MIPS-III COFF executable'],
+  [0L, 'beshort', '=', 16897L, 'MIPSEL-LE MIPS-III COFF executable'],
+  [0L, 'beshort', '=', 384L, 'MIPSEB Ucode'],
+  [0L, 'beshort', '=', 386L, 'MIPSEL Ucode'],
+  [0L, 'belong', '=', 3735924144L, 'IRIX core dump'],
+  [0L, 'belong', '=', 3735924032L, 'IRIX 64-bit core dump'],
+  [0L, 'belong', '=', 3133063355L, 'IRIX N32 core dump'],
+  [0L, 'string', '=', 'CrshDump', 'IRIX vmcore dump of'],
+  [0L, 'string', '=', 'SGIAUDIT', 'SGI Audit file'],
+  [0L, 'string', '=', 'WNGZWZSC', 'Wingz compiled script'],
+  [0L, 'string', '=', 'WNGZWZSS', 'Wingz spreadsheet'],
+  [0L, 'string', '=', 'WNGZWZHP', 'Wingz help file'],
+  [0L, 'string', '=', '\\#Inventor', 'V IRIS Inventor 1.0 file'],
+  [0L, 'string', '=', '\\#Inventor', 'V2 Open Inventor 2.0 file'],
+  [0L, 'string', '=', 'glfHeadMagic();', 'GLF_TEXT'],
+  [4L, 'belong', '=', 1090584576L, 'GLF_BINARY_LSB_FIRST'],
+  [4L, 'belong', '=', 321L, 'GLF_BINARY_MSB_FIRST'],
+  [0L, 'string', '=', '<!DOCTYPE HTML', 'text/html'],
+  [0L, 'string', '=', '<!doctype html', 'text/html'],
+  [0L, 'string', '=', '<HEAD', 'text/html'],
+  [0L, 'string', '=', '<head', 'text/html'],
+  [0L, 'string', '=', '<TITLE', 'text/html'],
+  [0L, 'string', '=', '<title', 'text/html'],
+  [0L, 'string', '=', '<html', 'text/html'],
+  [0L, 'string', '=', '<HTML', 'text/html'],
+  [0L, 'string', '=', '<!DOCTYPE', 'exported SGML document text'],
+  [0L, 'string', '=', '<!doctype', 'exported SGML document text'],
+  [0L, 'string', '=', '<!SUBDOC', 'exported SGML subdocument text'],
+  [0L, 'string', '=', '<!subdoc', 'exported SGML subdocument text'],
+  [0L, 'string', '=', '<!--', 'exported SGML document text'],
+  [0L, 'string', '=', 'RTSS', 'NetMon capture file'],
+  [0L, 'string', '=', 'TRSNIFF data    \032', 'Sniffer capture file'],
+  [0L, 'string', '=', 'XCP\000', 'NetXRay capture file'],
+  [0L, 'ubelong', '=', 2712847316L, 'tcpdump capture file (big-endian)'],
+  [0L, 'ulelong', '=', 2712847316L, 'tcpdump capture file (little-endian)'],
+  [0L, 'string', '=', '<!SQ DTD>', 'Compiled SGML rules file'],
+  [0L, 'string', '=', '<!SQ A/E>', 'A/E SGML Document binary'],
+  [0L, 'string', '=', '<!SQ STS>', 'A/E SGML binary styles file'],
+  [0L, 'short', '=', 49374L, 'Compiled PSI (v1) data'],
+  [0L, 'short', '=', 49370L, 'Compiled PSI (v2) data'],
+  [0L, 'short', '=', 125252L, 'SoftQuad DESC or font file binary'],
+  [0L, 'string', '=', 'SQ BITMAP1', 'SoftQuad Raster Format text'],
+  [0L, 'string', '=', 'X SoftQuad', 'troff Context intermediate'],
+  [0L, 'belong&077777777', '=', 600413L, 'sparc demand paged'],
+  [0L, 'belong&077777777', '=', 600410L, 'sparc pure'],
+  [0L, 'belong&077777777', '=', 600407L, 'sparc'],
+  [0L, 'belong&077777777', '=', 400413L, 'mc68020 demand paged'],
+  [0L, 'belong&077777777', '=', 400410L, 'mc68020 pure'],
+  [0L, 'belong&077777777', '=', 400407L, 'mc68020'],
+  [0L, 'belong&077777777', '=', 200413L, 'mc68010 demand paged'],
+  [0L, 'belong&077777777', '=', 200410L, 'mc68010 pure'],
+  [0L, 'belong&077777777', '=', 200407L, 'mc68010'],
+  [0L, 'belong', '=', 407L, 'old sun-2 executable'],
+  [0L, 'belong', '=', 410L, 'old sun-2 pure executable'],
+  [0L, 'belong', '=', 413L, 'old sun-2 demand paged executable'],
+  [0L, 'belong', '=', 525398L, 'SunOS core file'],
+  [0L, 'long', '=', 4197695630L, 'SunPC 4.0 Hard Disk'],
+  [0L, 'string', '=', '#SUNPC_CONFIG', 'SunPC 4.0 Properties Values'],
+  [0L, 'string', '=', 'snoop', 'Snoop capture file'],
+  [36L, 'string', '=', 'acsp', 'Kodak Color Management System, ICC Profile'],
+  [0L, 'string', '=', '#!teapot\012xdr', 'teapot work sheet (XDR format)'],
+  [0L, 'string', '=', '\032\001', 'Compiled terminfo entry'],
+  [0L, 'short', '=', 433L, 'Curses screen image'],
+  [0L, 'short', '=', 434L, 'Curses screen image'],
+  [0L, 'string', '=', '\367\002', 'TeX DVI file'],
+  [0L, 'string', '=', '\367\203', 'font/x-tex'],
+  [0L, 'string', '=', '\367Y', 'font/x-tex'],
+  [0L, 'string', '=', '\367\312', 'font/x-tex'],
+  [0L, 'string', '=', 'This is TeX,', 'TeX transcript text'],
+  [0L, 'string', '=', 'This is METAFONT,', 'METAFONT transcript text'],
+  [2L, 'string', '=', '\000\021', 'font/x-tex-tfm'],
+  [2L, 'string', '=', '\000\022', 'font/x-tex-tfm'],
+  [0L, 'string', '=', '\\\\input\\', 'texinfo Texinfo source text'],
+  [0L, 'string', '=', 'This is Info file', 'GNU Info text'],
+  [0L, 'string', '=', '\\\\input', 'TeX document text'],
+  [0L, 'string', '=', '\\\\section', 'LaTeX document text'],
+  [0L, 'string', '=', '\\\\setlength', 'LaTeX document text'],
+  [0L, 'string', '=', '\\\\documentstyle', 'LaTeX document text'],
+  [0L, 'string', '=', '\\\\chapter', 'LaTeX document text'],
+  [0L, 'string', '=', '\\\\documentclass', 'LaTeX 2e document text'],
+  [0L, 'string', '=', '\\\\relax', 'LaTeX auxiliary file'],
+  [0L, 'string', '=', '\\\\contentsline', 'LaTeX table of contents'],
+  [0L, 'string', '=', '\\\\indexentry', 'LaTeX raw index file'],
+  [0L, 'string', '=', '\\\\begin{theindex}', 'LaTeX sorted index'],
+  [0L, 'string', '=', '\\\\glossaryentry', 'LaTeX raw glossary'],
+  [0L, 'string', '=', '\\\\begin{theglossary}', 'LaTeX sorted glossary'],
+  [0L, 'string', '=', 'This is makeindex', 'Makeindex log file'],
+  [0L, 'string', '=', '**TI82**', 'TI-82 Graphing Calculator'],
+  [0L, 'string', '=', '**TI83**', 'TI-83 Graphing Calculator'],
+  [0L, 'string', '=', '**TI85**', 'TI-85 Graphing Calculator'],
+  [0L, 'string', '=', '**TI92**', 'TI-92 Graphing Calculator'],
+  [0L, 'string', '=', '**TI80**', 'TI-80 Graphing Calculator File.'],
+  [0L, 'string', '=', '**TI81**', 'TI-81 Graphing Calculator File.'],
+  [0L, 'string', '=', 'TZif', 'timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000', 'old timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000', 'old timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\003\000', 'old timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004\000', 'old timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\005\000', 'old timezone data'],
+  [0L, 'string', '=', '\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\000', 'old timezone data'],
+  [0L, 'string', '=', '.\\\\"', 'troff or preprocessor input text'],
+  [0L, 'string', '=', '\'\\\\"', 'troff or preprocessor input text'],
+  [0L, 'string', '=', '\'.\\\\"', 'troff or preprocessor input text'],
+  [0L, 'string', '=', '\\\\"', 'troff or preprocessor input text'],
+  [0L, 'string', '=', 'x T', 'ditroff text'],
+  [0L, 'string', '=', '@\357', 'very old (C/A/T) troff output data'],
+  [0L, 'string', '=', 'Interpress/Xerox', 'Xerox InterPress data'],
+  [0L, 'short', '=', 263L, 'unknown machine executable'],
+  [0L, 'short', '=', 264L, 'unknown pure executable'],
+  [0L, 'short', '=', 265L, 'PDP-11 separate I&D'],
+  [0L, 'short', '=', 267L, 'unknown pure executable'],
+  [0L, 'long', '=', 268L, 'unknown demand paged pure executable'],
+  [0L, 'long', '=', 269L, 'unknown demand paged pure executable'],
+  [0L, 'long', '=', 270L, 'unknown readable demand paged pure executable'],
+  [0L, 'string', '=', 'begin uuencoded', 'or xxencoded text'],
+  [0L, 'string', '=', 'xbtoa Begin', "btoa'd text"],
+  [0L, 'string', '=', '$\012ship', "ship'd binary text"],
+  [0L, 'string', '=', 'Decode the following with bdeco', 'bencoded News text'],
+  [11L, 'string', '=', 'must be converted with BinHex', 'BinHex binary text'],
+  [0L, 'short', '=', 610L, 'Perkin-Elmer executable'],
+  [0L, 'beshort', '=', 572L, 'amd 29k coff noprebar executable'],
+  [0L, 'beshort', '=', 1572L, 'amd 29k coff prebar executable'],
+  [0L, 'beshort', '=', 160007L, 'amd 29k coff archive'],
+  [6L, 'beshort', '=', 407L, 'unicos (cray) executable'],
+  [596L, 'string', '=', 'X\337\377\377', 'Ultrix core file'],
+  [0L, 'string', '=', 'Joy!peffpwpc', 'header for PowerPC PEF executable'],
+  [0L, 'lelong', '=', 101557L, 'VAX single precision APL workspace'],
+  [0L, 'lelong', '=', 101556L, 'VAX double precision APL workspace'],
+  [0L, 'lelong', '=', 407L, 'VAX executable'],
+  [0L, 'lelong', '=', 410L, 'VAX pure executable'],
+  [0L, 'lelong', '=', 413L, 'VAX demand paged pure executable'],
+  [0L, 'leshort', '=', 570L, 'VAX COFF executable'],
+  [0L, 'leshort', '=', 575L, 'VAX COFF pure executable'],
+  [0L, 'string', '=', 'LBLSIZE=', 'VICAR image data'],
+  [43L, 'string', '=', 'SFDU_LABEL', 'VICAR label file'],
+  [0L, 'short', '=', 21845L, 'VISX image file'],
+  [0L, 'string', '=', '\260\0000\000', 'VMS VAX executable'],
+  [0L, 'belong', '=', 50331648L, 'VMS Alpha executable'],
+  [1L, 'string', '=', 'WPC', '(Corel/WP)'],
+  [0L, 'string', '=', 'core', 'core file (Xenix)'],
+  [0L, 'byte', '=', 128L, '8086 relocatable (Microsoft)'],
+  [0L, 'leshort', '=', 65381L, 'x.out'],
+  [0L, 'leshort', '=', 518L, 'Microsoft a.out'],
+  [0L, 'leshort', '=', 320L, 'old Microsoft 8086 x.out'],
+  [0L, 'lelong', '=', 518L, 'b.out'],
+  [0L, 'leshort', '=', 1408L, 'XENIX 8086 relocatable or 80286 small model'],
+  [0L, 'long', '=', 59399L, 'object file (z8000 a.out)'],
+  [0L, 'long', '=', 59400L, 'pure object file (z8000 a.out)'],
+  [0L, 'long', '=', 59401L, 'separate object file (z8000 a.out)'],
+  [0L, 'long', '=', 59397L, 'overlay object file (z8000 a.out)'],
+  [0L, 'string', '=', 'ZyXEL\002', 'ZyXEL voice data'],
+]
+
+magicNumbers = []
+
+def strToNum(n):
+  val = 0
+  col = long(1)
+  if n[:1] == 'x': n = '0' + n
+  if n[:2] == '0x':
+    # hex
+    n = string.lower(n[2:])
+    while len(n) > 0:
+      l = n[len(n) - 1]
+      val = val + string.hexdigits.index(l) * col
+      col = col * 16
+      n = n[:len(n)-1]
+  elif n[0] == '\\':
+    # octal
+    n = n[1:]
+    while len(n) > 0:
+      l = n[len(n) - 1]
+      if ord(l) < 48 or ord(l) > 57: break
+      val = val + int(l) * col
+      col = col * 8
+      n = n[:len(n)-1]
+  else:
+    val = string.atol(n)
+  return val
+       
+def unescape(s):
+  # replace string escape sequences
+  while 1:
+    m = re.search(r'\\', s)
+    if not m: break
+    x = m.start()+1
+    if m.end() == len(s): 
+      # escaped space at end
+      s = s[:len(s)-1] + ' '
+    elif s[x:x+2] == '0x':
+      # hex ascii value
+      c = chr(strToNum(s[x:x+4]))
+      s = s[:x-1] + c + s[x+4:]
+    elif s[m.start()+1] == 'x':
+      # hex ascii value
+      c = chr(strToNum(s[x:x+3]))
+      s = s[:x-1] + c + s[x+3:]
+    elif ord(s[x]) > 47 and ord(s[x]) < 58:
+      # octal ascii value
+      end = x
+      while (ord(s[end]) > 47 and ord(s[end]) < 58):
+        end = end + 1
+        if end > len(s) - 1: break
+      c = chr(strToNum(s[x-1:end]))
+      s = s[:x-1] + c + s[end:]
+    elif s[x] == 'n':
+      # newline
+      s = s[:x-1] + '\n' + s[x+1:]
+    else:
+      break
+  return s
+
+class magicTest:
+  def __init__(self, offset, t, op, value, msg, mask = None):
+    if t.count('&') > 0:
+      mask = strToNum(t[t.index('&')+1:])  
+      t = t[:t.index('&')]
+    if type(offset) == type('a'):
+      self.offset = strToNum(offset)
+    else:
+      self.offset = offset
+    self.type = t
+    self.msg = msg
+    self.subTests = []
+    self.op = op
+    self.mask = mask
+    self.value = value
+      
+
+  def test(self, data):
+    if self.mask:
+      data = data & self.mask
+    if self.op == '=': 
+      if self.value == data: return self.msg
+    elif self.op ==  '<':
+      pass
+    elif self.op ==  '>':
+      pass
+    elif self.op ==  '&':
+      pass
+    elif self.op ==  '^':
+      pass
+    return None
+
+  def compare(self, data):
+    #print str([self.type, self.value, self.msg])
+    try: 
+      if self.type == 'string':
+        c = ''; s = ''
+        for i in range(0, len(self.value)+1):
+          if i + self.offset > len(data) - 1: break
+          s = s + c
+          [c] = struct.unpack('c', data[self.offset + i])
+        data = s
+      elif self.type == 'short':
+        [data] = struct.unpack('h', data[self.offset : self.offset + 2])
+      elif self.type == 'leshort':
+        [data] = struct.unpack('<h', data[self.offset : self.offset + 2])
+      elif self.type == 'beshort':
+        [data] = struct.unpack('>H', data[self.offset : self.offset + 2])
+      elif self.type == 'long':
+        [data] = struct.unpack('l', data[self.offset : self.offset + 4])
+      elif self.type == 'lelong':
+        [data] = struct.unpack('<l', data[self.offset : self.offset + 4])
+      elif self.type == 'belong':
+        [data] = struct.unpack('>l', data[self.offset : self.offset + 4])
+      else:
+        #print 'UNKNOWN TYPE: ' + self.type
+        pass
+    except:
+      return None
+  
+#    print str([self.msg, self.value, data])
+    return self.test(data)
+    
+
+def load(file):
+  global magicNumbers
+  lines = open(file).readlines()
+  last = { 0: None }
+  for line in lines:
+    if re.match(r'\s*#', line):
+      # comment
+      continue
+    else:
+      # split up by space delimiters, and remove trailing space
+      line = string.rstrip(line)
+      line = re.split(r'\s*', line)
+      if len(line) < 3:
+        # bad line
+        continue
+      offset = line[0]
+      type = line[1]
+      value = line[2]
+      level = 0
+      while offset[0] == '>':
+        # count the level of the type
+        level = level + 1
+        offset = offset[1:]
+      l = magicNumbers
+      if level > 0:
+        l = last[level - 1].subTests
+      if offset[0] == '(':
+        # don't handle indirect offsets just yet
+        print 'SKIPPING ' + string.join(list(line[3:]))
+        pass
+      elif offset[0] == '&':
+        # don't handle relative offsets just yet
+        print 'SKIPPING ' + string.join(list(line[3:]))
+        pass
+      else:
+        operands = ['=', '<', '>', '&']
+        if operands.count(value[0]) > 0:
+          # a comparison operator is specified
+          op = value[0] 
+          value = value[1:]
+        else:
+          print str([value, operands])
+          if len(value) >1 and value[0] == '\\' and operands.count(value[1]) >0:
+            # literal value that collides with operands is escaped
+            value = value[1:]
+          op = '='
+
+        mask = None
+        if type == 'string':
+          while 1:
+            value = unescape(value)
+            if value[len(value)-1] == ' ' and len(line) > 3:
+              # last value was an escaped space, join
+              value = value + line[3]
+              del line[3]
+            else:
+              break
+        else:
+          if value.count('&') != 0:
+            mask = value[(value.index('&') + 1):]
+            print 'MASK: ' + mask
+            value = value[:(value.index('&')+1)]
+          try: value = strToNum(value)
+          except: continue
+          msg = string.join(list(line[3:]))
+        new = magicTest(offset, type, op, value, msg, mask)
+        last[level] = new
+        l.append(new)
+
+def whatis(data):
+  for test in magicNumbers:
+     m = test.compare(data)
+     if m: return m
+  # no matching, magic number. is it binary or text?
+  for c in data:
+    if ord(c) > 128:
+      return 'data'
+  # its ASCII, now do text tests
+  if string.find('The', data, 0, 8192) > -1:
+    return 'English text'
+  if string.find('def', data, 0, 8192) > -1:
+    return 'Python Source'
+  return 'ASCII text'
+      
+    
+def file(file):
+  try:
+    return whatis(open(file, 'r').read(8192))
+  except Exception, e:
+    if str(e) == '[Errno 21] Is a directory':
+      return 'directory'
+    else:
+      raise e
+  
+
+#### BUILD DATA ####
+#load('mime-magic')
+#f = open('out', 'w')
+#for m in magicNumbers:
+#  f.write(str([m.offset, m.type, m.op, m.value, m.msg]) + ',\n')
+#f.close
+
+import sys
+for m in magic:
+  magicNumbers.append(magicTest(m[0], m[1], m[2], m[3], m[4]))
+
+if __name__ == '__main__':
+  import sys
+  for arg in sys.argv[1:]:
+    msg = file(arg)
+    if msg:
+      print arg + ': ' + msg
+    else:
+      print arg + ': unknown'
diff --git a/DAVServer/server.py b/DAVServer/server.py
index 0b2801b..b070a5c 100755
--- a/DAVServer/server.py
+++ b/DAVServer/server.py
@@ -52,6 +52,7 @@ def runserver(
          server = BaseHTTPServer.HTTPServer):
 
     directory = directory.strip()
+    directory = directory.rstrip('/')
     host = host.strip()
    
     if not os.path.isdir(directory):
@@ -79,6 +80,14 @@ def runserver(
         handler.DO_AUTH = False
 
     print >>sys.stderr, '>> Serving data from %s' % directory
+
+    if handler._config.DAV.lockemulation is False:
+        print >>sys.stderr, '>> Deactivated LOCK, UNLOCK (WebDAV level 2) support'
+
+    handler.IFACE_CLASS.mimecheck = True
+    if handler._config.DAV.mimecheck is False:
+        handler.IFACE_CLASS.mimecheck = False
+        print >>sys.stderr, '>> Disabled mimetype sniffing (All files will have type application/octet-stream)'
    
     # initialize server on specified port
     runner = server( (host, port), handler )
@@ -95,7 +104,11 @@ def runserver(
         print >>sys.stderr, '\n>> Killed by user'
 
 usage = """PyWebDAV server (version %s)
-Standalone WebDAV server based on python
+Standalone WebDAV server
+
+Make sure to activate LOCK, UNLOCK using parameter -J if you want
+to use clients like Windows Explorer or Mac OS X Finder that expect
+LOCK working for write support.
 
 Usage: ./server.py [OPTIONS]
 Parameters:
@@ -116,7 +129,10 @@ Parameters:
                     If you want to use MySQL then the usage of a configuration
                     file is mandatory.
     -J, --lockemu   Activate experimental LOCK and UNLOCK mode (WebDAV Version 2).
-                    Currently know to work but needs more tests.
+                    Currently know to work but needs more tests. Default is ON.
+    -M, --nomime    Deactivate mimetype sniffing. Sniffing is based on magic numbers
+                    detection but can be slow under heavy load. If you are experiencing
+                    speed problems try to use this parameter.
     -i, --icounter  If you want to run multiple instances then you have to
                     give each instance it own number so that logfiles and such
                     can be identified. Default is 0
@@ -158,14 +174,15 @@ def run():
     daemonaction = 'start'
     counter = 0
     mysql = False
-    lockemulation = False
+    lockemulation = True
     configfile = ''
+    mimecheck = True
     
     # parse commandline
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:', 
+        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:M', 
                 ['host=', 'port=', 'directory=', 'user=', 'password=','daemon=', 'noauth', 'help', 'verbose', 
-                 'mysql', 'icounter=', 'config=', 'lockemu'])
+                 'mysql', 'icounter=', 'config=', 'lockemu', 'nomime'])
     except getopt.GetoptError, e:
         print usage
         print '>>>> ERROR: %s' % str(e)
@@ -178,6 +195,9 @@ def run():
         if o in ['-m', '--mysql']:
             mysql = True
 
+        if o in ['-M', '--nomime']:
+            mimecheck = False
+
         if o in ['-J', '--lockemu']:
             lockemulation = True
 
@@ -190,7 +210,7 @@ def run():
         if o in ['-H', '--host']:
             host = a
 
-        if o in ['-P', '--p']:
+        if o in ['-P', '--port']:
             port = a
 
         if o in ['-v', '--verbose']:
@@ -230,6 +250,8 @@ def run():
         if daemonaction != 'stop':
             daemonaction = dv.daemonaction
         counter = int(dv.counter)
+        lockemulation = dv.lockemulation
+        mimecheck = dv.mimecheck
 
     else:
 
@@ -243,7 +265,8 @@ def run():
                 'daemonize' : daemonize,
                 'daemonaction' : daemonaction,
                 'counter' : counter,
-                'lockemulation' : lockemulation }
+                'lockemulation' : lockemulation,
+                'mimecheck' : mimecheck}
 
         conf = setupDummyConfig(**_dc)
 
@@ -272,7 +295,7 @@ def run():
     if daemonize:
 
         # check if pid file exists
-        if os.path.exists('/tmp/pydav%s.pid' % counter) and daemonaction != 'stop':
+        if os.path.exists('/tmp/pydav%s.pid' % counter) and daemonaction not in ['status', 'stop']:
             print >>sys.stderr, \
                   '>> ERROR: Found another instance! Either use -i to specifiy another instance number or remove /tmp/pydav%s.pid!' % counter
             sys.exit(3)
diff --git a/PKG-INFO b/PKG-INFO
index d95eda0..a24cad4 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,18 +1,20 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.2
+Version: 0.9.3
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
 Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
-        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
         Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
         
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
+        This package does *not* provide client functionality.
+        
         Example (using easy_install)::
         
         easy_install PyWebDAV
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
index d95eda0..a24cad4 100644
--- a/PyWebDAV.egg-info/PKG-INFO
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -1,18 +1,20 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.2
+Version: 0.9.3
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
 Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
-        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
         Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
         
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
+        This package does *not* provide client functionality.
+        
         Example (using easy_install)::
         
         easy_install PyWebDAV
diff --git a/PyWebDAV.egg-info/SOURCES.txt b/PyWebDAV.egg-info/SOURCES.txt
index 4c55b7f..75f83ea 100644
--- a/PyWebDAV.egg-info/SOURCES.txt
+++ b/PyWebDAV.egg-info/SOURCES.txt
@@ -19,6 +19,7 @@ DAV/iface.py
 DAV/locks.py
 DAV/propfind.py
 DAV/propfind.py.orig
+DAV/report.py
 DAV/status.py
 DAV/utils.py
 DAVServer/__init__.py
@@ -26,6 +27,7 @@ DAVServer/config.ini
 DAVServer/daemonize.py
 DAVServer/fileauth.py
 DAVServer/fshandler.py
+DAVServer/magic.py
 DAVServer/mysqlauth.py
 DAVServer/server.py
 PyWebDAV.egg-info/PKG-INFO
diff --git a/VERSION b/VERSION
index 2003b63..965065d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.2
+0.9.3
diff --git a/doc/Changes b/doc/Changes
index 0eb5f39..2c53012 100644
--- a/doc/Changes
+++ b/doc/Changes
@@ -1,4 +1,46 @@
 
+0.9.3 (July 2 2009)
+--
+
+- Setting WebDAV v2 as default because LOCK and UNLOCK seem
+  to be stable by now. -J parameter is ignored and will go away.
+  [Simon Pamies]
+
+- Fix for PROPFIND to return *all* properties
+  [Cedric Krier]
+
+- Fixed do_PUT initialisation
+  [Cedric Krier]
+
+- Added REPORT support
+  [Cedric Krier]
+
+- Added support for gzip encoding
+  [Cedric Krier]
+
+- Fix for wrong --port option
+  [Martin Wendt]
+
+- Handle paths correctly for Windows related env
+  [Martin Wendt]
+
+- Included mimetype check for files
+  based on magic.py from Jason Petrone. Included
+  magic.py into this package. All magic.py code
+  (c) 2000 Jason Petrone. Included from
+  http://www.jsnp.net/code/magic.py.
+  [Joerg Friedrich, Simon Pamies]
+
+- Status check not working when server is running
+  [Joerg Friedrich]
+
+- Fixed wrong time formatting for Last-Modified
+  and creationdate (must follow RFC 822 and 3339)
+  [Cedric Krier]
+
+0.9.2 (May 11 2009)
+--
+
 - Fixed COPY, MOVE, DELETE to support locked
   resources
   [Simon Pamies]
@@ -19,23 +61,23 @@
   [Jesus Cea]
 
 - Make propfind respect the depth from queries
-  [Cédric Krier]
+  [Cedric Krier]
 
 - Add ETag in the header of GET. This is needed to implement 
   GroupDAV, CardDAV and CalDAV.
-  [Cédric Krier]
+  [Cedric Krier]
 
 - Handle the "Expect 100-continue" header
-  [Cédric Krier]
+  [Cedric Krier]
 
 - Remove debug statements and remove logging
-  [Cédric Krier]
+  [Cedric Krier]
 
 - Use the Host header in baseuri if set.
-  [Cédric Krier]
+  [Cedric Krier]
 
 - Adding If-Match on PUT and DELETE
-  [Cédric Krier]
+  [Cedric Krier]
 
 0.9.1 (May 4th 2009)
 --
diff --git a/setup.py b/setup.py
index cef371b..f6149c5 100644
--- a/setup.py
+++ b/setup.py
@@ -12,12 +12,14 @@ VERSION = open('VERSION', 'r').read()
 VERSION = VERSION.replace('\n', '')
 
 DOC = """
-WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+WebDAV library for python. Consists of a server and the DAV package that provides WebDAV server(!) functionality.
 Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
 
 After installation of this package you will have a new script in you $PYTHON/bin directory called
 *davserver*. This serves as the main entry point to the server.
 
+This package does *not* provide client functionality.
+
 Example (using easy_install)::
 
     easy_install PyWebDAV
commit 4ee2b74440080853b094474ccec1be1e672bc421
Author: Daniel Baumann <daniel at debian.org>
Date:   Tue May 12 22:24:13 2009 +0200

    Adding upstream version 0.9.2.

diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
index ba771e2..2058ede 100644
--- a/DAV/AuthServer.py
+++ b/DAV/AuthServer.py
@@ -77,11 +77,7 @@ class AuthRequestHandler:
         Special handle method with buffering and authentication
         """
         
-        self.infp=open("/tmp/in.%s" %self.__class__, "a+")
-
-        self.infp.write("------------------------------------------------------------------------------\n")
         self.raw_requestline = self.rfile.readline()
-        self.infp.write(self.raw_requestline)
         self.request_version = version = "HTTP/0.9" # Default
         requestline = self.raw_requestline
 
@@ -112,7 +108,6 @@ class AuthRequestHandler:
         
         self.command, self.path, self.request_version = command, path, version
         self.headers = self.MessageClass(self.rfile, 0)
-        self.infp.write(str(self.headers))
 
         # test authentification
         if self.DO_AUTH:
@@ -138,14 +133,8 @@ class AuthRequestHandler:
         method = getattr(self, mname)
         method()
 
-        self.infp.flush()
-        self.infp.close()
         self._flush()
 
-    def write_infp(self,s):
-        self.infp.write(str(s))
-        self.infp.flush()
-
     def send_response(self,code, message=None):
         """Override send_response to use the correct http version
            in the response."""
diff --git a/DAV/BufferingHTTPServer.py b/DAV/BufferingHTTPServer.py
index 5984d35..db96618 100644
--- a/DAV/BufferingHTTPServer.py
+++ b/DAV/BufferingHTTPServer.py
@@ -48,7 +48,6 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
         this (see below)
         """
         self.__buffer=""
-        self.__outfp=open("/tmp/out.%s" %self.__class__,"a+")
     
     def _append(self,s):
         """ append a string to the buffer """
@@ -57,8 +56,6 @@ class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
     def _flush(self):
         """ flush the buffer to wfile """
         self.wfile.write(self.__buffer)
-        self.__outfp.write(self.__buffer)
-        self.__outfp.flush()
         self.wfile.flush()
         self.__buffer=""
 
diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
index 57da08f..fdfa564 100644
--- a/DAV/WebDAVServer.py
+++ b/DAV/WebDAVServer.py
@@ -47,11 +47,14 @@ from delete import DELETE
 from davcopy import COPY
 from davmove import MOVE
 
+from utils import rfc1123_date
 from string import atoi,split
-from status import STATUS_CODES
 from errors import *
 
-class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
+from constants import DAV_VERSION_1, DAV_VERSION_2
+from locks import LockManager
+
+class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler, LockManager):
     """Simple DAV request handler with 
     
     - GET
@@ -83,15 +86,16 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
         self.send_response(code,message=msg)
         self.send_header("Connection", "close")
         self.send_header("Accept-Ranges", "bytes")
+        self.send_header('Date', rfc1123_date())
         
         for a,v in headers.items():
             self.send_header(a,v)
         
         if DATA:
-            self.send_header("Content-Length", str(len(DATA)))
-            self.send_header("Content-Type", ctype)
+            self.send_header('Content-Length', len(DATA))
+            self.send_header('Content-Type', ctype)
         else:
-            self.send_header("Content-Length", "0")
+            self.send_header('Content-Length', 0)
         
         self.end_headers()
         if DATA:
@@ -105,7 +109,9 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
         self.send_header("Content-type", ctype)
         self.send_header("Connection", "close")
         self.send_header("Transfer-Encoding", "chunked")
+        self.send_header('Date', rfc1123_date())
         self.end_headers()
+
         self._append(hex(len(DATA))[2:]+"\r\n")
         self._append(DATA)
         self._append("\r\n")
@@ -116,80 +122,101 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
 
     def do_OPTIONS(self):
         """return the list of capabilities """
+
         self.send_response(200)
-        self.send_header("Content-Type", "text/plain")
+        self.send_header("Content-Length", 0)
 
         if self._config.DAV.lockemulation is True:
             if self._config.DAV.verbose is True:
-                print >>sys.stderr, 'Activated LOCK,UNLOCK emulation for this connection (NOT known to work currently)'
+                print >>sys.stderr, 'Activated LOCK,UNLOCK emulation (experimental)'
 
-            self.send_header('Allow', 'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, LOCK, UNLOCK')
-            self.send_header('DAV', '1,2')
+            self.send_header('Allow', DAV_VERSION_2['options'])
+            self.send_header('DAV', DAV_VERSION_2['version'])
 
         else:
-            self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
-            self.send_header('DAV', '1')
+            self.send_header('Allow', DAV_VERSION_1['options'])
+            self.send_header('DAV', DAV_VERSION_1['version'])
 
         self.send_header('MS-Author-Via', 'DAV') # this is for M$
         self.end_headers()
 
-    def _init_locks(self):
-        if not hasattr(self, '_lock_table'):
-            self._lock_table = {}
+    def _HEAD_GET(self, with_body=False):
+        """ Returns headers and body for given resource """
 
-        return self._lock_table
+        dc = self.IFACE_CLASS
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
 
-    def is_locked(self, uri):
-        table = self._init_locks()
-        return table.has_key(uri)
+        headers = {}
 
-    def do_LOCK(self):
-        """ Locking is implemented via in-memory caches. No data is written.  """
+        # get the last modified date (RFC 1123!)
+        try:
+            headers['Last-Modified'] = dc.get_prop(uri,"DAV:","getlastmodified")
+        except DAV_NotFound: headers['Last-Modified'] = "Sun, 01 Dec 2038 00:00:00 GMT"
 
-        if self._config.DAV.verbose is True:
-            print >>sys.stderr, 'LOCKing resource %s' % self.headers
+        # get the ETag if any
+        try:
+            headers['Etag'] = dc.get_prop(uri, "DAV:", "getetag")
+        except DAV_NotFound: pass
 
-        # XXX this is not finished and'll be replaced soon
-        token = str(random.randint(123, 12345678))
-        header = {'Lock-Token' : token}
-        return self.send_status(body=str(token))
+        # get the content type
+        try:
+            content_type = dc.get_prop(uri,"DAV:","getcontenttype")
+        except DAV_NotFound: content_type = "application/octet-stream"
 
-    def do_UNLOCK(self):
-        """ Always send OK with no content = Status 204 """
+        # get the data
+        try:
+            data = dc.get_data(uri)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+            return 
 
-        if self.DAV._config.DAV.verbose is True:
-            print >>sys.stderr, 'UNLOCKing resource %s' % self.headers
+        # send the data
+        if with_body is False:
+            data = None
 
-        return self.send_status(204)
+        self.send_body(data, '200', "OK", "OK", content_type, headers)
 
-    def do_PROPFIND(self):
+    def do_HEAD(self):
+        """ Send a HEAD response: Retrieves resource information w/o body """
 
-        dc=self.IFACE_CLASS
+        return self._HEAD_GET(with_body=False)
 
-        # read the body
-        body=None
-        if self.headers.has_key("Content-Length"):
-            l=self.headers['Content-Length']
-            body=self.rfile.read(atoi(l))
-        self.write_infp(body)
+    def do_GET(self):
+        """Serve a GET request."""
 
-        # which Depth?
-        if self.headers.has_key('Depth'):
-            d=self.headers['Depth']
-        else:
-            d="infinity"
+        return self._HEAD_GET(with_body=True)
 
-        uri=urlparse.urljoin(dc.baseuri,self.path)
-        uri=urllib.unquote(uri)
-        pf=PROPFIND(uri,dc,d)
+    def do_TRACE(self):
+        """ This will always fail because we can not reproduce HTTP requests. 
+        We send back a 405=Method Not Allowed. """
+
+        self.send_body(None, '405', 'Method Not Allowed', 'Method Not Allowed')
 
-        if body:    
-            pf.read_propfind(body)
+    def do_POST(self):
+        """ Replacement for GET response. Not implemented here. """
+
+        self.send_body(None, '405', 'Method Not Allowed', 'Method Not Allowed')
+
+    def do_PROPFIND(self):
+        """ Retrieve properties on defined resource. """
+
+        dc = self.IFACE_CLASS
+
+        # read the body containing the xml request
+        # iff there is no body then this is an ALLPROP request
+        body = None
+        if self.headers.has_key('Content-Length'):
+            l = self.headers['Content-Length']
+            body = self.rfile.read(atoi(l))
+
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
 
+        pf = PROPFIND(uri, dc, self.headers.get('Depth', 'infinity'), body)
 
         try:
-            DATA=pf.createResponse()
-            DATA=DATA+"\n"
+            DATA = '%s\n' % pf.createResponse()
         except DAV_Error, (ec,dd):
             return self.send_status(ec)
 
@@ -197,151 +224,203 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
         # taken from Resource.py @ Zope webdav
         if (self.headers.get('User-Agent') ==
             'Microsoft Data Access Internet Publishing Provider DAV 1.1'):
-            result = result.replace('<ns0:getlastmodified xmlns:ns0="DAV:">',
+            DATA = DATA.replace('<ns0:getlastmodified xmlns:ns0="DAV:">',
                                     '<ns0:getlastmodified xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">')
-            result = result.replace('<ns0:creationdate xmlns:ns0="DAV:">',
+            DATA = DATA.replace('<ns0:creationdate xmlns:ns0="DAV:">',
                                     '<ns0:creationdate xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.tz">')
 
-        self.send_body_chunks(DATA,'207','Multi-Status','Multiple responses')
+        self.send_body_chunks(DATA, '207','Multi-Status','Multiple responses')
 
-    def do_GET(self):
-        """Serve a GET request."""
+    def do_MKCOL(self):
+        """ create a new collection """
 
         dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri=urllib.unquote(uri)
 
-        # get the last modified date
         try:
-            lm=dc.get_prop(uri,"DAV:","getlastmodified")
-        except:
-            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
-        headers={"Last-Modified":lm}
-
-        # get the content type
-        try:
-            ct=dc.get_prop(uri,"DAV:","getcontenttype")
-        except:
-            ct="application/octet-stream"
-
-        # get the data
-        try:
-            data=dc.get_data(uri)
+            dc.mkcol(uri)
+            self.send_status(201)
         except DAV_Error, (ec,dd):
-            self.send_status(ec)
-            return 
-
-        # send the data
-        self.send_body(data,"200","OK","OK",ct,headers)
-
-    def do_HEAD(self):
-        """ Send a HEAD response """
-
-        dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(dc.baseuri,self.path)
-        uri=urllib.unquote(uri)
-
-        # get the last modified date
-        try:
-            lm=dc.get_prop(uri,"DAV:","getlastmodified")
-        except:
-            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
-
-        headers={"Last-Modified":lm}
-
-        # get the content type
-        try:
-            ct=dc.get_prop(uri,"DAV:","getcontenttype")
-        except:
-            ct="application/octet-stream"
+            return self.send_status(ec)
 
-        try:
-            data=dc.get_data(uri)
-            headers["Content-Length"]=str(len(data))
-        except DAV_NotFound:
-            self.send_body(None,"404","Not Found","")
-            return
+    def do_DELETE(self):
+        """ delete an resource """
 
-        self.send_body(None,"200","OK","OK",ct,headers)
+        dc = self.IFACE_CLASS
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
 
-    def do_POST(self):
-        self.send_error(404,"File not found")
+        # Handle If-Match
+        if self.headers.has_key('If-Match'):
+            test = False
+            etag = None
+            try:
+                etag = dc.get_prop(uri, "DAV:", "getetag")
+            except:
+                pass
+            for match in self.headers['If-Match'].split(','):
+                if match == '*':
+                    if dc.exists(uri):
+                        test = True
+                        break
+                else:
+                    if match == etag:
+                        test = True
+                        break
+            if not test:
+                self.send_status(412)
+                return
 
-    def do_MKCOL(self):
-        """ create a new collection """
+        # Handle If-None-Match
+        if self.headers.has_key('If-None-Match'):
+            test = True
+            etag = None
+            try:
+                etag = dc.get_prop(uri, "DAV:", "getetag")
+            except:
+                pass
+            for match in self.headers['If-None-Match'].split(','):
+                if match == '*':
+                    if dc.exists(uri):
+                        test = False
+                        break
+                else:
+                    if match == etag:
+                        test = False
+                        break
+            if not test:
+                self.send_status(412)
+                return
 
-        dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(dc.baseuri,self.path)
-        uri=urllib.unquote(uri)
-        try:
-            dc.mkcol(uri)
-            self.send_status(200)
-        except DAV_Error, (ec,dd):
-            self.send_status(ec)
+        # locked resources are not allowed to delete
+        if self._l_isLocked(uri):
+            return self.send_body(None, '423', 'Locked', 'Locked')
 
-    def do_DELETE(self):
-        """ delete an resource """
-        dc=self.IFACE_CLASS
-        uri=urlparse.urljoin(dc.baseuri,self.path)
-        uri=urllib.unquote(uri)
-        dl=DELETE(uri,dc)
+        dl = DELETE(uri,dc)
         if dc.is_collection(uri):
             res=dl.delcol()
-        else:
-            res=dl.delone()
+        else: res=dl.delone()
 
         if res:
             self.send_status(207,body=res)
-        else:
-            self.send_status(204)
+        else: self.send_status(204)
 
     def do_PUT(self):
         dc=self.IFACE_CLASS
 
+        # Handle If-Match
+        if self.headers.has_key('If-Match'):
+            test = False
+            etag = None
+            try:
+                etag = dc.get_prop(uri, "DAV:", "getetag")
+            except:
+                pass
+            for match in self.headers['If-Match'].split(','):
+                if match == '*':
+                    if dc.exists(uri):
+                        test = True
+                        break
+                else:
+                    if match == etag:
+                        test = True
+                        break
+            if not test:
+                self.send_status(412)
+                return
+
+        # Handle If-None-Match
+        if self.headers.has_key('If-None-Match'):
+            test = True
+            etag = None
+            try:
+                etag = dc.get_prop(uri, "DAV:", "getetag")
+            except:
+                pass
+            for match in self.headers['If-None-Match'].split(','):
+                if match == '*':
+                    if dc.exists(uri):
+                        test = False
+                        break
+                else:
+                    if match == etag:
+                        test = False
+                        break
+            if not test:
+                self.send_status(412)
+                return
+
+        # Handle expect
+        expect = self.headers.get('Expect', '')
+        if (expect.lower() == '100-continue' and
+                self.protocol_version >= 'HTTP/1.1' and
+                self.request_version >= 'HTTP/1.1'):
+            self.send_status(100)
+            self._flush()
+
         # read the body
         body=None
         if self.headers.has_key("Content-Length"):
             l=self.headers['Content-Length']
             body=self.rfile.read(atoi(l))
-        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
         uri=urllib.unquote(uri)
 
+        # locked resources are not allowed to be overwritten
+        if self._l_isLocked(uri):
+            return self.send_body(None, '423', 'Locked', 'Locked')
+
         ct=None
         if self.headers.has_key("Content-Type"):
             ct=self.headers['Content-Type']
         try:
-            dc.put(uri,body,ct)
+            location = dc.put(uri,body,ct)
         except DAV_Error, (ec,dd):
-            self.send_status(ec)
-            return
-        self.send_status(201)
+            return self.send_status(ec)
+
+        headers = {}
+        if location:
+            headers['Location'] = location
+
+        try:
+            etag = dc.get_prop(location or uri, "DAV:", "getetag")
+            headers['ETag'] = etag
+        except:
+            pass
+
+        self.send_body(None, '201', 'Created', '', headers=headers)
 
     def do_COPY(self):
         """ copy one resource to another """
         try:
             self.copymove(COPY)
         except DAV_Error, (ec,dd):
-            self.send_status(ec)
+            return self.send_status(ec)
 
     def do_MOVE(self):
         """ move one resource to another """
         try:
             self.copymove(MOVE)
         except DAV_Error, (ec,dd):
-            self.send_status(ec)
+            return self.send_status(ec)
 
     def copymove(self,CLASS):
         """ common method for copying or moving objects """
         dc=self.IFACE_CLASS
 
         # get the source URI
-        source_uri=urlparse.urljoin(dc.baseuri,self.path)
+        source_uri=urlparse.urljoin(self.get_baseuri(dc),self.path)
         source_uri=urllib.unquote(source_uri)
 
         # get the destination URI
         dest_uri=self.headers['Destination']
         dest_uri=urllib.unquote(dest_uri)
 
+        # check locks on source and dest
+        if self._l_isLocked(source_uri) or self._l_isLocked(dest_uri):
+            return self.send_body(None, '423', 'Locked', 'Locked')
+
         # Overwrite?
         overwrite=1
         result_code=204
@@ -382,7 +461,8 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
                 return
 
         if res:
-            self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
+            self.send_body_chunks(res,207,self.responses[207][0],
+                            self.responses[207][1],
                             ctype='text/xml; charset="utf-8"')
         else:
             self.send_status(result_code)
@@ -395,6 +475,13 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
     def send_status(self,code=200,mediatype='text/xml;  charset="utf-8"', \
                                 msg=None,body=None):
 
-        if not msg: msg=STATUS_CODES[code]
-        self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
+        if not msg: msg=self.responses[code][1]
+        self.send_body(body,code,self.responses[code][0],msg,mediatype)
 
+    def get_baseuri(self, dc):
+        baseuri = dc.baseuri
+        if self.headers.has_key('Host'):
+            uparts = list(urlparse.urlparse(dc.baseuri))
+            uparts[1] = self.headers['Host']
+            baseuri = urlparse.urlunparse(uparts)
+        return baseuri
diff --git a/DAV/constants.py b/DAV/constants.py
index e858ac5..2b1d796 100644
--- a/DAV/constants.py
+++ b/DAV/constants.py
@@ -1,18 +1,24 @@
-"""
-
-constants definition
-
-
-"""
-
 # definition for resourcetype
 COLLECTION=1
 OBJECT=None
-DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
 
+# attributes for resources
+DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
 
 # Request classes in propfind
-
 RT_ALLPROP=1
 RT_PROPNAME=2
 RT_PROP=3
+
+# server mode
+DAV_VERSION_1 = {
+        'version' : '1',
+        'options' : 
+        'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE'
+}
+
+DAV_VERSION_2 = {
+        'version' : '1,2',
+        'options' : 
+        DAV_VERSION_1['options'] + ', LOCK, UNLOCK'
+}
diff --git a/DAV/davcmd.py b/DAV/davcmd.py
index 249b88a..cd2f418 100644
--- a/DAV/davcmd.py
+++ b/DAV/davcmd.py
@@ -169,7 +169,6 @@ def copytree(dc,src,dst,overwrite=None):
         esrc=replace(element,src,"")
         actual_dst=dpath+esrc
 
-        print actual_dst
         # now copy stuff
         try:
             copy(dc,element,actual_dst)
diff --git a/DAV/delete.py b/DAV/delete.py
index ed017e5..4f3e4eb 100644
--- a/DAV/delete.py
+++ b/DAV/delete.py
@@ -26,7 +26,6 @@ import string
 import urllib
 from StringIO import StringIO
 
-from status import STATUS_CODES
 from utils import gen_estring, quote_uri, make_xmlresponse
 from davcmd import deltree
 
diff --git a/DAV/iface.py b/DAV/iface.py
index 1d06f16..5f3bda7 100644
--- a/DAV/iface.py
+++ b/DAV/iface.py
@@ -7,6 +7,8 @@ class.
 
 """
 
+from xml.dom import minidom
+from locks import LockManager
 from errors import *
 
 import time
@@ -93,12 +95,36 @@ class dav_interface:
     def put(self,uri,data):
         """ write an object to the repository 
 
-        return a result code or raise an exception
+        return the location uri or raise an exception
         """
 
         raise DAV_Forbidden
 
     ###
+    ### LOCKing information
+    ###
+    def _get_dav_supportedlock(self, uri):
+
+        txt = ('<main xmlns:D="http://dummy/d" xmlns:ns1="http://webdav.de/ns1"><D:lockentry>\n'
+                '<D:lockscope><D:exclusive></D:exclusive></D:lockscope>\n'
+                '<D:locktype><D:write></D:write></D:locktype>\n'
+                '</D:lockentry></main>\n')
+        xml = minidom.parseString(txt)
+        return xml.firstChild.firstChild
+
+    def _get_dav_lockdiscovery(self, uri):
+        lcm = LockManager()
+        if lcm._l_isLocked(uri):
+            lock = lcm._l_getLockForUri(uri)
+            txt = lock.asXML(discover=True, namespace='D')
+
+            txtwithns = '<main xmlns:D="http://dummy/D" xmlns:n="http://webdav.de/N">%s</main>'
+            xml = minidom.parseString(txtwithns % txt)
+            return xml.firstChild.firstChild
+
+        return ''
+
+    ###
     ### Methods for DAV properties
     ###
 
diff --git a/DAV/locks.py b/DAV/locks.py
new file mode 100644
index 0000000..0badcc9
--- /dev/null
+++ b/DAV/locks.py
@@ -0,0 +1,240 @@
+
+import os
+import sys
+import time
+import socket
+import string
+import posixpath
+import base64
+import urlparse
+import urllib
+import random
+
+from xml.dom import minidom
+
+from utils import rfc1123_date, IfParser, tokenFinder
+from string import atoi,split
+from errors import *
+
+tokens_to_lock = {}
+uris_to_token = {}
+
+class LockManager:
+    """ Implements the locking backend and serves as MixIn for DAVRequestHandler """
+
+    def _init_locks(self):
+        return tokens_to_lock, uris_to_token
+
+    def _l_isLocked(self, uri):
+        tokens, uris = self._init_locks()
+        return uris.has_key(uri)
+
+    def _l_hasLock(self, token):
+        tokens, uris = self._init_locks()
+        return tokens.has_key(token)
+
+    def _l_getLockForUri(self, uri):
+        tokens, uris = self._init_locks()
+        return uris.get(uri, None)
+
+    def _l_getLock(self, token):
+        tokens, uris = self._init_locks()
+        return tokens.get(token, None)
+
+    def _l_delLock(self, token):
+        tokens, uris = self._init_locks()
+        if tokens.has_key(token):
+            del uris[tokens[token].uri]
+            del tokens[token]
+
+    def _l_setLock(self, lock):
+        tokens, uris = self._init_locks()
+        tokens[lock.token] = lock
+        uris[lock.uri] = lock
+
+    def _lock_unlock_parse(self, body):
+        doc = minidom.parseString(body)
+
+        data = {}
+        info = doc.getElementsByTagNameNS('DAV:', 'lockinfo')[0]
+        data['lockscope'] = info.getElementsByTagNameNS('DAV:', 'lockscope')[0]\
+                                .firstChild.localName
+        data['locktype'] = info.getElementsByTagNameNS('DAV:', 'locktype')[0]\
+                                .firstChild.localName
+        data['lockowner'] = info.getElementsByTagNameNS('DAV:', 'owner')
+        return data
+
+    def _lock_unlock_create(self, uri, creator, depth, data):
+        lock = LockItem(uri, creator, **data)
+        iscollection = uri[-1] == '/' # very dumb collection check
+
+        result = ''
+        if depth == 'infinity' and iscollection:
+            # locking of children/collections not yet supported
+            pass
+
+        if not self._l_isLocked(uri):
+            self._l_setLock(lock)
+
+        # because we do not handle children we leave result empty
+        return lock.token, result
+
+    def do_UNLOCK(self):
+        """ Unlocks given resource """
+
+        dc = self.IFACE_CLASS
+
+        if self._config.DAV.verbose is True:
+            print >>sys.stderr, 'UNLOCKing resource %s' % self.headers
+
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
+
+        token = tokenFinder(self.headers.get('Lock-Token'))
+        if self._l_isLocked(uri):
+            self._l_delLock(token)
+        
+        self.send_body(None, '204', 'Ok', 'Ok')
+
+    def do_LOCK(self):
+        """ Locking is implemented via in-memory caches. No data is written to disk.  """
+
+        dc = self.IFACE_CLASS
+
+        if self._config.DAV.verbose is True:
+            print >>sys.stderr, 'LOCKing resource %s' % self.headers
+
+        body = None
+        if self.headers.has_key('Content-Length'):
+            l = self.headers['Content-Length']
+            body = self.rfile.read(atoi(l))
+
+        depth = self.headers.get('Depth', 'infinity')
+
+        uri = urlparse.urljoin(self.get_baseuri(dc), self.path)
+        uri = urllib.unquote(uri)
+
+        ifheader = self.headers.get('If')
+        alreadylocked = self._l_isLocked(uri)
+
+        if body and alreadylocked:
+            # Full LOCK request but resource already locked
+            self.responses[423] = ('Locked', 'Already locked')
+            return self.send_status(423)
+        
+        elif body and not ifheader:
+            # LOCK with XML information
+            data = self._lock_unlock_parse(body)
+            token, result = self._lock_unlock_create(uri, 'unknown', depth, data)
+
+            if result:
+                self.send_body(result, '207', 'Error', 'Error', 
+                                'text/xml; charset="utf-8"')
+
+            else:
+                lock = self._l_getLock(token)
+                self.send_body(lock.asXML(), '200', 'OK', 'OK', 
+                                'text/xml; charset="utf-8"',
+                                {'Lock-Token' : 'opaquelocktoken:%s' % token})
+
+
+        else:
+            # refresh request - refresh lock timeout
+            taglist = IfParser(ifheader)
+            found = 0
+            for tag in taglist:
+                for listitem in tag.list:
+                    token = tokenFinder(listitem)
+                    if token and self._l_hasLock(token):
+                        lock = self._l_getLock(token)
+                        timeout = self.headers.get('Timeout', 'Infinite')
+                        lock.setTimeout(timeout) # automatically refreshes
+                        found = 1
+
+                        self.send_body(lock.asXML(), 
+                                        '200', 'OK', 'OK', 'text/xml; encoding="utf-8"')
+                        break
+                if found: 
+                    break
+
+            # we didn't find any of the tokens mentioned - means
+            # that table was cleared or another error
+            if not found:
+                self.send_status(412) # precondition failed
+
+class LockItem:
+    """ Lock with support for exclusive write locks. Some code taken from
+    webdav.LockItem from the Zope project. """
+
+    def __init__(self, uri, creator, lockowner, depth=0, timeout='Infinite', 
+                    locktype='write', lockscope='exclusive', token=None, **kw):
+
+        self.uri = uri
+        self.creator = creator
+        self.owner = lockowner
+        self.depth = depth
+        self.timeout = timeout
+        self.locktype = locktype
+        self.lockscope = lockscope
+        self.token = token and token or self.generateToken()
+        self.modified = time.time()
+
+    def getModifiedTime(self):
+        return self.modified
+
+    def refresh(self):
+        self.modified = time.time()
+
+    def isValid(self):
+        now = time.time()
+        modified = self.modified
+        timeout = self.timeout
+        return (modified + timeout) > now
+
+    def generateToken(self):
+        _randGen = random.Random(time.time())
+        return '%s-%s-00105A989226:%.03f' %  \
+             (_randGen.random(),_randGen.random(),time.time())
+
+    def getTimeoutString(self):
+        t = str(self.timeout)
+        if t[-1] == 'L': t = t[:-1]
+        return 'Second-%s' % t
+
+    def setTimeout(self, timeout):
+        self.timeout = timeout
+        self.modified = time.time()
+
+    def asXML(self, namespace='d', discover=False):
+        token = self.token
+        base = ('<%(ns)s:activelock>\n'
+             '  <%(ns)s:locktype><%(ns)s:%(locktype)s/></%(ns)s:locktype>\n'
+             '  <%(ns)s:lockscope><%(ns)s:%(lockscope)s/></%(ns)s:lockscope>\n'
+             '  <%(ns)s:depth>%(depth)s</%(ns)s:depth>\n'
+             '  <%(ns)s:owner>%(owner)s</%(ns)s:owner>\n'
+             '  <%(ns)s:timeout>%(timeout)s</%(ns)s:timeout>\n'
+             '  <%(ns)s:locktoken>\n'
+             '   <%(ns)s:href>opaquelocktoken:%(locktoken)s</%(ns)s:href>\n'
+             '  </%(ns)s:locktoken>\n'
+             ' </%(ns)s:activelock>\n'
+             ) % {
+               'ns': namespace,
+               'locktype': self.locktype,
+               'lockscope': self.lockscope,
+               'depth': self.depth,
+               'owner': self.owner and self.owner or '',
+               'timeout': self.getTimeoutString(),
+               'locktoken': token,
+               }
+
+        if discover is True:
+            return base
+
+        s = """<?xml version="1.0" encoding="utf-8" ?>
+<d:prop xmlns:d="DAV:">
+ <d:lockdiscovery>
+  %s
+ </d:lockdiscovery>
+</d:prop>""" % base
+
+        return s
diff --git a/DAV/propfind.py b/DAV/propfind.py
index 5796db4..4c68cd6 100644
--- a/DAV/propfind.py
+++ b/DAV/propfind.py
@@ -1,25 +1,3 @@
-#!/usr/bin/env python
-
-"""
-    python davserver
-    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library 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
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-"""
-
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
@@ -49,8 +27,7 @@ class PROPFIND:
     
     """
 
-
-    def __init__(self,uri,dataclass,depth):
+    def __init__(self,uri,dataclass,depth, body):
         self.request_type=None
         self.nsmap={}
         self.proplist={}
@@ -63,11 +40,12 @@ class PROPFIND:
         if dataclass.verbose:
             print >>sys.stderr, 'PROPFIND: Depth is %s, URI is %s' % (depth, uri)
 
-    def read_propfind(self,xml_doc):
-        self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
+        if body:
+            self.request_type, self.proplist, self.namespaces = utils.parse_propfind(body)
+            self.__has_body = True
 
     def createResponse(self):
-        """ create the multistatus response 
+        """ Create the multistatus response 
 
         This will be delegated to the specific method
         depending on which request (allprop, propname, prop)
@@ -81,12 +59,16 @@ class PROPFIND:
 
         """
 
+        # check if resource exists
+        if not self.__dataclass.exists(self.__uri):
+            raise DAV_NotFound
+
         df = None
         if self.request_type==RT_ALLPROP:
             df = self.create_allprop()
         
         if self.request_type==RT_PROPNAME:
-            df =  self.create_propname()
+            df = self.create_propname()
         
         if self.request_type==RT_PROP:
             df = self.create_prop()
@@ -118,10 +100,10 @@ class PROPFIND:
             re=self.mk_propname_response(self.__uri,pnames, doc)
             ms.appendChild(re)
         
-        for newuri in dc.get_childs(self.__uri):
-            pnames=dc.get_propnames(newuri)
-            re=self.mk_propname_response(newuri,pnames, doc)
-            ms.appendChild(re)
+            for newuri in dc.get_childs(self.__uri):
+                pnames=dc.get_propnames(newuri)
+                re=self.mk_propname_response(newuri,pnames, doc)
+                ms.appendChild(re)
 
         # *** depth=="infinity"  
 
@@ -176,10 +158,10 @@ class PROPFIND:
             res=self.mk_prop_response(self.__uri,gp,bp,doc)
             ms.appendChild(res)
         
-        for newuri in self.__dataclass.get_childs(self.__uri):
-            gp,bp=self.get_propvalues(newuri)
-            res=self.mk_prop_response(newuri,gp,bp,doc)
-            ms.appendChild(res)
+            for newuri in self.__dataclass.get_childs(self.__uri):
+                gp,bp=self.get_propvalues(newuri)
+                res=self.mk_prop_response(newuri,gp,bp,doc)
+                ms.appendChild(res)
 
         return doc.toxml(encoding="utf-8")
 
@@ -252,14 +234,18 @@ class PROPFIND:
         for ns in good_props.keys():
             ns_prefix="ns"+str(self.namespaces.index(ns))+":"
             for p,v in good_props[ns].items():
+
                 pe=doc.createElement(ns_prefix+str(p))
-                if p=="resourcetype":
-                    if v=="1":
-                        ve=doc.createElement("D:collection")
-                        pe.appendChild(ve)
+                if hasattr(v, '__class__') and v.__class__.__name__ == 'Element':
+                    pe.appendChild(v)
                 else:
-                    ve=doc.createTextNode(str(v))
-                    pe.appendChild(ve)
+                    if p=="resourcetype":
+                        if v=="1":
+                            ve=doc.createElement("D:collection")
+                            pe.appendChild(ve)
+                    else:
+                        ve=doc.createTextNode(str(v))
+                        pe.appendChild(ve)
 
                 gp.appendChild(pe)
         
@@ -316,7 +302,11 @@ class PROPFIND:
                 ec = 0
                 try:
                     r=ddc.get_prop(uri,ns,prop)
-                    good_props[ns][prop]=str(r)
+
+                    # support for element returns
+                    if hasattr(r, '__class__') and r.__class__.__name__ == 'Element':
+                        good_props[ns][prop]=r
+                    else: good_props[ns][prop]=str(r)
                 except DAV_Error, error_code:
                     ec=error_code[0]
                 
diff --git a/DAV/status.py b/DAV/status.py
index 47e1b32..f719987 100644
--- a/DAV/status.py
+++ b/DAV/status.py
@@ -7,6 +7,7 @@ status codes for DAV services
 
 
 STATUS_CODES={
+        100:    "Continue",
         102:    "Processing",
         200:    "Ok",
         201:    "Created",
diff --git a/DAV/utils.py b/DAV/utils.py
index 45fa792..8150e5b 100755
--- a/DAV/utils.py
+++ b/DAV/utils.py
@@ -1,44 +1,29 @@
-#!/usr/bin/env python
-
-"""
-
-UTILITIES
-
-- parse a propfind request body into a list of props
-
-"""
-
+import time
+import re
 
 from xml.dom import minidom
 
-from string import lower, split, atoi, joinfields
 import urlparse
+from string import lower, split, atoi, joinfields
 from StringIO import StringIO
 
 from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
-from status import STATUS_CODES
+from BaseHTTPServer import BaseHTTPRequestHandler
 
-VERSION = '0.9-dev'
+VERSION = '0.9.2'
 AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
 
-
 def gen_estring(ecode):
     """ generate an error string from the given code """
     ec=atoi(str(ecode))
-    if STATUS_CODES.has_key(ec):
-        return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
+    if BaseHTTPRequestHandler.responses.has_key(ec):
+        return "HTTP/1.1 %s %s" %(ec, BaseHTTPRequestHandler.responses[ec][0])
     else:
         return "HTTP/1.1 %s" %(ec)
 
 def parse_propfind(xml_doc):
-    """ parse an propfind xml file and return a list of props 
-
-    returns:
-        
-        request_type            -- ALLPROP, PROPNAME, PROP
-        proplist            -- list of properties found
-        namespaces            -- list of namespaces found
-    
+    """ 
+    Parse an propfind xml file and return a list of props 
     """
 
     doc = minidom.parseString(xml_doc)
@@ -146,3 +131,97 @@ def make_xmlresponse(result):
         doc.documentElement.appendChild(re)
 
     return doc.toxml(encoding="utf-8")
+
+# taken from App.Common
+
+weekday_abbr = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+weekday_full = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
+                'Friday', 'Saturday', 'Sunday']
+monthname    = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+def rfc1123_date(ts=None):
+    # Return an RFC 1123 format date string, required for
+    # use in HTTP Date headers per the HTTP 1.1 spec.
+    # 'Fri, 10 Nov 2000 16:21:09 GMT'
+    if ts is None: ts=time.time()
+    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(ts)
+    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (weekday_abbr[wd],
+                                                    day, monthname[month],
+                                                    year,
+                                                    hh, mm, ss)
+def iso8601_date(ts=None):
+    # Return an ISO 8601 formatted date string, required
+    # for certain DAV properties.
+    # '2000-11-10T16:21:09-08:00
+    if ts is None: ts=time.time()
+    return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(ts))
+
+def rfc850_date(ts=None):
+    # Return an HTTP-date formatted date string.
+    # 'Friday, 10-Nov-00 16:21:09 GMT'
+    if ts is None: ts=time.time()
+    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(ts)
+    return "%s, %02d-%3s-%2s %02d:%02d:%02d GMT" % (
+            weekday_full[wd],
+            day, monthname[month],
+            str(year)[2:],
+            hh, mm, ss)
+
+### If: header handling support.  IfParser returns a sequence of
+### TagList objects in the order they were parsed which can then
+### be used in WebDAV methods to decide whether an operation can
+### proceed or to raise HTTP Error 412 (Precondition failed)
+IfHdr = re.compile(
+    r"(?P<resource><.+?>)?\s*\((?P<listitem>[^)]+)\)"
+    )
+
+ListItem = re.compile(
+    r"(?P<not>not)?\s*(?P<listitem><[a-zA-Z]+:[^>]*>|\[.*?\])",
+    re.I)
+
+class TagList:
+    def __init__(self):
+        self.resource = None
+        self.list = []
+        self.NOTTED = 0
+
+def IfParser(hdr):
+    out = []
+    i = 0
+    while 1:
+        m = IfHdr.search(hdr[i:])
+        if not m: break
+
+        i = i + m.end()
+        tag = TagList()
+        tag.resource = m.group('resource')
+        if tag.resource:                # We need to delete < >
+            tag.resource = tag.resource[1:-1]
+        listitem = m.group('listitem')
+        tag.NOTTED, tag.list = ListParser(listitem)
+        out.append(tag)
+
+    return out
+
+def tokenFinder(token):
+    # takes a string like '<opaquelocktoken:afsdfadfadf> and returns the token
+    # part.
+    if not token: return None           # An empty string was passed in
+    if token[0] == '[': return None     # An Etag was passed in
+    if token[0] == '<': token = token[1:-1]
+    return token[token.find(':')+1:]
+
+def ListParser(listitem):
+    out = []
+    NOTTED = 0
+    i = 0
+    while 1:
+        m = ListItem.search(listitem[i:])
+        if not m: break
+
+        i = i + m.end()
+        out.append(m.group('listitem'))
+        if m.group('not'): NOTTED = 1
+
+    return NOTTED, out
diff --git a/DAVServer/fshandler.py b/DAVServer/fshandler.py
index b6a0f65..df6adaa 100644
--- a/DAVServer/fshandler.py
+++ b/DAVServer/fshandler.py
@@ -184,7 +184,8 @@ class FilesystemHandler(dav_interface):
     def mkcol(self,uri):
         """ create a new collection """
         path=self.uri2local(uri)
-        # remove leading slash
+
+        # remove trailing slash
         if path[-1]=="/": path=path[:-1]
 
         # test if file already exists
diff --git a/DAVServer/server.py b/DAVServer/server.py
index a018c8b..0b2801b 100755
--- a/DAVServer/server.py
+++ b/DAVServer/server.py
@@ -115,10 +115,8 @@ Parameters:
     -m, --mysql     Pass this parameter if you want MySQL based authentication.
                     If you want to use MySQL then the usage of a configuration
                     file is mandatory.
-    -J, --lockemu   Activate experimental lock and unlock emulation. Currently
-                    no real locking is done. It is only to satisfy clients
-                    needing DAV 2 compliant server for read/write access 
-                    (Mac OS X Finder).
+    -J, --lockemu   Activate experimental LOCK and UNLOCK mode (WebDAV Version 2).
+                    Currently know to work but needs more tests.
     -i, --icounter  If you want to run multiple instances then you have to
                     give each instance it own number so that logfiles and such
                     can be identified. Default is 0
diff --git a/PKG-INFO b/PKG-INFO
index 3cf5c0e..d95eda0 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.1
+Version: 0.9.2
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -8,13 +8,24 @@ Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
         WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+        
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
-        Example ::
-        davserver -D /home/files -n
+        Example (using easy_install)::
+        
+        easy_install PyWebDAV
+        davserver -D /tmp -n
+        
+        Example (unpacking file locally)::
+        
+        tar xvzf PyWebDAV-$VERSION.tar.gz
+        cd pywebdav
+        python setup.py develop
+        davserver -D /tmp -n
         
-        For more information go to http://code.google.com/p/pywebdav/
+        For more information: http://code.google.com/p/pywebdav/
         
 Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
 Platform: Unix
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
index 3cf5c0e..d95eda0 100644
--- a/PyWebDAV.egg-info/PKG-INFO
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: PyWebDAV
-Version: 0.9.1
+Version: 0.9.2
 Summary: WebDAV library including a standalone server for python
 Home-page: http://code.google.com/p/pywebdav/
 Author: Simon Pamies
@@ -8,13 +8,24 @@ Author-email: spamsch at gmail.com
 License: GPL v2
 Description: 
         WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+        
         After installation of this package you will have a new script in you $PYTHON/bin directory called
         *davserver*. This serves as the main entry point to the server.
         
-        Example ::
-        davserver -D /home/files -n
+        Example (using easy_install)::
+        
+        easy_install PyWebDAV
+        davserver -D /tmp -n
+        
+        Example (unpacking file locally)::
+        
+        tar xvzf PyWebDAV-$VERSION.tar.gz
+        cd pywebdav
+        python setup.py develop
+        davserver -D /tmp -n
         
-        For more information go to http://code.google.com/p/pywebdav/
+        For more information: http://code.google.com/p/pywebdav/
         
 Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
 Platform: Unix
diff --git a/PyWebDAV.egg-info/SOURCES.txt b/PyWebDAV.egg-info/SOURCES.txt
index ce7f1ae..4c55b7f 100644
--- a/PyWebDAV.egg-info/SOURCES.txt
+++ b/PyWebDAV.egg-info/SOURCES.txt
@@ -16,6 +16,7 @@ DAV/dbconn.py
 DAV/delete.py
 DAV/errors.py
 DAV/iface.py
+DAV/locks.py
 DAV/propfind.py
 DAV/propfind.py.orig
 DAV/status.py
diff --git a/README b/README
index 22bc1af..cfcd9be 100644
--- a/README
+++ b/README
@@ -1,129 +1,92 @@
+PyWebDAV is a standards compliant WebDAV server and library written in Python
 
-Python WebDAV implementation.
 
-A working WebDAV Server is included in the PyDAVServer package. Feel free to
-extend it to your needs. Please send suggestions and bugfixes to the author(s).
+DESCRIPTION
+-----------
 
-LICENSE
--------
+Python WebDAV implementation (level 1 and 2) that features a library that enables you
+to integrate WebDAV server capabilities to your application
 
-see doc/LICENSE file
+A fully working example on how to use the library is included. You can find the server in the DAVServer package. Upon installation a script called davserver is created in your $PYTHON/bin directory.
 
+If you search an easy to use WebDAV server that supports most clients (cadaver, Mac OS X Finder, Windows Explorer, ...) then try out PyWebDAV.
 
-AUTHOR(s)
-------
-
-Simon Pamies (also current maintainer)
-Bielefeld, Germany
-s.pamies at banality.de
-
-Christian Scholz
-Aachen, Germany
-mrtopf at webdav.de
 
-Vince Spicer
-Ontario, Canada
-vince at vince.ca
+INSTALLATION
+------------
 
+Installation and setup of server can be as easy as follows:
 
-REQUIREMENTS
-------------
+$ easy_install PyWebDAV 
+$ davserver -D /tmp -n -J
 
-- Python 2.0  or higher (www.python.org)
-- PyXML 0.66 (pyxml.sourceforge.net)
+If you're living on the bleeding edge then check out the sourcecode from
+http://code.google.com/p/pywebdav/source/checkout
 
-OPTIONAL
---------
+After having downloaded code simply install a development egg:
 
-- MySQLdb (http://sourceforge.net/projects/mysql-python)
-	- Mysql server 4.0+ for Mysql authentication with
-	  with read/write access to one database
+$ svn co svn checkout http://pywebdav.googlecode.com/svn/trunk/ pywebdav
+$ cd pywebdav
+$ python setup.py develop
+$ davserver --help
 
-INSTALLATION
-------------
+Send patches to http://code.google.com/p/pywebdav/issues/list
 
-see doc/INSTALL file
+If you want to use the library then have a look at the DAVServer package that
+holds all code for a full blown server. Also doc/ARCHITECURE has information for you.
 
-What is WebDAV?
----------------
 
-http://www.webdav.org.
-http://www.ietf.org/rfc/rfc2518.txt
+QUESTIONS?
+---------
 
-What is Python?
----------------
+Ask here http://groups.google.com/group/pywebdav
+or send an email to the maintainer.
 
-http://www.python.org.
-http://diveintopython.org/
 
-Contents
---------
+REQUIREMENTS
+------------
 
-Here is a little overview of the package:
+- Python 2.4  or higher (www.python.org)
+- PyXML 0.66 (pyxml.sourceforge.net)
 
-A. In the DAV/ package:
 
-    1. BufferingHTTPServer
-       
-       This is the same as the normal BasicHTTPServer but instead of
-       directly sending each string over the network this implementation
-       caches it and sends it at once after finishing one request.
+LICENSE
+-------
 
-       This has the advantage that clients like cadaver don't break as
-       they want to peek at the next lines when encountering e.g. a header.
+General Public License v2
+see doc/LICENSE
 
-    2. AuthHTTPServer
-       
-       This works on top of either the BasicHTTPServer or the 
-       BufferingHTTPServer and implements basic authentication.
 
-    3. WebDAVServer
-       This server uses AuthHTTPServer for the base functionality. It also uses
-       a dav interface class for interfacing with the actual data storage (e.g.
-       a filesystem or a database).
+AUTHOR(s)
+------
 
-B. In the PyDAVServer directory:
+Simon Pamies [*]
+Bielefeld, Germany
+s.pamies at banality.de
 
-    1. server.py
-       Main file for server. Serves as a start point for the WebDAV server
+Christian Scholz
+Aachen, Germany
+mrtopf at webdav.de
 
-    2. fshandler.py
-       Backend for the DAV server. Makes him serving content from the filesystem.
-       Have a deeper look at it if you want to implement backends to other data sources
+Vince Spicer
+Ontario, Canada
+vince at vince.ca
 
-    3. fileauth.py
-       Handler for authentication. Nothing very special about it.
+[*]: Current Maintainer
 
-    4. dbconn.py
-       Mysql Database Handler.
 
-    5. INI_Parse.py
-       Parses the config.ini file.
+OPTIONAL
+--------
 
-    6. config.ini
-       PyWebDav configuration file.
+- MySQLdb (http://sourceforge.net/projects/mysql-python)
+	- Mysql server 4.0+ for Mysql authentication with
+	  with read/write access to one database
 
 
 NOTES
 -----
 
-Right now some parts are missing like handling of ALLPROP and PROPNAMES as 
-the whole proppatch method. Also not everything is tested and the return 
-codes might not be the right ones in every case.
-
-I plan to adjust things in order to work with my groupware project which 
-I then will release (actually it's my thesis I am working on). 
-
-Look inside the file TODO for things which needs to be done and will be done 
+Look inside the file doc/TODO for things which needs to be done and will be done 
 in the near future. 
 
-If you find bugs (many!) or have suggestions then please email the maintainer
-
-Thanks :)
-
-
-Windows Notes
--------------
-
-This version can not be run under windows.
-
+Have a look at doc/ARCHITECTURE to understand what's going on under the hood
diff --git a/VERSION b/VERSION
index f374f66..2003b63 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.1
+0.9.2
diff --git a/doc/ARCHITECTURE b/doc/ARCHITECTURE
index 39348b1..b683365 100644
--- a/doc/ARCHITECTURE
+++ b/doc/ARCHITECTURE
@@ -1,3 +1,52 @@
+
+OVERVIEW
+--------
+
+Here is a little overview of the package:
+
+A. In the DAV/ package:
+
+    1. BufferingHTTPServer
+       
+       This is the same as the normal BasicHTTPServer but instead of
+       directly sending each string over the network this implementation
+       caches it and sends it at once after finishing one request.
+
+       This has the advantage that clients like cadaver don't break as
+       they want to peek at the next lines when encountering e.g. a header.
+
+    2. AuthHTTPServer
+       
+       This works on top of either the BasicHTTPServer or the 
+       BufferingHTTPServer and implements basic authentication.
+
+    3. WebDAVServer
+       This server uses AuthHTTPServer for the base functionality. It also uses
+       a dav interface class for interfacing with the actual data storage (e.g.
+       a filesystem or a database).
+
+B. In the PyDAVServer directory:
+
+    1. server.py
+       Main file for server. Serves as a start point for the WebDAV server
+
+    2. fshandler.py
+       Backend for the DAV server. Makes him serving content from the filesystem.
+       Have a deeper look at it if you want to implement backends to other data sources
+
+    3. fileauth.py
+       Handler for authentication. Nothing very special about it.
+
+    4. dbconn.py
+       Mysql Database Handler.
+
+    5. INI_Parse.py
+       Parses the config.ini file.
+
+    6. config.ini
+       PyWebDav configuration file.
+
+
 Class Tree
 ----------
 
diff --git a/doc/Changes b/doc/Changes
index 119ab9f..0eb5f39 100644
--- a/doc/Changes
+++ b/doc/Changes
@@ -1,4 +1,42 @@
 
+- Fixed COPY, MOVE, DELETE to support locked
+  resources
+  [Simon Pamies]
+
+- Fixed PROPFIND to return 404 for non existing
+  objects and also reduce property bloat
+  [Simon Pamies]
+
+- Implemented fully working LOCK and UNLOCK based
+  on in memory lock/token database. Now fully supports
+  cadaver and Mac OS X Finder.
+  [Simon Pamies]
+
+- Fixed MKCOL answer to 201
+  [Jesus Cea]
+
+- Fixed MSIE webdav headers
+  [Jesus Cea]
+
+- Make propfind respect the depth from queries
+  [Cédric Krier]
+
+- Add ETag in the header of GET. This is needed to implement 
+  GroupDAV, CardDAV and CalDAV.
+  [Cédric Krier]
+
+- Handle the "Expect 100-continue" header
+  [Cédric Krier]
+
+- Remove debug statements and remove logging
+  [Cédric Krier]
+
+- Use the Host header in baseuri if set.
+  [Cédric Krier]
+
+- Adding If-Match on PUT and DELETE
+  [Cédric Krier]
+
 0.9.1 (May 4th 2009)
 --
 
diff --git a/doc/INSTALL b/doc/INSTALL
index ecce054..9e167e1 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -4,14 +4,14 @@ How to install python WebDAV server
 1. Check prerequisites
 
     + *nix OS (including Mac OS X)
-      Will not run on Windows!
+      Windows seems to work
       
-    + Python 2.x
+    + Python >=2.4.x
     + PyXML
 
 2. Run setup.py
 
-    > sudo python setup.py install
+    $ python setup.py install
 
 4. Change to the PyDAVServer directory and start server with 
 
diff --git a/doc/TODO b/doc/TODO
index 8fcde44..fa66e2a 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -25,13 +25,7 @@ PROPFIND
 GET
 ---
 
-- guessing type?
-
-
-HEAD
-----
-
-to be done
+- guessing mimetype?
 
 
 PROPPATCH
@@ -40,10 +34,3 @@ PROPPATCH
 to be done (not that important as only DAV props are supported right now which
 you cannot change)
 
-data.py
--------
-
-- implement proppatch? As example we might use some sort of database for 
-  storing..  (to much trouble for a simple example? Better show it with 
-  the groupware stuff?)
-
diff --git a/setup.py b/setup.py
index abc91bb..cef371b 100644
--- a/setup.py
+++ b/setup.py
@@ -13,13 +13,24 @@ VERSION = VERSION.replace('\n', '')
 
 DOC = """
 WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+Currently supports WebDAV level 1 and level 2 (LOCK, UNLOCK) making it play nice with cadaver, Mac OS X Finder, Windows Explorer or even iCal.
+
 After installation of this package you will have a new script in you $PYTHON/bin directory called
 *davserver*. This serves as the main entry point to the server.
 
-Example ::
-    davserver -D /home/files -n
+Example (using easy_install)::
+
+    easy_install PyWebDAV
+    davserver -D /tmp -n
+
+Example (unpacking file locally)::
+
+    tar xvzf PyWebDAV-$VERSION.tar.gz
+    cd pywebdav
+    python setup.py develop
+    davserver -D /tmp -n
 
-For more information go to http://code.google.com/p/pywebdav/
+For more information: http://code.google.com/p/pywebdav/
 """
 
 from distutils.core import setup
commit 3c58ac269eb29a220955396df6f319b8281b7c31
Author: Daniel Baumann <daniel at debian.org>
Date:   Tue May 12 21:53:53 2009 +0200

    Adding upstream version 0.9.1.

diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
old mode 100755
new mode 100644
index 131da36..ba771e2
--- a/DAV/AuthServer.py
+++ b/DAV/AuthServer.py
@@ -121,7 +121,9 @@ class AuthRequestHandler:
                 m,up=string.split(a)
                 up2=base64.decodestring(up)
                 user,pw=string.split(up2,":")
-                if not self.get_userinfo(user,pw):
+
+                # Check if the given user can access
+                if not self.get_userinfo(user,pw,command):
                     self.send_autherror(401,"Authorization Required"); return
             except:
                 self.send_autherror(401,"Authorization Required")
@@ -209,9 +211,9 @@ class AuthRequestHandler:
         for l in lines:
             self._append("%s\r\n" %l)
 
-    def get_userinfo(self,user):
-        """ return the password of the user 
-        Override this class to return the right password 
+    def get_userinfo(self,user, password, command):
+        """Checks if the given user and the given
+        password are allowed to access.
         """
         
         # Always reject
diff --git a/DAV/INI_Parse.py b/DAV/INI_Parse.py
new file mode 100644
index 0000000..03cdff8
--- /dev/null
+++ b/DAV/INI_Parse.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+from ConfigParser import SafeConfigParser
+
+class Configuration:
+    def __init__ (self, fileName):
+        cp = SafeConfigParser()
+        cp.read(fileName)
+        self.__parser = cp
+        self.fileName = fileName
+        
+    def __getattr__ (self, name):
+        if name in self.__parser.sections():
+            return Section(name, self.__parser)
+        else:
+            return None
+            
+    def __str__ (self):
+        p = self.__parser
+        result = []
+        result.append('<Configuration from %s>' % self.fileName)
+        for s in p.sections():
+            result.append('[%s]' % s)
+            for o in p.options(s):
+                result.append('%s=%s' % (o, p.get(s, o)))
+        return '\n'.join(result)
+
+class Section:
+    def __init__ (self, name, parser):
+        self.name = name
+        self.__parser = parser
+    def __getattr__ (self, name):
+        return self.__parser.get(self.name, name)
+
+# Test
+if __name__ == '__main__':
+    c = Configuration('Importador.ini')
+    print c.Origem.host, c.Origem.port
+    
\ No newline at end of file
diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
index 6d89916..57da08f 100644
--- a/DAV/WebDAVServer.py
+++ b/DAV/WebDAVServer.py
@@ -40,6 +40,7 @@ import base64
 import AuthServer
 import urlparse
 import urllib
+import random
 
 from propfind import PROPFIND
 from delete import DELETE
@@ -61,6 +62,10 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
     - PROPPATCH
     - MKCOL
 
+    experimental
+    - LOCK
+    - UNLOCK
+
     It uses the resource/collection classes for serving and
     storing content.
     
@@ -107,16 +112,56 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
         self._append("0\r\n")
         self._append("\r\n")
 
-    ### HTTP METHODS
+    ### HTTP METHODS called by the server
 
     def do_OPTIONS(self):
         """return the list of capabilities """
         self.send_response(200)
-        self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
         self.send_header("Content-Type", "text/plain")
-        self.send_header("DAV", "1")
+
+        if self._config.DAV.lockemulation is True:
+            if self._config.DAV.verbose is True:
+                print >>sys.stderr, 'Activated LOCK,UNLOCK emulation for this connection (NOT known to work currently)'
+
+            self.send_header('Allow', 'GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, LOCK, UNLOCK')
+            self.send_header('DAV', '1,2')
+
+        else:
+            self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
+            self.send_header('DAV', '1')
+
+        self.send_header('MS-Author-Via', 'DAV') # this is for M$
         self.end_headers()
 
+    def _init_locks(self):
+        if not hasattr(self, '_lock_table'):
+            self._lock_table = {}
+
+        return self._lock_table
+
+    def is_locked(self, uri):
+        table = self._init_locks()
+        return table.has_key(uri)
+
+    def do_LOCK(self):
+        """ Locking is implemented via in-memory caches. No data is written.  """
+
+        if self._config.DAV.verbose is True:
+            print >>sys.stderr, 'LOCKing resource %s' % self.headers
+
+        # XXX this is not finished and'll be replaced soon
+        token = str(random.randint(123, 12345678))
+        header = {'Lock-Token' : token}
+        return self.send_status(body=str(token))
+
+    def do_UNLOCK(self):
+        """ Always send OK with no content = Status 204 """
+
+        if self.DAV._config.DAV.verbose is True:
+            print >>sys.stderr, 'UNLOCKing resource %s' % self.headers
+
+        return self.send_status(204)
+
     def do_PROPFIND(self):
 
         dc=self.IFACE_CLASS
@@ -141,13 +186,23 @@ class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
         if body:    
             pf.read_propfind(body)
 
+
         try:
             DATA=pf.createResponse()
             DATA=DATA+"\n"
         except DAV_Error, (ec,dd):
             return self.send_status(ec)
 
-        self.send_body_chunks(DATA,"207","Multi-Status","Multiple responses")
+        # work around MSIE DAV bug for creation and modified date
+        # taken from Resource.py @ Zope webdav
+        if (self.headers.get('User-Agent') ==
+            'Microsoft Data Access Internet Publishing Provider DAV 1.1'):
+            result = result.replace('<ns0:getlastmodified xmlns:ns0="DAV:">',
+                                    '<ns0:getlastmodified xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">')
+            result = result.replace('<ns0:creationdate xmlns:ns0="DAV:">',
+                                    '<ns0:creationdate xmlns:n="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.tz">')
+
+        self.send_body_chunks(DATA,'207','Multi-Status','Multiple responses')
 
     def do_GET(self):
         """Serve a GET request."""
diff --git a/DAV/davcmd.py b/DAV/davcmd.py
index 5c6be3f..249b88a 100644
--- a/DAV/davcmd.py
+++ b/DAV/davcmd.py
@@ -37,32 +37,32 @@ def deltree(dc,uri,exclude={}):
         problem_uris=result.keys()
         element=tlist[i-1]
     
-    # test here, if an element is a prefix of an uri which
-    # generated an error before.
-    # note that we walk here from childs to parents, thus
-    # we cannot delete a parent if a child made a problem.
-    # (see example in 8.6.2.1)
-    ok=1
-    for p in problem_uris:
-        if is_prefix(element,p):
-            ok=None
-            break
+        # test here, if an element is a prefix of an uri which
+        # generated an error before.
+        # note that we walk here from childs to parents, thus
+        # we cannot delete a parent if a child made a problem.
+        # (see example in 8.6.2.1)
+        ok=1
+        for p in problem_uris:
+            if is_prefix(element,p):
+                ok=None
+                break
     
-        if not ok: continue 
+            if not ok: continue 
     
-    # here we test for the exclude list which is the other way round!
-    for p in exclude.keys():
-        if is_prefix(p,element):
-            ok=None
-            break
+        # here we test for the exclude list which is the other way round!
+        for p in exclude.keys():
+            if is_prefix(p,element):
+                ok=None
+                break
         
-        if not ok: continue 
+            if not ok: continue 
     
-    # now delete stuff
-    try:
-        delone(dc,element)
-    except DAV_Error, (ec,dd):  
-        result[element]=ec
+        # now delete stuff
+        try:
+            delone(dc,element)
+        except DAV_Error, (ec,dd):  
+            result[element]=ec
     
     return result
 
@@ -130,7 +130,6 @@ def copytree(dc,src,dst,overwrite=None):
 
     """
 
-
     # first delete the destination resource
     if overwrite and dc.exists(dst):
         delres=deltree(dc,dst)
@@ -149,32 +148,33 @@ def copytree(dc,src,dst,overwrite=None):
 
     for element in tlist:
         problem_uris=result.keys()
-    
-    # now URIs get longer and longer thus we have
-    # to test if we had a parent URI which we were not
-    # able to copy in problem_uris which is the prefix
-    # of the actual element. If it is, then we cannot
-    # copy this as well but do not generate another error.
-    ok=1
-    for p in problem_uris:
-        if is_prefix(p,element):
-            ok=None
-            break
+
+        # now URIs get longer and longer thus we have
+        # to test if we had a parent URI which we were not
+        # able to copy in problem_uris which is the prefix
+        # of the actual element. If it is, then we cannot
+        # copy this as well but do not generate another error.
+        ok=1
+        for p in problem_uris:
+            if is_prefix(p,element):
+                ok=None
+                break
     
         if not ok: continue 
 
-    # now create the destination URI which corresponds to
-    # the actual source URI. -> actual_dst
-    # ("subtract" the base src from the URI and prepend the
-    # dst prefix to it.)
-    esrc=replace(element,src,"")
-    actual_dst=dpath+esrc
-
-    # now copy stuff
-    try:
-        copy(dc,element,actual_dst)
-    except DAV_Error, (ec,dd):  
-        result[element]=ec
+        # now create the destination URI which corresponds to
+        # the actual source URI. -> actual_dst
+        # ("subtract" the base src from the URI and prepend the
+        # dst prefix to it.)
+        esrc=replace(element,src,"")
+        actual_dst=dpath+esrc
+
+        print actual_dst
+        # now copy stuff
+        try:
+            copy(dc,element,actual_dst)
+        except DAV_Error, (ec,dd):  
+            result[element]=ec
 
     return result
         
diff --git a/DAV/davcopy.py b/DAV/davcopy.py
old mode 100755
new mode 100644
index ec44931..d9e819d
--- a/DAV/davcopy.py
+++ b/DAV/davcopy.py
@@ -22,8 +22,8 @@
 """
 
 
-from xml.dom import ext
-from xml.dom.Document import Document
+import xml.dom.minidom
+domimpl = xml.dom.minidom.getDOMImplementation()
 
 import sys
 import string
@@ -108,10 +108,8 @@ class COPY:
         ###  we might make a common method out of it)
         ###
 
-        doc = Document(None)
-        ms=doc.createElement("D:multistatus")
-        ms.setAttribute("xmlns:D","DAV:")
-        doc.appendChild(ms)
+        doc = domimpl.createDocument(None, "D:multistatus", None)
+        doc.documentElement.setAttribute("xmlns:D","DAV:")
 
         for el,ec in result.items():
                 re=doc.createElement("D:response")
@@ -124,10 +122,5 @@ class COPY:
                 re.appendChild(hr)
                 re.appendChild(st)
                 ms.appendChild(re)
- 
-        sfile=StringIO()
-        ext.PrettyPrint(doc,stream=sfile)
-        s=sfile.getvalue()
-        sfile.close()
-        return s
 
+        return doc.toxml(encoding="utf-8")
diff --git a/DAV/davmove.py b/DAV/davmove.py
old mode 100755
new mode 100644
diff --git a/DAV/dbconn.py b/DAV/dbconn.py
new file mode 100644
index 0000000..6805b7f
--- /dev/null
+++ b/DAV/dbconn.py
@@ -0,0 +1,66 @@
+try:
+    import MySQLdb
+except ImportError:
+    pass
+
+import sys
+
+class Mconn:
+	def connect(self,username,userpasswd,host,port,db):
+		try: connection = MySQLdb.connect(host=host, port=int(port), user=username, passwd=userpasswd,db=db)
+		except MySQLdb.OperationalError, message:   
+			print >>sys.stderr,"Error %d:\n%s" % (message[ 0 ], message[ 1 ] )  
+			return 0
+		else:
+			self.db = connection.cursor()
+			
+			return 1
+
+	def execute(self,qry):
+		if self.db:
+			try: res=self.db.execute(qry)
+			except MySQLdb.OperationalError, message:   
+				print >>sys.stderr, "Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
+				return 0
+				
+			except MySQLdb.ProgrammingError, message:   
+				print >>sys.stderr,"Error %d:\n%s" % (message[ 0 ], message[ 1 ] )
+				return 0
+				
+			else:
+				print >>sys.stderr,'Query Returned '+str(res)+' results'
+				return self.db.fetchall()
+	
+	def create_user(self,user,passwd):
+		qry="select * from Users where User='%s'"%(user)
+		res=self.execute(qry)
+		if not res or len(res) ==0:
+			qry="insert into Users (User,Pass) values('%s','%s')"%(user,passwd)
+			res=self.execute(qry)
+		else:
+			print >>sys.stderr, "Username already in use"
+			
+	def create_table(self):
+		qry="""CREATE TABLE `Users` (                    
+          		`uid` int(10) NOT NULL auto_increment,  
+          		`User` varchar(60) default NULL,        
+          		`Pass` varchar(60) default NULL,        
+			`Write` tinyint(1) default '0',
+          		PRIMARY KEY  (`uid`)                    
+        		) ENGINE=MyISAM DEFAULT CHARSET=latin1"""
+		self.execute(qry)
+			
+	
+	def first_run(self,user,passwd):
+		res= self.execute('select * from Users')
+		if res or type(res)==type(()) :
+			pass
+		else:
+			self.create_table()
+			self.create_user(user,passwd)
+		
+		
+	def __init__(self,user,password,host,port,db):
+		self.db=0
+		self.connect(user,password,host,port,db)
+		
diff --git a/DAV/delete.py b/DAV/delete.py
old mode 100755
new mode 100644
diff --git a/DAV/errors.py b/DAV/errors.py
old mode 100755
new mode 100644
diff --git a/DAV/iface.py b/DAV/iface.py
index dbe8e10..1d06f16 100644
--- a/DAV/iface.py
+++ b/DAV/iface.py
@@ -106,7 +106,7 @@ class dav_interface:
         """ return the creationdate of a resource """
         d=self.get_creationdate(uri)
         # format it
-        return time.strftime("%Y-%m-%dT%H-%M-%SZ",time.localtime(d))
+        return time.strftime("%Y-%m-%dT%H:%M:%SZ",time.localtime(d))
 
     def _get_dav_getlastmodified(self,uri):
         """ return the last modified date of a resource """
diff --git a/DAV/propfind.py b/DAV/propfind.py
old mode 100755
new mode 100644
index 9fdd981..5796db4
--- a/DAV/propfind.py
+++ b/DAV/propfind.py
@@ -21,8 +21,8 @@
 """
 
 
-from xml.dom import ext
-from xml.dom.Document import Document
+import xml.dom.minidom
+domimpl = xml.dom.minidom.getDOMImplementation()
 
 import sys
 import string
@@ -60,6 +60,9 @@ class PROPFIND:
         self.__uri=uri
         self.__has_body=None    # did we parse a body?
 
+        if dataclass.verbose:
+            print >>sys.stderr, 'PROPFIND: Depth is %s, URI is %s' % (depth, uri)
+
     def read_propfind(self,xml_doc):
         self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
 
@@ -78,49 +81,51 @@ class PROPFIND:
 
         """
 
+        df = None
         if self.request_type==RT_ALLPROP:
-            return self.create_allprop()
+            df = self.create_allprop()
         
         if self.request_type==RT_PROPNAME:
-            return self.create_propname()
+            df =  self.create_propname()
         
         if self.request_type==RT_PROP:
-            return self.create_prop()
-        
+            df = self.create_prop()
+
+        if df != None:
+            return df
+
         # no body means ALLPROP!
-        return self.create_allprop()
+        df = self.create_allprop()
+        return df
     
     def create_propname(self):
         """ create a multistatus response for the prop names """
         
         dc=self.__dataclass
         # create the document generator
-        doc = Document(None)
-        ms=doc.createElement("D:multistatus")
-        ms.setAttribute("xmlns:D","DAV:")
-        doc.appendChild(ms)
+        doc = domimpl.createDocument(None, "multistatus", None)
+        ms = doc.documentElement
+        ms.setAttribute("xmlns:D", "DAV:")
+        ms.tagName = 'D:multistatus'
 
         if self.__depth=="0":
             pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames)
+            re=self.mk_propname_response(self.__uri,pnames, doc)
             ms.appendChild(re)
 
         elif self.__depth=="1":
             pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames)
+            re=self.mk_propname_response(self.__uri,pnames, doc)
             ms.appendChild(re)
         
         for newuri in dc.get_childs(self.__uri):
             pnames=dc.get_propnames(newuri)
-            re=self.mk_propname_response(newuri,pnames)
+            re=self.mk_propname_response(newuri,pnames, doc)
             ms.appendChild(re)
+
         # *** depth=="infinity"  
 
-        sfile=StringIO()
-        ext.PrettyPrint(doc,stream=sfile)
-        s=sfile.getvalue()
-        sfile.close()
-        return s
+        return doc.toxml(encoding="utf-8")
 
     def create_allprop(self):
         """ return a list of all properties """
@@ -156,10 +161,10 @@ class PROPFIND:
 
 
         # create the document generator
-        doc = Document(None)
-        ms=doc.createElement("D:multistatus")
-        ms.setAttribute("xmlns:D","DAV:")
-        doc.appendChild(ms)
+        doc = domimpl.createDocument(None, "multistatus", None)
+        ms = doc.documentElement
+        ms.setAttribute("xmlns:D", "DAV:")
+        ms.tagName = 'D:multistatus'
 
         if self.__depth=="0":
             gp,bp=self.get_propvalues(self.__uri)
@@ -175,13 +180,8 @@ class PROPFIND:
             gp,bp=self.get_propvalues(newuri)
             res=self.mk_prop_response(newuri,gp,bp,doc)
             ms.appendChild(res)
-        
-        sfile=StringIO()
-        ext.PrettyPrint(doc,stream=sfile)
-        s=sfile.getvalue()
-        sfile.close()
-        return s
 
+        return doc.toxml(encoding="utf-8")
 
     def mk_propname_response(self,uri,propnames,doc):
         """ make a new <prop> result element for a PROPNAME request 
@@ -196,7 +196,7 @@ class PROPFIND:
         uparts=urlparse.urlparse(uri)
         fileloc=uparts[2]
         href=doc.createElement("D:href")
-        huri=doc.createTextNode(urllib.quote(fileloc))
+        huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
         href.appendChild(huri)
         re.appendChild(href)
         
@@ -239,13 +239,13 @@ class PROPFIND:
         uparts=urlparse.urlparse(uri)
         fileloc=uparts[2]
         href=doc.createElement("D:href")
-        huri=doc.createTextNode(urllib.quote(fileloc))
+        huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
         href.appendChild(huri)
         re.appendChild(href)
 
         # write good properties
+        ps=doc.createElement("D:propstat")
         if good_props:
-            ps=doc.createElement("D:propstat")
             re.appendChild(ps)
 
         gp=doc.createElement("D:prop")
@@ -308,13 +308,14 @@ class PROPFIND:
         good_props={}
         bad_props={}
 
+        ddc = self.__dataclass
         for (ns,plist) in self.proplist.items():
             good_props[ns]={}
             bad_props={}
-            ec = 0
             for prop in plist:
+                ec = 0
                 try:
-                    r=self.__dataclass.get_prop(uri,ns,prop)
+                    r=ddc.get_prop(uri,ns,prop)
                     good_props[ns][prop]=str(r)
                 except DAV_Error, error_code:
                     ec=error_code[0]
diff --git a/DAV/propfind.py b/DAV/propfind.py.orig
old mode 100755
new mode 100644
similarity index 94%
copy from DAV/propfind.py
copy to DAV/propfind.py.orig
index 9fdd981..371d012
--- a/DAV/propfind.py
+++ b/DAV/propfind.py.orig
@@ -78,17 +78,22 @@ class PROPFIND:
 
         """
 
+        df = None
         if self.request_type==RT_ALLPROP:
-            return self.create_allprop()
+            df = self.create_allprop()
         
         if self.request_type==RT_PROPNAME:
-            return self.create_propname()
+            df =  self.create_propname()
         
         if self.request_type==RT_PROP:
-            return self.create_prop()
+            df = self.create_prop()
         
+        if df != None:
+            return df
+
         # no body means ALLPROP!
-        return self.create_allprop()
+        df = self.create_allprop()
+        return df
     
     def create_propname(self):
         """ create a multistatus response for the prop names """
@@ -102,17 +107,17 @@ class PROPFIND:
 
         if self.__depth=="0":
             pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames)
+            re=self.mk_propname_response(self.__uri,pnames, doc)
             ms.appendChild(re)
 
         elif self.__depth=="1":
             pnames=dc.get_propnames(self.__uri)
-            re=self.mk_propname_response(self.__uri,pnames)
+            re=self.mk_propname_response(self.__uri,pnames, doc)
             ms.appendChild(re)
         
         for newuri in dc.get_childs(self.__uri):
             pnames=dc.get_propnames(newuri)
-            re=self.mk_propname_response(newuri,pnames)
+            re=self.mk_propname_response(newuri,pnames, doc)
             ms.appendChild(re)
         # *** depth=="infinity"  
 
@@ -244,8 +249,8 @@ class PROPFIND:
         re.appendChild(href)
 
         # write good properties
+        ps=doc.createElement("D:propstat")
         if good_props:
-            ps=doc.createElement("D:propstat")
             re.appendChild(ps)
 
         gp=doc.createElement("D:prop")
@@ -308,13 +313,14 @@ class PROPFIND:
         good_props={}
         bad_props={}
 
+        ddc = self.__dataclass
         for (ns,plist) in self.proplist.items():
             good_props[ns]={}
             bad_props={}
-            ec = 0
             for prop in plist:
+                ec = 0
                 try:
-                    r=self.__dataclass.get_prop(uri,ns,prop)
+                    r=ddc.get_prop(uri,ns,prop)
                     good_props[ns][prop]=str(r)
                 except DAV_Error, error_code:
                     ec=error_code[0]
diff --git a/DAV/utils.py b/DAV/utils.py
index 910619a..45fa792 100755
--- a/DAV/utils.py
+++ b/DAV/utils.py
@@ -8,11 +8,8 @@ UTILITIES
 
 """
 
-from xml.dom import ext
-from xml.dom.Document import Document
-from xml.dom.ext.reader import PyExpat
-from xml.dom import Node
-from xml.dom import NodeIterator, NodeFilter
+
+from xml.dom import minidom
 
 from string import lower, split, atoi, joinfields
 import urlparse
@@ -21,7 +18,7 @@ from StringIO import StringIO
 from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
 from status import STATUS_CODES
 
-VERSION = '0.6'
+VERSION = '0.9-dev'
 AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
 
 
@@ -43,32 +40,30 @@ def parse_propfind(xml_doc):
         namespaces            -- list of namespaces found
     
     """
-    doc = PyExpat.Reader().fromString(xml_doc)
-    snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
+
+    doc = minidom.parseString(xml_doc)
 
     request_type=None
     props={}
     namespaces=[]
 
-    while 1:
-        curr_elem = snit.nextNode()
-        if not curr_elem: break
-        ename=fname=lower(curr_elem.nodeName)
-        if ":" in fname:
-            ename=split(fname,":")[1]
-        if ename=="prop": request_type=RT_PROP; continue
-        if ename=="propfind": continue
-        if ename=="allprop": request_type=RT_ALLPROP; continue
-        if ename=="propname": request_type=RT_PROPNAME; continue
-
-        # rest should be names of attributes
-
-        ns = curr_elem.namespaceURI
-        if props.has_key(ns):
-            props[ns].append(ename)
-        else:
-            props[ns]=[ename]
-            namespaces.append(ns)
+    if doc.getElementsByTagNameNS("DAV:", "allprop"):
+        request_type = RT_ALLPROP
+    elif doc.getElementsByTagNameNS("DAV:", "propname"):
+        request_type = RT_PROPNAME
+    else:
+        request_type = RT_PROP
+        e = doc.getElementsByTagNameNS("DAV:", "prop")
+        for e in e[0].childNodes:
+            if e.nodeType != minidom.Node.ELEMENT_NODE:
+                continue
+            ns = e.namespaceURI
+            ename = e.localName
+            if props.has_key(ns):
+                props[ns].append(ename)
+            else:
+                props[ns]=[ename]
+                namespaces.append(ns)
 
     return request_type,props,namespaces
 
@@ -135,26 +130,19 @@ def get_parenturi(uri):
 
 def make_xmlresponse(result):
     """ construct a response from a dict of uri:error_code elements """
-    doc = Document.Document(None)
-    ms=doc.createElement("D:multistatus")
-    ms.setAttribute("xmlns:D","DAV:")
-    doc.appendChild(ms)
+    doc = minidom.getDOMImplementation().createDocument(None, "D:multistatus", None)
+    doc.documentElement.setAttribute("xmlns:D","DAV:")
 
     for el,ec in result.items():
-        re=doc.createElement("D:response")
-        hr=doc.createElement("D:href")
-        st=doc.createElement("D:status")
+        re=doc.createElementNS("DAV:","response")
+        hr=doc.createElementNS("DAV:","href")
+        st=doc.createElementNS("DAV:","status")
         huri=doc.createTextNode(quote_uri(el))
         t=doc.createTextNode(gen_estring(ec))
         st.appendChild(t)
         hr.appendChild(huri)
         re.appendChild(hr)
         re.appendChild(st)
-        ms.appendChild(re)
-
-    sfile=StringIO()
-    ext.PrettyPrint(doc,stream=sfile)
-    s=sfile.getvalue()
-    sfile.close()
-    return s
+        doc.documentElement.appendChild(re)
 
+    return doc.toxml(encoding="utf-8")
diff --git a/DAVServer/__init__.py b/DAVServer/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/DAVServer/config.ini b/DAVServer/config.ini
new file mode 100644
index 0000000..47b0d60
--- /dev/null
+++ b/DAVServer/config.ini
@@ -0,0 +1,50 @@
+
+# PyWebDAV config.ini
+# Read documents before editing this file
+
+# Created 11:48 10-08-2006 By Vince Spicer
+
+
+[MySQL]
+
+# Mysql server information
+host=localhost
+port=3306
+user=root
+passwd=rootpw
+
+# Auth Database Table, Must exists in database prior to firstrun
+dbtable=webDav
+
+# Create User Database Table and Insert system user
+# Disable after the Table is created; for performance reasons
+firstrun=0
+
+[DAV]
+
+# Verbose?
+verbose = 1
+
+# main directory 
+directory = /home/spamies/tmp
+
+# Server address
+port = 8081
+host = localhost
+
+# disable auth
+noauth = 0
+
+# Enable mysql auth
+mysql_auth=0
+
+# admin user
+user = test
+password = test00
+
+# daemonize?
+daemonize = 0
+daemonaction = start
+
+# instance counter
+counter = 0
diff --git a/PyDAVServer/daemonize.py b/DAVServer/daemonize.py
old mode 100755
new mode 100644
similarity index 98%
rename from PyDAVServer/daemonize.py
rename to DAVServer/daemonize.py
index 0c59417..84e527d
--- a/PyDAVServer/daemonize.py
+++ b/DAVServer/daemonize.py
@@ -112,7 +112,7 @@ def startstop(stdout='/dev/null', stderr=None, stdin='/dev/null',
         
         if 'start' == action:
             if pid:
-                mess = "Start aborded since pid file '%s' exists.\n"
+                mess = "Start aborted since pid file '%s' exists.\n"
                 sys.stderr.write(mess % pidfile)
                 sys.exit(1)
 
diff --git a/PyDAVServer/fileauth.py b/DAVServer/fileauth.py
old mode 100755
new mode 100644
similarity index 80%
rename from PyDAVServer/fileauth.py
rename to DAVServer/fileauth.py
index 002612e..5668e50
--- a/PyDAVServer/fileauth.py
+++ b/DAVServer/fileauth.py
@@ -26,28 +26,30 @@ This is an example implementation of a DAVserver using the DAV package.
 from DAV.WebDAVServer import DAVRequestHandler
 from fshandler import FilesystemHandler
 import sys
+from DAV.dbconn import Mconn
 
 class DAVAuthHandler(DAVRequestHandler):
     """
-    Provide real auth check based on filesystem files
+    Provides authentication based on parameters. The calling
+    class has to inject password and username into this.
+    (Variables: auth_user and auth_pass)
     """
     
     # Do not forget to set IFACE_CLASS by caller
     # ex.: IFACE_CLASS = FilesystemHandler('/tmp', 'http://localhost/')
-
     verbose = False
-
+    
     def _log(self, message):
         if self.verbose:
             print >>sys.stderr, '>> (DAVAuthHandler) %s' % message
 
-    def get_userinfo(self,user,pw):
-    	""" authenticate user """
+    def get_userinfo(self,user,pw,command):
+        """ authenticate user """
 
-        if user == self.auth_user and pw == self.auth_pass:
+        if user == self._config.DAV.user and pw == self._config.DAV.password:
             self._log('Successfully authenticated user %s' % user)
             return 1
 
         self._log('Authentication failed for user %s' % user)
-        return None
+        return 0
 
diff --git a/PyDAVServer/fshandler.py b/DAVServer/fshandler.py
old mode 100755
new mode 100644
similarity index 97%
rename from PyDAVServer/fshandler.py
rename to DAVServer/fshandler.py
index f7a1d8a..b6a0f65
--- a/PyDAVServer/fshandler.py
+++ b/DAVServer/fshandler.py
@@ -78,11 +78,8 @@ class FilesystemHandler(dav_interface):
             if os.path.isdir(fileloc):
                 try:
                     files=os.listdir(fileloc)
-                
-                # silently ignore directories
-                # not allowed.
                 except:
-                    raise DAV_Forbidden
+                    raise DAV_NotFound
                 
                 for file in files:
                     newloc=os.path.join(fileloc,file)
@@ -118,10 +115,14 @@ class FilesystemHandler(dav_interface):
         path=self.uri2local(uri)
         if os.path.isfile(path):
             return OBJECT
-        else:
+
+        elif os.path.isdir(path):
             return COLLECTION
+
+        raise DAV_NotFound
         
     def _get_dav_displayname(self,uri):
+        #return uri
         raise DAV_Secret    # do not show
 
     def _get_dav_getcontentlength(self,uri):
@@ -161,11 +162,10 @@ class FilesystemHandler(dav_interface):
         if os.path.exists(path):
             if os.path.isfile(path):
                 return "application/octet-stream"
-            else:
+            elif os.path.isdir(path):
                 return "httpd/unix-directory"
 
-        else:
-            raise DAV_NotFound, 'Could not find %s' % path
+        raise DAV_NotFound, 'Could not find %s' % path
 
     def put(self,uri,data,content_type=None):
         """ put the object into the filesystem """
@@ -227,6 +227,7 @@ class FilesystemHandler(dav_interface):
         if not os.system("rm -f '%s'" %path):
             return 204
         else:
+            self._log('rm: Forbidden')
             raise DAV_Forbidden # forbidden
 
     ###
@@ -355,6 +356,7 @@ class FilesystemHandler(dav_interface):
         try:
             os.system("cp '%s' '%s'" %(srcfile,dstfile))
         except:
+            self._log('copy: forbidden')
             raise DAV_Error, Forbidden
 
     def copycol(self,src,dst):
diff --git a/DAVServer/mysqlauth.py b/DAVServer/mysqlauth.py
new file mode 100644
index 0000000..da6249c
--- /dev/null
+++ b/DAVServer/mysqlauth.py
@@ -0,0 +1,36 @@
+from fileauth import DAVAuthHandler
+
+class MySQLAuthHandler(DAVAuthHandler):
+    """
+    Provides authentication based on a mysql table
+    """
+
+    def get_userinfo(self,user,pw,command):
+        """ authenticate user """
+
+        # Commands that need write access
+        nowrite=['OPTIONS','PROPFIND','GET']
+
+        Mysql=self._config.MySQL
+        DB=Mconn(Mysql.user,Mysql.passwd,Mysql.host,Mysql.port,Mysql.dbtable)
+        if self.verbose:
+            print >>sys.stderr,user,command
+
+        qry="select * from %s.Users where User='%s' and Pass='%s'"%(Mysql.dbtable,user,pw)
+        Auth=DB.execute(qry)
+        
+        if len(Auth) == 1:
+            can_write=Auth[0][3]
+            if not can_write and not command in nowrite:
+                self._log('Authentication failed for user %s using command %s' %(user,command))
+                return 0
+            else:
+                self._log('Successfully authenticated user %s writable=%s' % (user,can_write))
+                return 1
+        else:
+            self._log('Authentication failed for user %s' % user)
+            return 0
+            
+        self._log('Authentication failed for user %s' % user)
+        return 0
+
diff --git a/PyDAVServer/server.py b/DAVServer/server.py
similarity index 57%
rename from PyDAVServer/server.py
rename to DAVServer/server.py
index 552e90e..a018c8b 100755
--- a/PyDAVServer/server.py
+++ b/DAVServer/server.py
@@ -31,19 +31,15 @@ except ImportError:
     print 'DAV package not found! Please install into site-packages or set PYTHONPATH!'
     sys.exit(2)
 
-try:
-    from xml.dom import ext
-except ImportError:
-    print 'PyXML not found! Get it from http://pyxml.sourceforge.net/'
-    sys.exit(2)
-
 from DAV.utils import VERSION, AUTHOR
 __version__ = VERSION
 __author__  = AUTHOR
 
 from fileauth import DAVAuthHandler
+from mysqlauth import MySQLAuthHandler
 from fshandler import FilesystemHandler
 from daemonize import startstop
+from DAV.INI_Parse import Configuration
 
 def runserver(
          port = 8008, host='localhost',
@@ -73,6 +69,7 @@ def runserver(
         sys.exit(233)
         
     # dispatch directory and host to the filesystem handler
+    # This handler is responsible from where to take the data
     handler.IFACE_CLASS = FilesystemHandler(directory, 'http://%s:%s/' % (host, port), verbose )
 
     # put some extra vars
@@ -81,10 +78,6 @@ def runserver(
         print >>sys.stderr, '>> ATTENTION: Authentication disabled!'
         handler.DO_AUTH = False
 
-    else:
-        handler.auth_user = user
-        handler.auth_pass = password
-    
     print >>sys.stderr, '>> Serving data from %s' % directory
    
     # initialize server on specified port
@@ -106,18 +99,31 @@ Standalone WebDAV server based on python
 
 Usage: ./server.py [OPTIONS]
 Parameters:
+    -c, --config    Specify a file where configuration is specified. In this
+                    file you can specify options for a running server.
+                    For an example look at the config.ini in this directory.
     -D, --directory Directory where to serve data from
                     The user that runs this server must have permissions
                     on that directory. NEVER run as root!
                     Default directory is /tmp
-
     -H, --host      Host where to listen on (default: localhost)
     -P, --port      Port to bind server to  (default: 8008)
     -u, --user      Username for authentication
     -p, --password  Password for given user
-    -n, --noauth    Pass parameter if server should not ask for authentication  
+    -n, --noauth    Pass parameter if server should not ask for authentication
+                    This means that every user has access
+    -m, --mysql     Pass this parameter if you want MySQL based authentication.
+                    If you want to use MySQL then the usage of a configuration
+                    file is mandatory.
+    -J, --lockemu   Activate experimental lock and unlock emulation. Currently
+                    no real locking is done. It is only to satisfy clients
+                    needing DAV 2 compliant server for read/write access 
+                    (Mac OS X Finder).
+    -i, --icounter  If you want to run multiple instances then you have to
+                    give each instance it own number so that logfiles and such
+                    can be identified. Default is 0
     -d, --daemon    Make server act like a daemon. That means that it is going
-                    to the background mode. All messages are redirected to
+                    to background mode. All messages are redirected to
                     logfiles (default: /tmp/pydav.log and /tmp/pydav.err).
                     You need to pass one of the following values to this parameter
                         start   - Start daemon
@@ -131,8 +137,18 @@ Parameters:
 Please send bug reports and feature requests to %s
 """ % (__version__, __author__)
 
-if __name__ == '__main__':
+def setupDummyConfig(**kw):
+
+    class DummyConfigDAV:
+        def __init__(self, **kw):
+            self.__dict__.update(**kw)
+
+    class DummyConfig:
+        DAV = DummyConfigDAV(**kw)
+
+    return DummyConfig()
 
+def run():
     verbose = False
     directory = '/tmp'
     port = 8008
@@ -142,17 +158,34 @@ if __name__ == '__main__':
     password = ''
     daemonize = False
     daemonaction = 'start'
+    counter = 0
+    mysql = False
+    lockemulation = False
+    configfile = ''
     
     # parse commandline
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvh', 
-                ['host=', 'port=', 'directory=', 'user=', 'password=','daemon=', 'noauth', 'help', 'verbose'])     
+        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvhmJi:c:', 
+                ['host=', 'port=', 'directory=', 'user=', 'password=','daemon=', 'noauth', 'help', 'verbose', 
+                 'mysql', 'icounter=', 'config=', 'lockemu'])
     except getopt.GetoptError, e:
         print usage
         print '>>>> ERROR: %s' % str(e)
         sys.exit(2)
     
     for o,a in opts:
+        if o in ['-i', '--icounter']:
+            counter = int(str(a).strip())
+
+        if o in ['-m', '--mysql']:
+            mysql = True
+
+        if o in ['-J', '--lockemu']:
+            lockemulation = True
+
+        if o in ['-c', '--config']:
+            configfile = a
+
         if o in ['-D', '--directory']:
             directory = a
 
@@ -182,24 +215,85 @@ if __name__ == '__main__':
             daemonize = True
             daemonaction = a
 
-    print >>sys.stderr, 'Starting up PyWebDAV server (version %s)' % __version__
-    if not noauth and daemonaction != 'status':
+    conf = None
+    if configfile != '':
+        print >>sys.stderr, 'Reading configuration from %s' % configfile
+        conf = Configuration(configfile)
+
+        dv = conf.DAV
+        verbose = bool(int(dv.verbose))
+        directory = dv.directory
+        port = dv.port
+        host = dv.host
+        noauth = bool(int(dv.noauth))
+        user = dv.user
+        password = dv.password
+        daemonize = bool(int(dv.daemonize))
+        if daemonaction != 'stop':
+            daemonaction = dv.daemonaction
+        counter = int(dv.counter)
+
+    else:
+
+        _dc = { 'verbose' : verbose,
+                'directory' : directory,
+                'port' : port,
+                'host' : host,
+                'noauth' : noauth,
+                'user' : user,
+                'password' : password,
+                'daemonize' : daemonize,
+                'daemonaction' : daemonaction,
+                'counter' : counter,
+                'lockemulation' : lockemulation }
+
+        conf = setupDummyConfig(**_dc)
+
+    if mysql == True and configfile == '':
+        print >>sys.stderr, '>> ERROR: You can only use MySQL with configuration file!'
+        sys.exit(3)
+
+    if daemonaction != 'stop':
+        print >>sys.stderr, 'Starting up PyWebDAV server (version %s)' % __version__
+    else:
+        print >>sys.stderr, 'Stopping PyWebDAV server (version %s)' % __version__
+
+    if not noauth and daemonaction not in ['status', 'stop']:
         if not user:
-            print >>sys.stderr, '>> ERROR: Please specify an username or pass parameter --noauth'
+            print usage
+            print >>sys.stderr, '>> ERROR: No usable parameter specified!'
+            print >>sys.stderr, '>> Example: davserver -D /home/files -n'
             sys.exit(3)
   
     if daemonaction == 'status':
-        print >>sys.stdout, 'Checking for status...'
+        print >>sys.stdout, 'Checking for state...'
    
     if type(port) == type(''):
         port = int(port.strip())
    
     if daemonize:
-        startstop(stdout='/tmp/pydav.log', 
-                    stderr='/tmp/pydav.err', 
-                    pidfile='/tmp/pydav.pid', 
+
+        # check if pid file exists
+        if os.path.exists('/tmp/pydav%s.pid' % counter) and daemonaction != 'stop':
+            print >>sys.stderr, \
+                  '>> ERROR: Found another instance! Either use -i to specifiy another instance number or remove /tmp/pydav%s.pid!' % counter
+            sys.exit(3)
+
+        startstop(stdout='/tmp/pydav%s.log' % counter, 
+                    stderr='/tmp/pydav%s.err' % counter, 
+                    pidfile='/tmp/pydav%s.pid' % counter, 
                     startmsg='>> Started PyWebDAV (PID: %s)',
                     action=daemonaction)
   
     # start now
-    runserver(port, host, directory, verbose, noauth, user, password)
+    handler = DAVAuthHandler
+    if mysql == True:
+        handler = MySQLAuthHandler
+
+    # injecting options
+    handler._config = conf
+
+    runserver(port, host, directory, verbose, noauth, user, password, handler=handler)
+
+if __name__ == '__main__':
+    run()
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..22c139a
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include setup.py README VERSION
+include doc/*
+include DAVServer/*.py DAVServer/*.ini
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..3cf5c0e
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,31 @@
+Metadata-Version: 1.0
+Name: PyWebDAV
+Version: 0.9.1
+Summary: WebDAV library including a standalone server for python
+Home-page: http://code.google.com/p/pywebdav/
+Author: Simon Pamies
+Author-email: spamsch at gmail.com
+License: GPL v2
+Description: 
+        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        After installation of this package you will have a new script in you $PYTHON/bin directory called
+        *davserver*. This serves as the main entry point to the server.
+        
+        Example ::
+        davserver -D /home/files -n
+        
+        For more information go to http://code.google.com/p/pywebdav/
+        
+Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
+Platform: Unix
+Platform: Windows
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: GNU General Public License (GPL)
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries
diff --git a/PyWebDAV.egg-info/PKG-INFO b/PyWebDAV.egg-info/PKG-INFO
new file mode 100644
index 0000000..3cf5c0e
--- /dev/null
+++ b/PyWebDAV.egg-info/PKG-INFO
@@ -0,0 +1,31 @@
+Metadata-Version: 1.0
+Name: PyWebDAV
+Version: 0.9.1
+Summary: WebDAV library including a standalone server for python
+Home-page: http://code.google.com/p/pywebdav/
+Author: Simon Pamies
+Author-email: spamsch at gmail.com
+License: GPL v2
+Description: 
+        WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+        After installation of this package you will have a new script in you $PYTHON/bin directory called
+        *davserver*. This serves as the main entry point to the server.
+        
+        Example ::
+        davserver -D /home/files -n
+        
+        For more information go to http://code.google.com/p/pywebdav/
+        
+Keywords: webdav,server,dav,standalone,library,gpl,http,rfc2518,rfc 2518
+Platform: Unix
+Platform: Windows
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: GNU General Public License (GPL)
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries
diff --git a/PyWebDAV.egg-info/SOURCES.txt b/PyWebDAV.egg-info/SOURCES.txt
new file mode 100644
index 0000000..ce7f1ae
--- /dev/null
+++ b/PyWebDAV.egg-info/SOURCES.txt
@@ -0,0 +1,42 @@
+MANIFEST.in
+README
+VERSION
+ez_setup.py
+setup.py
+DAV/AuthServer.py
+DAV/BufferingHTTPServer.py
+DAV/INI_Parse.py
+DAV/WebDAVServer.py
+DAV/__init__.py
+DAV/constants.py
+DAV/davcmd.py
+DAV/davcopy.py
+DAV/davmove.py
+DAV/dbconn.py
+DAV/delete.py
+DAV/errors.py
+DAV/iface.py
+DAV/propfind.py
+DAV/propfind.py.orig
+DAV/status.py
+DAV/utils.py
+DAVServer/__init__.py
+DAVServer/config.ini
+DAVServer/daemonize.py
+DAVServer/fileauth.py
+DAVServer/fshandler.py
+DAVServer/mysqlauth.py
+DAVServer/server.py
+PyWebDAV.egg-info/PKG-INFO
+PyWebDAV.egg-info/SOURCES.txt
+PyWebDAV.egg-info/dependency_links.txt
+PyWebDAV.egg-info/entry_points.txt
+PyWebDAV.egg-info/not-zip-safe
+PyWebDAV.egg-info/top_level.txt
+doc/ARCHITECTURE
+doc/Changes
+doc/INSTALL
+doc/LICENSE
+doc/TODO
+doc/interface_class
+doc/walker
\ No newline at end of file
diff --git a/PyWebDAV.egg-info/dependency_links.txt b/PyWebDAV.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/PyWebDAV.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/PyWebDAV.egg-info/entry_points.txt b/PyWebDAV.egg-info/entry_points.txt
new file mode 100644
index 0000000..5a11fa1
--- /dev/null
+++ b/PyWebDAV.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+davserver = DAVServer.server:run
+
diff --git a/PyWebDAV.egg-info/not-zip-safe b/PyWebDAV.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/PyWebDAV.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/PyWebDAV.egg-info/top_level.txt b/PyWebDAV.egg-info/top_level.txt
new file mode 100644
index 0000000..e863022
--- /dev/null
+++ b/PyWebDAV.egg-info/top_level.txt
@@ -0,0 +1,2 @@
+DAVServer
+DAV
diff --git a/README b/README
index aa10c2b..22bc1af 100644
--- a/README
+++ b/README
@@ -1,17 +1,19 @@
 
-I want to introduce you to my webdav server implementation in Python. Actually
-it's supposed to be a generic class which can be used for all sorts of servers.
-Thus the actual webdav part and the data interface part is implemented in two
-modules. All you have to do in order to create a wevdav server for you 
-application is to write an interface class.
+Python WebDAV implementation.
 
 A working WebDAV Server is included in the PyDAVServer package. Feel free to
 extend it to your needs. Please send suggestions and bugfixes to the author(s).
 
+LICENSE
+-------
+
+see doc/LICENSE file
+
+
 AUTHOR(s)
 ------
 
-Simon Pamies (Current maintainer)
+Simon Pamies (also current maintainer)
 Bielefeld, Germany
 s.pamies at banality.de
 
@@ -19,16 +21,28 @@ Christian Scholz
 Aachen, Germany
 mrtopf at webdav.de
 
+Vince Spicer
+Ontario, Canada
+vince at vince.ca
+
+
 REQUIREMENTS
 ------------
 
 - Python 2.0  or higher (www.python.org)
 - PyXML 0.66 (pyxml.sourceforge.net)
 
+OPTIONAL
+--------
+
+- MySQLdb (http://sourceforge.net/projects/mysql-python)
+	- Mysql server 4.0+ for Mysql authentication with
+	  with read/write access to one database
+
 INSTALLATION
 ------------
 
-see INSTALL file
+see doc/INSTALL file
 
 What is WebDAV?
 ---------------
@@ -80,6 +94,15 @@ B. In the PyDAVServer directory:
     3. fileauth.py
        Handler for authentication. Nothing very special about it.
 
+    4. dbconn.py
+       Mysql Database Handler.
+
+    5. INI_Parse.py
+       Parses the config.ini file.
+
+    6. config.ini
+       PyWebDav configuration file.
+
 
 NOTES
 -----
@@ -98,10 +121,9 @@ If you find bugs (many!) or have suggestions then please email the maintainer
 
 Thanks :)
 
-LICENSE
--------
 
-see LICENSE file
+Windows Notes
+-------------
 
-That's all there is to it!
+This version can not be run under windows.
 
diff --git a/VERSION b/VERSION
index 5a2a580..f374f66 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6
+0.9.1
diff --git a/ARCHITECTURE b/doc/ARCHITECTURE
similarity index 100%
rename from ARCHITECTURE
rename to doc/ARCHITECTURE
diff --git a/Changes b/doc/Changes
similarity index 60%
rename from Changes
rename to doc/Changes
index 888ce23..119ab9f 100644
--- a/Changes
+++ b/doc/Changes
@@ -1,5 +1,57 @@
-Changes since 0.5.2
--------------------
+
+0.9.1 (May 4th 2009)
+--
+
+- Restructured the structure a bit: Made server package
+  a real python package. Adapted error messages. Prepared
+  egg distribution.
+  [Simon Pamies]
+
+- Fix for time formatting bug. Thanks to Ian Kallen
+  [Simon Pamies]
+
+- Small fixes for WebDavServer (status not handled correctly) and
+  propfind (children are returned from a PROPFIND with "Depth: 0")
+  [Kjetil Ørbekk]
+
+0.8 (Jul 15th 2008)
+---
+
+- First try of an implementation of the LOCK and UNLOCK features.
+  Still very incomplete (read: very incomplete) and not working
+  in this version.
+  [Simon Pamies]
+
+- Some code cleanups to prepare restructuring
+  [Simon Pamies]
+
+- Port to minidom because PyXML isn't longer maintained
+  [Martin v. Loewis]
+
+- utils.py: Makes use of DOMImplementation class to create a new xml document
+  Uses dom namespace features to create elements within DAV: namespace
+  [Stephane Bonhomme]
+
+- davcmd.py: Missing an indent in loop on remove and copy operations on trees, the
+  effect was that only the last object was removed/copied : always leads
+  to a failure when copying collections.
+  [Stephane Bonhomme]
+
+- propfind.py: missing a return at the end of the createResponse method (case of a
+  propfind without xml body, should act as a allprops).
+  [Stephane Bonhomme]
+
+0.7
+---
+
+- Added MySQL auth support brought by Vince Spicer
+- Added INI file support also  introduced by Vince
+- Some minor bugfixes and integration changes.
+- Added instance counter to make multiple instances possible
+- Extended --help text a bit
+
+0.6
+---
 
 - Added bugfixes for buggy Mac OS X Finder implementation
   Finder tries to stat .DS_Store without checking if it exists
@@ -13,7 +65,7 @@ Changes since 0.5.2
 - Added logging facilities
 - Added extended arguments
 
-- some more things I can't remember
+- some more things I can't remember (spamsch)
 
 Changes since 0.5.1
 -------------------
diff --git a/INSTALL b/doc/INSTALL
similarity index 100%
rename from INSTALL
rename to doc/INSTALL
diff --git a/LICENSE b/doc/LICENSE
similarity index 100%
rename from LICENSE
rename to doc/LICENSE
diff --git a/TODO b/doc/TODO
similarity index 100%
rename from TODO
rename to doc/TODO
diff --git a/ez_setup.py b/ez_setup.py
new file mode 100644
index 0000000..d24e845
--- /dev/null
+++ b/ez_setup.py
@@ -0,0 +1,276 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c9"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
index ed160b8..abc91bb 100644
--- a/setup.py
+++ b/setup.py
@@ -1,25 +1,50 @@
 #!/usr/bin/env python
 
+try: 
+   from ez_setup import use_setuptools 
+   use_setuptools() 
+except ImportError: 
+   pass 
+
+from setuptools import setup
+
+VERSION = open('VERSION', 'r').read()
+VERSION = VERSION.replace('\n', '')
+
+DOC = """
+WebDAV library for python. Consists of a server and the DAV package that provides WebDAV functionality.
+After installation of this package you will have a new script in you $PYTHON/bin directory called
+*davserver*. This serves as the main entry point to the server.
+
+Example ::
+    davserver -D /home/files -n
+
+For more information go to http://code.google.com/p/pywebdav/
+"""
+
 from distutils.core import setup
-setup(name='PyDAV',
-      description='WebDAV library and server for python',
-      author='Christian Scholz',
-      author_email='cs at comlounge.net',
+setup(name='PyWebDAV',
+      description='WebDAV library including a standalone server for python',
+      author='Simon Pamies',
+      author_email='spamsch at gmail.com',
       maintainer='Simon Pamies',
-      maintainer_email='s.pamies at banality.de',
-      url='http://www.webdav.de',
-      version='0.6',
+      maintainer_email='spamsch at gmail.com',
+      url='http://code.google.com/p/pywebdav/',
+      platforms=['Unix', 'Windows'],
+      license='GPL v2',
+      version=VERSION,
+      long_description=DOC,
       classifiers = [
-          'Development Status :: 0.6',
+          'Development Status :: 5 - Production/Stable',
           'Environment :: Console',
           'Environment :: Web Environment',
-          'Intended Audience :: End Users',
           'Intended Audience :: Developers',
           'Intended Audience :: System Administrators',
-          'License :: GPL',
+          'License :: OSI Approved :: GNU General Public License (GPL)',
           'Operating System :: MacOS :: MacOS X',
           'Operating System :: POSIX',
           'Programming Language :: Python',
+          'Topic :: Software Development :: Libraries'
           ],
       keywords = ['webdav',
                   'server',
@@ -30,5 +55,9 @@ setup(name='PyDAV',
                   'http',
                   'rfc2518',
                   'rfc 2518'],
-      packages=['DAV', ],
+      packages=['DAV', 'DAVServer'],
+      zip_safe=False,
+      entry_points={
+          'console_scripts' : ['davserver = DAVServer.server:run']
+      }
       )
commit b24c8be644a6641c80ca130bbe3f15c121047ffe
Author: Daniel Baumann <daniel at debian.org>
Date:   Wed Feb 4 22:52:22 2009 +0100

    Adding upstream version 0.6.

diff --git a/ARCHITECTURE b/ARCHITECTURE
new file mode 100644
index 0000000..39348b1
--- /dev/null
+++ b/ARCHITECTURE
@@ -0,0 +1,84 @@
+Class Tree
+----------
+
+PyWebDAVServer.fileauth.DAVAuthHandler
+<Very simple handler for authentication. Must have its data
+injected (look at server.py). Overwrites get_userinfo from AuthRequestHandler>
+
+            |
+            |
+            |
+            
+DAV.WebDAVServer.DAVRequestHandler
+<Provides methods for DAV commands. These methods are triggered
+by AuthRequestHandler.handle().>
+
+            |
+            |
+            |
+
+DAV.AuthServer.BufferedAuthRequestHandler
+<Calls the right methods to buffer request data>
+
+        |                      |
+        |                      |
+        |                      |
+
+DAV.BufferingHTTPServer     DAV.AuthServer.AuthRequestHandler
+<Saves the complete result  <Overwrites handle() method in order
+in one file and returns     to provide authentication and to pass
+only if request is          control to the do_ methods handling
+complete>                   DAV commands>
+
+
+Information
+----------
+
+This document describes the architecture of the python davserver.
+
+The main programm is stored in DAV/WebDAVServer.py. It exports a class WebDAVServer
+which is subclassed from AuthServer which again is subclassed from 
+BufferingHTTPServer.
+
+The BufferingHTTPServer class extends the BaseHTTPServer class by
+storing all output in a buffer and sending it at once when the request
+is finished. Otherwise clients like cadaver might break.
+
+The AuthServer class implements Basic Authentication in order to make
+connections somewhat more secure.
+
+For processing requests the WebDAVServer class needs some connection to
+the actual data on server. In contrast to a normal web server this
+data must not simply be stored on a filesystem but can also live in
+databases and the like.  
+
+Thus the WebDAVServer class needs an interface
+to this data which is implemented via an interface class (in our
+example stored in fshandler.py). This class will be instantiated by the
+WebDAVServer class and be used when needed (e.g. retrieving properties,
+creating new resources, obtaining existing resources etc.).
+
+When it comes to parsing XML (like in the PROPFIND and PROPPATCH
+methods) the WebDAVServer class uses for each method another extra class
+stored e.g. in propfind.py.  This class parses the XML body and
+createsan XML response while obtaining data from the interface
+class. Thus all the XML parsing is factored out into the specific
+method classes.
+
+In order to create your own davserver for your own purposes you have to do the
+following:
+
+- subclass the WebDAVServer class and write your own get_userinfo() method for 
+  identifying users.
+
+- create your own interface class for interfacing with your actual data. 
+  You might use the existing class as skeleton and explanation of which 
+  methods are needed.
+
+That should be basically all you need to do. Have a look at PyDAVServer/fileauth.py in order
+to get an example how to subclass WebDAVServer.
+
+===
+* describe the methods which need to be implemented.
+
+
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..888ce23
--- /dev/null
+++ b/Changes
@@ -0,0 +1,70 @@
+Changes since 0.5.2
+-------------------
+
+- Added bugfixes for buggy Mac OS X Finder implementation
+  Finder tries to stat .DS_Store without checking if it exists
+- Cleaned up readme and install files
+- Moved license to extra file
+- Added distutils support
+- Refactored module layout
+- Refactored class and module names
+- Added commandline support
+- Added daemonize support
+- Added logging facilities
+- Added extended arguments
+
+- some more things I can't remember
+
+Changes since 0.5.1
+-------------------
+Updated to work with latest 4Suite
+
+Changes since 0.5
+-----------------
+
+- added constants.py 
+- data.py must now return COLLECTION or OBJECT when getting asked for
+  resourcetype. propfind.py will automatically generate the right xml 
+  element.
+- <href> now only contains the path
+- changed HTTP/1.0 header to HTTP/1.1 which makes it work with WebFolders
+- added DO_AUTH constant to AuthServer.py to control whether authentication
+  should be done or not.
+- added chunked responses in davserver.py
+  One step in order to get a server with keep-alive one day.
+- we now use 4DOM instead if PyDOM
+- the URI in a href is quoted
+- complete rewrite of the PROPFIND stuff:
+    - error responses are now generated when a property if not found
+      or not accessible
+    - namespace handling is now better. We forget any prefix and
+      create them ourselves later in the response.
+- added superclass iface.py in DAV/ in order to make implementing
+  interface classes easier. See data.py for how to use it.
+  Also note that the way data.py handles things might have changed from
+  the previous release (if you don't like it wait for 1.0!)
+- added functions to iface.py which format creationdate and lastmodified
+- implemented HEAD
+
+- lots of bugfixes
+
+
+Changes since 0.3
+-----------------
+
+- removed hard coded base uri from davserver.py and replaced by
+  a reference to the dataclass. Added this to iface.py where you
+  have to define it in your subclass.
+- added davcmd.py which contains utility functions for copy and move
+- reimplemented DELETE and removed dependencies to pydom. move actual
+  delete method to davcmd.
+- implemented COPY
+- implemented MOVE
+- fixed bugs in errors.py, needs revisiting anyway..
+- URIs are now unquoted in davserver.py before being used
+- paths in data.py are quoted in system calls in order to support
+  blanks in pathnames (e.g. mkdir '%s' )
+- switched to exceptions when catching errors from the interface class
+- added exists() method to data.py
+- added more uri utility functions to utils.py
+- millenium bugfixes ;-)
diff --git a/DAV/AuthServer.py b/DAV/AuthServer.py
new file mode 100755
index 0000000..131da36
--- /dev/null
+++ b/DAV/AuthServer.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+
+"""
+    Authenticating HTTP Server
+
+    This module builds on BaseHTTPServer and implements 
+    basic authentication
+
+"""
+
+from DAV.utils import VERSION, AUTHOR
+__version__ = VERSION
+__author__  = AUTHOR
+
+import os
+import sys
+import time
+import socket
+import string
+import posixpath
+import SocketServer
+import BufferingHTTPServer
+import BaseHTTPServer
+import base64
+
+from string import atoi,split
+
+AUTH_ERROR_MSG="""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML><HEAD>
+<TITLE>401 Authorization Required</TITLE>
+</HEAD><BODY>
+<H1>Authorization Required</H1>
+This server could not verify that you
+are authorized to access the document
+requested.  Either you supplied the wrong
+credentials (e.g., bad password), or your
+browser doesn't understand how to supply
+the credentials required.<P>
+</BODY></HTML>"""
+
+class AuthRequestHandler:
+    """
+    Simple handler that use buffering and can check for auth headers 
+
+    In order to use it create a subclass of BufferedAuthRequestHandler
+    or BasicAuthRequestHandler depending on if you want to send
+    responses as block or as stream.
+
+    In your subclass you have to define the method get_userinfo(user,pw)
+    which should return 1 or None depending on whether the password was
+    ok or not. None means that the user is not authorized.
+    """
+
+    # False means no authentiation 
+    DO_AUTH=1
+
+    AUTH_ERROR_MSG="""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+    <HTML><HEAD>
+    <TITLE>401 Authorization Required</TITLE>
+    </HEAD><BODY>
+    <H1>Authorization Required</H1>
+    This server could not verify that you
+    are authorized to access the document
+    requested.  Either you supplied the wrong
+    credentials (e.g., bad password), or your
+    browser doesn't understand how to supply
+    the credentials required.<P>
+    </BODY></HTML>"""
+
+    server_version = "AuthHTTP/" + __version__
+
+    def _log(self, message):
+        pass
+
+    def handle(self):
+        """
+        Special handle method with buffering and authentication
+        """
+        
+        self.infp=open("/tmp/in.%s" %self.__class__, "a+")
+
+        self.infp.write("------------------------------------------------------------------------------\n")
+        self.raw_requestline = self.rfile.readline()
+        self.infp.write(self.raw_requestline)
+        self.request_version = version = "HTTP/0.9" # Default
+        requestline = self.raw_requestline
+
+        # needed by send_error
+        self.command = requestline
+
+        if requestline[-2:] == '\r\n':
+            requestline = requestline[:-2]
+        elif requestline[-1:] == '\n':
+            requestline = requestline[:-1]
+
+        self.requestline = requestline
+        words = string.split(requestline)
+        if len(words) == 3:
+            [command, path, version] = words
+            if version[:5] != 'HTTP/':
+                self.send_error(400, "Bad request version (%s)" % `version`)
+                return
+        elif len(words) == 2:
+            [command, path] = words
+            if command != 'GET':
+                self.send_error(400,
+                                "Bad HTTP/0.9 request type (%s)" % `command`)
+                return
+        else:
+            self.send_error(400, "Bad request syntax (%s)" % `requestline`)
+            return
+        
+        self.command, self.path, self.request_version = command, path, version
+        self.headers = self.MessageClass(self.rfile, 0)
+        self.infp.write(str(self.headers))
+
+        # test authentification
+        if self.DO_AUTH:
+            try:
+                a=self.headers["Authorization"]
+                m,up=string.split(a)
+                up2=base64.decodestring(up)
+                user,pw=string.split(up2,":")
+                if not self.get_userinfo(user,pw):
+                    self.send_autherror(401,"Authorization Required"); return
+            except:
+                self.send_autherror(401,"Authorization Required")
+                return
+
+        # check for methods starting with do_
+        mname = 'do_' + command
+        if not hasattr(self, mname):
+            self.send_error(501, "Unsupported method (%s)" % `command`)
+            return
+
+        method = getattr(self, mname)
+        method()
+
+        self.infp.flush()
+        self.infp.close()
+        self._flush()
+
+    def write_infp(self,s):
+        self.infp.write(str(s))
+        self.infp.flush()
+
+    def send_response(self,code, message=None):
+        """Override send_response to use the correct http version
+           in the response."""
+
+        self.log_request(code)
+        if message is None:
+            if self.responses.has_key(code):
+                message = self.responses[code][0]
+            else:
+                message = ''
+
+        if self.request_version != 'HTTP/0.9':
+            self._append("%s %s %s\r\n" %
+                             (self.request_version, str(code), message))
+
+        self.send_header('Server', self.version_string())
+        self.send_header('Date', self.date_time_string())
+        self.send_header('Connection', 'close')
+
+    def send_head(self):
+        """Common code for GET and HEAD commands.
+
+        This sends the response code and MIME headers.
+
+        Return value is either a file object (which has to be copied
+        to the outputfile by the caller unless the command was HEAD,
+        and must be closed by the caller under all circumstances), or
+        None, in which case the caller has nothing further to do.
+
+        """
+        path = self.translate_path(self.path)
+        if os.path.isdir(path):
+            self.send_error(403, "Directory listing not supported")
+            return None
+        try:
+            f = open(path, 'rb')
+        except IOError:
+            self.send_error(404, "File not found")
+            return None
+
+        self.send_response(200)
+        self.send_header("Content-type", self.guess_type(path))
+        self.end_headers()
+        return f
+
+    def send_autherror(self,code,message=None):
+        try:
+            short, long = self.responses[code]
+        except KeyError:
+            short, long = '???', '???'
+        if not message:
+            message = short
+        explain = long
+
+        emsg=self.AUTH_ERROR_MSG
+        self.log_error("code %d, message %s", code, message)
+        self.send_response(code, message)
+        self.send_header("WWW-Authenticate","Basic realm=\"PyWebDAV\"")
+        self.send_header("Content-Type", 'text/html')
+        self.end_headers()
+        
+        lines=split(emsg,"\n")
+        for l in lines:
+            self._append("%s\r\n" %l)
+
+    def get_userinfo(self,user):
+        """ return the password of the user 
+        Override this class to return the right password 
+        """
+        
+        # Always reject
+        return None
+
+class BufferedAuthRequestHandler(BufferingHTTPServer.BufferedHTTPRequestHandler,AuthRequestHandler):
+
+    def handle(self):
+        self._init_buffer()
+        AuthRequestHandler.handle(self)
+        self._flush()
+
+class BasicAuthRequestHandler(BufferingHTTPServer.BufferedHTTPRequestHandler,AuthRequestHandler):
+
+    def _append(self,s):
+        """ write the string to wfile """
+        self.wfile.write(s)
+
diff --git a/DAV/BufferingHTTPServer.py b/DAV/BufferingHTTPServer.py
new file mode 100644
index 0000000..5984d35
--- /dev/null
+++ b/DAV/BufferingHTTPServer.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+"""
+    Buffering HTTP Server
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""
+
+
+from DAV.utils import VERSION, AUTHOR
+__version__ = VERSION
+__author__  = AUTHOR
+
+from BaseHTTPServer import BaseHTTPRequestHandler
+
+class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
+    """
+    Buffering HTTP Request Handler
+
+    This class is an extension to the BaseHTTPRequestHandler
+    class which buffers the whole output and sends it at once
+    after the processing if the request is finished.
+
+    This makes it possible to work together with some clients
+    which otherwise would break (e.g. cadaver)
+
+    """
+
+
+    def _init_buffer(self):
+        """initialize the buffer. 
+        
+        If you override the handle() method remember to call
+        this (see below)
+        """
+        self.__buffer=""
+        self.__outfp=open("/tmp/out.%s" %self.__class__,"a+")
+    
+    def _append(self,s):
+        """ append a string to the buffer """
+        self.__buffer=self.__buffer+s
+
+    def _flush(self):
+        """ flush the buffer to wfile """
+        self.wfile.write(self.__buffer)
+        self.__outfp.write(self.__buffer)
+        self.__outfp.flush()
+        self.wfile.flush()
+        self.__buffer=""
+
+    def handle(self):
+        """ Handle a HTTP request """
+
+        self._init_buffer()
+        BaseHTTPRequestHandler.handle(self)
+        self._flush()
+
+    def send_header(self, keyword, value):
+        """Send a MIME header."""
+        if self.request_version != 'HTTP/0.9':
+            self._append("%s: %s\r\n" % (keyword, value))
+
+    def end_headers(self):
+        """Send the blank line ending the MIME headers."""
+        if self.request_version != 'HTTP/0.9':
+            self._append("\r\n")
+
+    def send_response(self, code, message=None):
+        self.log_request(code)
+
+        if message is None:
+            if self.responses.has_key(code):
+                message = self.responses[code][0]
+            else:
+                message = ''
+
+        if self.request_version != 'HTTP/0.9':
+            self._append("%s %s %s\r\n" %
+                    (self.protocol_version, str(code), message))
+        
+        self.send_header('Server', self.version_string())
+        self.send_header('Connection', 'close')
+        self.send_header('Date', self.date_time_string())
+
+    protocol_version="HTTP/1.1"
+
diff --git a/DAV/WebDAVServer.py b/DAV/WebDAVServer.py
new file mode 100644
index 0000000..6d89916
--- /dev/null
+++ b/DAV/WebDAVServer.py
@@ -0,0 +1,345 @@
+"""
+    Python WebDAV Server.
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+This module builds on AuthServer by implementing the standard DAV
+methods.
+
+Subclass this class and specify an IFACE_CLASS. See example.
+
+"""
+
+DEBUG=None
+
+from DAV.utils import VERSION, AUTHOR
+__version__ = VERSION
+__author__  = AUTHOR
+
+
+import os
+import sys
+import time
+import socket
+import string
+import posixpath
+import base64
+import AuthServer
+import urlparse
+import urllib
+
+from propfind import PROPFIND
+from delete import DELETE
+from davcopy import COPY
+from davmove import MOVE
+
+from string import atoi,split
+from status import STATUS_CODES
+from errors import *
+
+class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
+    """Simple DAV request handler with 
+    
+    - GET
+    - HEAD
+    - PUT
+    - OPTIONS
+    - PROPFIND
+    - PROPPATCH
+    - MKCOL
+
+    It uses the resource/collection classes for serving and
+    storing content.
+    
+    """
+
+    server_version = "DAV/" + __version__
+
+    ### utility functions
+    def _log(self, message):
+        pass
+
+    def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers={}):
+        """ send a body in one part """
+
+        self.send_response(code,message=msg)
+        self.send_header("Connection", "close")
+        self.send_header("Accept-Ranges", "bytes")
+        
+        for a,v in headers.items():
+            self.send_header(a,v)
+        
+        if DATA:
+            self.send_header("Content-Length", str(len(DATA)))
+            self.send_header("Content-Type", ctype)
+        else:
+            self.send_header("Content-Length", "0")
+        
+        self.end_headers()
+        if DATA:
+            self._append(DATA)
+
+    def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
+        """ send a body in chunks """
+        
+        self.responses[207]=(msg,desc)
+        self.send_response(code,message=msg)
+        self.send_header("Content-type", ctype)
+        self.send_header("Connection", "close")
+        self.send_header("Transfer-Encoding", "chunked")
+        self.end_headers()
+        self._append(hex(len(DATA))[2:]+"\r\n")
+        self._append(DATA)
+        self._append("\r\n")
+        self._append("0\r\n")
+        self._append("\r\n")
+
+    ### HTTP METHODS
+
+    def do_OPTIONS(self):
+        """return the list of capabilities """
+        self.send_response(200)
+        self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
+        self.send_header("Content-Type", "text/plain")
+        self.send_header("DAV", "1")
+        self.end_headers()
+
+    def do_PROPFIND(self):
+
+        dc=self.IFACE_CLASS
+
+        # read the body
+        body=None
+        if self.headers.has_key("Content-Length"):
+            l=self.headers['Content-Length']
+            body=self.rfile.read(atoi(l))
+        self.write_infp(body)
+
+        # which Depth?
+        if self.headers.has_key('Depth'):
+            d=self.headers['Depth']
+        else:
+            d="infinity"
+
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+        pf=PROPFIND(uri,dc,d)
+
+        if body:    
+            pf.read_propfind(body)
+
+        try:
+            DATA=pf.createResponse()
+            DATA=DATA+"\n"
+        except DAV_Error, (ec,dd):
+            return self.send_status(ec)
+
+        self.send_body_chunks(DATA,"207","Multi-Status","Multiple responses")
+
+    def do_GET(self):
+        """Serve a GET request."""
+
+        dc=self.IFACE_CLASS
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+
+        # get the last modified date
+        try:
+            lm=dc.get_prop(uri,"DAV:","getlastmodified")
+        except:
+            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
+        headers={"Last-Modified":lm}
+
+        # get the content type
+        try:
+            ct=dc.get_prop(uri,"DAV:","getcontenttype")
+        except:
+            ct="application/octet-stream"
+
+        # get the data
+        try:
+            data=dc.get_data(uri)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+            return 
+
+        # send the data
+        self.send_body(data,"200","OK","OK",ct,headers)
+
+    def do_HEAD(self):
+        """ Send a HEAD response """
+
+        dc=self.IFACE_CLASS
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+
+        # get the last modified date
+        try:
+            lm=dc.get_prop(uri,"DAV:","getlastmodified")
+        except:
+            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
+
+        headers={"Last-Modified":lm}
+
+        # get the content type
+        try:
+            ct=dc.get_prop(uri,"DAV:","getcontenttype")
+        except:
+            ct="application/octet-stream"
+
+        try:
+            data=dc.get_data(uri)
+            headers["Content-Length"]=str(len(data))
+        except DAV_NotFound:
+            self.send_body(None,"404","Not Found","")
+            return
+
+        self.send_body(None,"200","OK","OK",ct,headers)
+
+    def do_POST(self):
+        self.send_error(404,"File not found")
+
+    def do_MKCOL(self):
+        """ create a new collection """
+
+        dc=self.IFACE_CLASS
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+        try:
+            dc.mkcol(uri)
+            self.send_status(200)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+
+    def do_DELETE(self):
+        """ delete an resource """
+        dc=self.IFACE_CLASS
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+        dl=DELETE(uri,dc)
+        if dc.is_collection(uri):
+            res=dl.delcol()
+        else:
+            res=dl.delone()
+
+        if res:
+            self.send_status(207,body=res)
+        else:
+            self.send_status(204)
+
+    def do_PUT(self):
+        dc=self.IFACE_CLASS
+
+        # read the body
+        body=None
+        if self.headers.has_key("Content-Length"):
+            l=self.headers['Content-Length']
+            body=self.rfile.read(atoi(l))
+        uri=urlparse.urljoin(dc.baseuri,self.path)
+        uri=urllib.unquote(uri)
+
+        ct=None
+        if self.headers.has_key("Content-Type"):
+            ct=self.headers['Content-Type']
+        try:
+            dc.put(uri,body,ct)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+            return
+        self.send_status(201)
+
+    def do_COPY(self):
+        """ copy one resource to another """
+        try:
+            self.copymove(COPY)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+
+    def do_MOVE(self):
+        """ move one resource to another """
+        try:
+            self.copymove(MOVE)
+        except DAV_Error, (ec,dd):
+            self.send_status(ec)
+
+    def copymove(self,CLASS):
+        """ common method for copying or moving objects """
+        dc=self.IFACE_CLASS
+
+        # get the source URI
+        source_uri=urlparse.urljoin(dc.baseuri,self.path)
+        source_uri=urllib.unquote(source_uri)
+
+        # get the destination URI
+        dest_uri=self.headers['Destination']
+        dest_uri=urllib.unquote(dest_uri)
+
+        # Overwrite?
+        overwrite=1
+        result_code=204
+        if self.headers.has_key("Overwrite"):
+            if self.headers['Overwrite']=="F":
+                overwrite=None
+                result_code=201
+
+        # instanciate ACTION class
+        cp=CLASS(dc,source_uri,dest_uri,overwrite)
+
+        # Depth?
+        d="infinity"
+        if self.headers.has_key("Depth"):
+            d=self.headers['Depth']
+            
+            if d!="0" and d!="infinity": 
+                self.send_status(400)
+                return
+            
+            if d=="0":  
+                res=cp.single_action()
+                self.send_status(res)
+                return
+
+        # now it only can be "infinity" but we nevertheless check for a collection
+        if dc.is_collection(source_uri):
+            try:
+                res=cp.tree_action()
+            except DAV_Error, (ec,dd):
+                self.send_status(ec)
+                return
+        else:
+            try:
+                res=cp.single_action()
+            except DAV_Error, (ec,dd):
+                self.send_status(ec)
+                return
+
+        if res:
+            self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
+                            ctype='text/xml; charset="utf-8"')
+        else:
+            self.send_status(result_code)
+
+    def get_userinfo(self,user,pw):
+        """ Dummy method which lets all users in """
+
+        return 1
+
+    def send_status(self,code=200,mediatype='text/xml;  charset="utf-8"', \
+                                msg=None,body=None):
+
+        if not msg: msg=STATUS_CODES[code]
+        self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
+
diff --git a/DAV/__init__.py b/DAV/__init__.py
new file mode 100644
index 0000000..10abdb4
--- /dev/null
+++ b/DAV/__init__.py
@@ -0,0 +1,20 @@
+"""
+    python davserver
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+"""
+
+
diff --git a/DAV/constants.py b/DAV/constants.py
new file mode 100644
index 0000000..e858ac5
--- /dev/null
+++ b/DAV/constants.py
@@ -0,0 +1,18 @@
+"""
+
+constants definition
+
+
+"""
+
+# definition for resourcetype
+COLLECTION=1
+OBJECT=None
+DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
+
+
+# Request classes in propfind
+
+RT_ALLPROP=1
+RT_PROPNAME=2
+RT_PROP=3
diff --git a/DAV/davcmd.py b/DAV/davcmd.py
new file mode 100644
index 0000000..5c6be3f
--- /dev/null
+++ b/DAV/davcmd.py
@@ -0,0 +1,218 @@
+"""
+
+davcmd.py
+---------
+
+containts commands like copy, move, delete for normal
+resources and collections
+
+"""
+
+from string import split,replace,joinfields
+import urlparse
+
+from utils import create_treelist, is_prefix
+from errors import *
+
+def deltree(dc,uri,exclude={}):
+    """ delete a tree of resources 
+
+    dc  -- dataclass to use
+    uri -- root uri to delete
+    exclude -- an optional list of uri:error_code pairs which should not
+           be deleted.
+
+    returns dict of uri:error_code tuples from which
+    another method can create a multistatus xml element.
+
+    Also note that we only know Depth=infinity thus we don't have
+    to test for it.
+
+    """
+
+    tlist=create_treelist(dc,uri)
+    result={}
+
+    for i in range(len(tlist),0,-1):
+        problem_uris=result.keys()
+        element=tlist[i-1]
+    
+    # test here, if an element is a prefix of an uri which
+    # generated an error before.
+    # note that we walk here from childs to parents, thus
+    # we cannot delete a parent if a child made a problem.
+    # (see example in 8.6.2.1)
+    ok=1
+    for p in problem_uris:
+        if is_prefix(element,p):
+            ok=None
+            break
+    
+        if not ok: continue 
+    
+    # here we test for the exclude list which is the other way round!
+    for p in exclude.keys():
+        if is_prefix(p,element):
+            ok=None
+            break
+        
+        if not ok: continue 
+    
+    # now delete stuff
+    try:
+        delone(dc,element)
+    except DAV_Error, (ec,dd):  
+        result[element]=ec
+    
+    return result
+
+def delone(dc,uri):
+    """ delete a single object """
+    if dc.is_collection(uri):
+        dc.rmcol(uri)   # should be empty
+    else:
+        dc.rm(uri)
+
+###
+### COPY
+###
+
+# helper function
+
+def copy(dc,src,dst):
+    """ only copy the element 
+
+    This is just a helper method factored out from copy and
+    copytree. It will not handle the overwrite or depth header.
+    
+    """
+
+    # destination should have been deleted before
+    if dc.exists(dst): raise DAV_Error, 412
+
+    # source should exist also
+    if not dc.exists(src): raise DAV_NotFound
+
+    if dc.is_collection(src):
+        dc.copycol(src,dst) # an exception will be passed thru
+    else:
+        dc.copy(src,dst)  # an exception will be passed thru
+
+
+# the main functions
+
+def copyone(dc,src,dst,overwrite=None):
+    """ copy one resource to a new destination """
+
+    if overwrite and dc.exists(dst):
+        delres=deltree(dc,dst)
+    else:
+        delres={}
+
+    # if we cannot delete everything, then do not copy!
+    if delres: return delres
+    
+    try:
+        copy(dc,src,dst)    # pass thru exceptions
+    except DAV_Error, (ec,dd):
+        return ec
+
+def copytree(dc,src,dst,overwrite=None):
+    """ copy a tree of resources to another location
+
+    dc  -- dataclass to use
+    src -- src uri from where to copy
+    dst -- dst uri 
+    overwrite -- if 1 then delete dst uri before
+
+    returns dict of uri:error_code tuples from which
+    another method can create a multistatus xml element.
+
+    """
+
+
+    # first delete the destination resource
+    if overwrite and dc.exists(dst):
+        delres=deltree(dc,dst)
+    else:
+        delres={}
+
+    # if we cannot delete everything, then do not copy!
+    if delres: return delres
+
+    # get the tree we have to copy
+    tlist=create_treelist(dc,src)
+    result={}
+
+    # prepare destination URIs (get the prefix)
+    dpath=urlparse.urlparse(dst)[2]
+
+    for element in tlist:
+        problem_uris=result.keys()
+    
+    # now URIs get longer and longer thus we have
+    # to test if we had a parent URI which we were not
+    # able to copy in problem_uris which is the prefix
+    # of the actual element. If it is, then we cannot
+    # copy this as well but do not generate another error.
+    ok=1
+    for p in problem_uris:
+        if is_prefix(p,element):
+            ok=None
+            break
+    
+        if not ok: continue 
+
+    # now create the destination URI which corresponds to
+    # the actual source URI. -> actual_dst
+    # ("subtract" the base src from the URI and prepend the
+    # dst prefix to it.)
+    esrc=replace(element,src,"")
+    actual_dst=dpath+esrc
+
+    # now copy stuff
+    try:
+        copy(dc,element,actual_dst)
+    except DAV_Error, (ec,dd):  
+        result[element]=ec
+
+    return result
+        
+        
+
+###
+### MOVE
+###
+
+
+def moveone(dc,src,dst,overwrite=None):
+    """ move a single resource
+
+    This is done by first copying it and then deleting
+    the original.
+    """
+
+    # first copy it
+    copyone(dc,src,dst,overwrite)
+
+    # then delete it
+    dc.rm(src)
+      
+def movetree(dc,src,dst,overwrite=None):
+    """ move a collection
+
+    This is done by first copying it and then deleting
+    the original.
+
+    PROBLEM: if something did not copy then we have a problem
+    when deleting as the original might get deleted!
+    """
+
+    # first copy it
+    res=copytree(dc,src,dst,overwrite)
+
+    # then delete it
+    res=deltree(dc,src,exclude=res)
+
+    return res
+      
diff --git a/DAV/davcopy.py b/DAV/davcopy.py
new file mode 100755
index 0000000..ec44931
--- /dev/null
+++ b/DAV/davcopy.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+"""
+    python davserver
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+"""
+
+
+from xml.dom import ext
+from xml.dom.Document import Document
+
+import sys
+import string
+import urlparse
+import urllib
+from StringIO import StringIO
+
+import utils
+from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
+from errors import *
+from utils import create_treelist, quote_uri, gen_estring
+
+class COPY:
+    """ copy resources and eventually create multistatus responses
+
+    This module implements the COPY class which is responsible for
+    copying resources. Usually the normal copy work is done in the
+    interface class. This class only creates error messages if error
+    occur.
+
+    """
+
+
+    def __init__(self,dataclass,src_uri,dst_uri,overwrite):
+        self.__dataclass=dataclass
+        self.__src=src_uri
+        self.__dst=dst_uri
+        self.__overwrite=overwrite
+
+
+    def single_action(self):
+        """ copy a normal resources.
+
+        We try to copy it and return the result code.
+        This is for Depth==0
+
+        """
+
+        dc=self.__dataclass
+        base=self.__src
+
+        ### some basic tests
+        # test if dest exists and overwrite is false
+        if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
+        # test if src and dst are the same
+        # (we assume that both uris are on the same server!)
+        ps=urlparse.urlparse(self.__src)[2]
+        pd=urlparse.urlparse(self.__dst)[2]
+        if ps==pd: raise DAV_Error, 403
+
+        return dc.copyone(self.__src,self.__dst,self.__overwrite)
+
+        #return copyone(dc,self.__src,self.__dst,self.__overwrite)
+
+    def tree_action(self):
+        """ copy a tree of resources (a collection)
+
+        Here we return a multistatus xml element.
+
+        """
+        dc=self.__dataclass
+        base=self.__src
+
+        ### some basic tests
+        # test if dest exists and overwrite is false
+        if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
+        # test if src and dst are the same
+        # (we assume that both uris are on the same server!)
+        ps=urlparse.urlparse(self.__src)[2]
+        pd=urlparse.urlparse(self.__dst)[2]
+        if ps==pd: raise DAV_Error, 403
+        
+        
+        result=dc.copytree(self.__src,self.__dst,self.__overwrite)
+        #result=copytree(dc,self.__src,self.__dst,self.__overwrite)
+
+        if not result: return None
+
+        ###
+        ### create the multistatus XML element
+        ### (this is also the same as in delete.py.
+        ###  we might make a common method out of it)
+        ###
+
+        doc = Document(None)
+        ms=doc.createElement("D:multistatus")
+        ms.setAttribute("xmlns:D","DAV:")
+        doc.appendChild(ms)
+
+        for el,ec in result.items():
+                re=doc.createElement("D:response")
+                hr=doc.createElement("D:href")
+                st=doc.createElement("D:status")
+                huri=doc.createTextNode(quote_uri(el))
+                t=doc.createTextNode(gen_estring(ec))
+                st.appendChild(t)
+                hr.appendChild(huri)
+                re.appendChild(hr)
+                re.appendChild(st)
+                ms.appendChild(re)
+ 
+        sfile=StringIO()
+        ext.PrettyPrint(doc,stream=sfile)
+        s=sfile.getvalue()
+        sfile.close()
+        return s
+
diff --git a/DAV/davmove.py b/DAV/davmove.py
new file mode 100755
index 0000000..78c1e64
--- /dev/null
+++ b/DAV/davmove.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+
+"""
+    python davserver
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+"""
+
+
+import sys
+import string
+import urlparse
+import urllib
+from StringIO import StringIO
+
+import utils
+from constants import COLLECTION, OBJECT, DAV_PROPS
+from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
+from errors import *
+from utils import create_treelist, quote_uri, gen_estring, make_xmlresponse
+from davcmd import moveone, movetree
+
+class MOVE:
+    """ move resources and eventually create multistatus responses
+
+    This module implements the MOVE class which is responsible for
+    moving resources. 
+
+    MOVE is implemented by a COPY followed by a DELETE of the old
+    resource. 
+
+    """
+
+
+    def __init__(self,dataclass,src_uri,dst_uri,overwrite):
+        self.__dataclass=dataclass
+        self.__src=src_uri
+        self.__dst=dst_uri
+        self.__overwrite=overwrite
+
+
+    def single_action(self):
+        """ move a normal resources.
+
+        We try to move it and return the result code.
+        This is for Depth==0
+
+        """
+
+        dc=self.__dataclass
+        base=self.__src
+
+        ### some basic tests
+        # test if dest exists and overwrite is false
+        if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
+        # test if src and dst are the same
+        # (we assume that both uris are on the same server!)
+        ps=urlparse.urlparse(self.__src)[2]
+        pd=urlparse.urlparse(self.__dst)[2]
+        if ps==pd: raise DAV_Error, 403
+
+        return dc.moveone(self.__src,self.__dst,self.__overwrite)
+
+    def tree_action(self):
+        """ move a tree of resources (a collection)
+
+        Here we return a multistatus xml element.
+
+        """
+        dc=self.__dataclass
+        base=self.__src
+
+        ### some basic tests
+        # test if dest exists and overwrite is false
+        if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error,  412
+        # test if src and dst are the same
+        # (we assume that both uris are on the same server!)
+        ps=urlparse.urlparse(self.__src)[2]
+        pd=urlparse.urlparse(self.__dst)[2]
+        if ps==pd: raise DAV_Error,  403
+        
+        result=dc.movetree(self.__src,self.__dst,self.__overwrite)
+        if not result: return None
+
+        # create the multistatus XML element
+        return make_xmlresponse(result)
+
diff --git a/DAV/delete.py b/DAV/delete.py
new file mode 100755
index 0000000..ed017e5
--- /dev/null
+++ b/DAV/delete.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+"""
+
+    python davserver
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+"""
+import os
+import string
+import urllib
+from StringIO import StringIO
+
+from status import STATUS_CODES
+from utils import gen_estring, quote_uri, make_xmlresponse
+from davcmd import deltree
+
+class DELETE:
+
+    def __init__(self,uri,dataclass):
+        self.__dataclass=dataclass
+        self.__uri=uri
+
+    def delcol(self):
+        """ delete a collection """
+
+        dc=self.__dataclass
+        result=dc.deltree(self.__uri)
+
+        if not len(result.items()):
+            return None # everything ok
+
+        # create the result element
+        return make_xmlresponse(result)
+
+    def delone(self):
+        """ delete a resource """
+
+        dc=self.__dataclass
+        result=dc.delone(self.__uri)
+        
+        if not result: return None
+        if not len(result.items()):
+            return None # everything ok
+
+        # create the result element
+        return make_xmlresponse(result)
+
diff --git a/DAV/errors.py b/DAV/errors.py
new file mode 100755
index 0000000..c2b12d1
--- /dev/null
+++ b/DAV/errors.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+"""
+
+    Exceptions for the DAVserver implementation
+
+"""
+
+class DAV_Error(Exception):
+    """ in general we can have the following arguments:
+
+	1. the error code
+	2. the error result element, e.g. a <multistatus> element
+    """
+
+    def __init__(self,*args):
+        if len(args)==1:
+            self.args=(args[0],"")
+        else:
+            self.args=args
+    
+class DAV_Secret(DAV_Error):
+    """ the user is not allowed to know anything about it
+    
+    returning this for a property value means to exclude it
+    from the response xml element.
+    """
+
+    def __init__(self):
+        DAV_Error.__init__(self,0)
+        pass
+
+class DAV_NotFound(DAV_Error):
+    """ a requested property was not found for a resource """
+    
+    def __init__(self,*args):
+        if len(args):
+            DAV_Error.__init__(self,404,args[0])
+        else:
+            DAV_Error.__init__(self,404)
+
+        pass
+
+class DAV_Forbidden(DAV_Error):
+    """ a method on a resource is not allowed """
+    
+    def __init__(self,*args):
+        if len(args):
+            DAV_Error.__init__(self,403,args[0])
+        else:
+            DAV_Error.__init__(self,403)
+        pass
+
diff --git a/DAV/iface.py b/DAV/iface.py
new file mode 100644
index 0000000..dbe8e10
--- /dev/null
+++ b/DAV/iface.py
@@ -0,0 +1,255 @@
+"""
+
+basic interface class
+
+use this for subclassing when writing your own interface
+class.
+
+"""
+
+from errors import *
+
+import time
+from string import lower
+
+class dav_interface:
+    """ interface class for implementing DAV servers """
+
+    ### defined properties (modify this but let the DAV stuff there!)
+    ### the format is namespace: [list of properties]
+
+    PROPS={"DAV:" : ('creationdate', 
+                     'displayname', 
+                     'getcontentlanguage', 
+                     'getcontentlength', 
+                     'getcontenttype', 
+                     'getetag', 
+                     'getlastmodified', 
+                     'lockdiscovery', 
+                     'resourcetype', 
+                     'source', 
+                     'supportedlock'),
+           "NS2" : ("p1","p2")
+           }
+
+    # here we define which methods handle which namespace
+    # the first item is the namespace URI and the second one
+    # the method prefix
+    # e.g. for DAV:getcontenttype we call dav_getcontenttype()
+    M_NS={"DAV:" : "_get_dav",
+          "NS2"  : "ns2" }
+
+    def get_propnames(self,uri):
+        """ return the property names allowed for the given URI 
+
+        In this method we simply return the above defined properties
+        assuming that they are valid for any resource. 
+        You can override this in order to return a different set
+        of property names for each resource.
+        
+        """
+        return self.PROPS
+
+    def get_prop2(self,uri,ns,pname):
+        """ return the value of a property 
+
+        
+        """
+        if lower(ns)=="dav:": return self.get_dav(uri,pname)
+
+        raise DAV_NotFound
+
+    def get_prop(self,uri,ns,propname):
+        """ return the value of a given property
+
+        uri        -- uri of the object to get the property of
+        ns        -- namespace of the property
+        pname        -- name of the property
+        """
+        if self.M_NS.has_key(ns):
+            prefix=self.M_NS[ns]
+        else:
+            raise DAV_NotFound
+        mname=prefix+"_"+propname
+        try:
+                m=getattr(self,mname)
+                r=m(uri)
+                return r
+        except AttributeError:
+                raise DAV_NotFound
+
+    ###
+    ### DATA methods (for GET and PUT)
+    ###
+
+    def get_data(self,uri):
+        """ return the content of an object 
+
+        return data or raise an exception
+        
+        """
+        raise DAV_NotFound
+
+    def put(self,uri,data):
+        """ write an object to the repository 
+
+        return a result code or raise an exception
+        """
+
+        raise DAV_Forbidden
+
+    ###
+    ### Methods for DAV properties
+    ###
+
+    def _get_dav_creationdate(self,uri):
+        """ return the creationdate of a resource """
+        d=self.get_creationdate(uri)
+        # format it
+        return time.strftime("%Y-%m-%dT%H-%M-%SZ",time.localtime(d))
+
+    def _get_dav_getlastmodified(self,uri):
+        """ return the last modified date of a resource """
+        d=self.get_lastmodified(uri)
+        # format it
+        return time.strftime("%a, %d %b %Y %H:%M:%S %Z",time.localtime(d))
+
+
+    ###
+    ### OVERRIDE THESE!
+    ###
+
+    def get_creationdate(self,uri):
+        """ return the creationdate of the resource """
+        return time.time()
+
+    def get_lastmodified(self,uri):
+        """ return the last modification date of the resource """
+        return time.time()
+
+    
+    ###
+    ### COPY MOVE DELETE
+    ###
+
+    ### methods for deleting a resource
+
+    def rmcol(self,uri):
+        """ delete a collection 
+
+        This should not delete any children! This is automatically done
+        before by the DELETE class in DAV/delete.py
+
+        return a success code or raise an exception
+        
+        """
+        raise DAV_NotFound
+
+    def rm(self,uri):
+        """ delete a single resource 
+
+        return a success code or raise an exception
+        
+        """
+        raise DAV_NotFound
+
+    """
+
+    COPY/MOVE HANDLER
+
+    These handler are called when a COPY or MOVE method is invoked by
+    a client. In the default implementation it works as follows:
+
+    - the davserver receives a COPY/MOVE method
+    - the davcopy or davmove module will be loaded and the corresponding
+      class will be initialized
+    - this class parses the query and decides which method of the interface class
+      to call:
+
+      copyone for a single resource to copy
+      copytree for a tree to copy (collection)
+      (the same goes for move of course).
+
+    - the interface class has now two options:
+        1. to handle the action directly (e.g. cp or mv on filesystems)
+        2. to let it handle via the copy/move methods in davcmd.
+
+    ad 1) The first approach can be used when we know that no error can 
+          happen inside a tree or when the action can exactly tell which
+          element made which error. We have to collect these and return
+          it in a dict of the form {uri: error_code, ...}
+
+    ad 2) The copytree/movetree/... methods of davcmd.py will do the recursion
+          themselves and call for each resource the copy/move method of the
+          interface class. Thus method will then only act on a single resource.
+          (Thus a copycol on a normal unix filesystem actually only needs to do
+          an mkdir as the content will be copied by the davcmd.py function.
+          The davcmd.py method will also automatically collect all errors and
+          return the dictionary described above.
+          When you use 2) you also have to implement the copy() and copycol()
+          methods in your interface class. See the example for details.
+
+    To decide which approach is the best you have to decide if your application
+    is able to generate errors inside a tree. E.g. a function which completely
+    fails on a tree if one of the tree's childs fail is not what we need. Then
+    2) would be your way of doing it.
+    Actually usually 2) is the better solution and should only be replaced by
+    1) if you really need it.
+
+    The remaining question is if we should do the same for the DELETE method.
+
+    """
+
+    ### MOVE handlers
+
+    def moveone(self,src,dst,overwrite):
+        """ move one resource with Depth=0 """
+        return moveone(self,src,dst,overwrite)
+
+    def movetree(self,src,dst,overwrite):
+        """ move a collection with Depth=infinity """
+        return movetree(self,src,dst,overwrite)
+
+    ### COPY handlers
+
+    def copyone(self,src,dst,overwrite):
+        """ copy one resource with Depth=0 """
+        return copyone(self,src,dst,overwrite)
+
+    def copytree(self,src,dst,overwrite):
+        """ copy a collection with Depth=infinity """
+        return copytree(self,src,dst,overwrite)
+
+
+    ### low level copy methods (you only need these for method 2)
+    def copy(self,src,dst):
+        """ copy a resource with depth==0 
+
+        You don't need to bother about overwrite or not.
+        This has been done already.
+
+        return a success code or raise an exception if something fails
+        """
+        return 201
+
+
+    def copycol(self,src,dst):
+        """ copy a resource with depth==infinity 
+
+        You don't need to bother about overwrite or not.
+        This has been done already.
+
+        return a success code or raise an exception if something fails
+        """
+        return 201
+
+    ### some utility functions you need to implement
+
+    def exists(self,uri):
+        """ return 1 or None depending on if a resource exists """
+        return None # no
+
+    def is_collection(self,uri):
+        """ return 1 or None depending on if a resource is a collection """
+        return None # no
+
diff --git a/DAV/propfind.py b/DAV/propfind.py
new file mode 100755
index 0000000..9fdd981
--- /dev/null
+++ b/DAV/propfind.py
@@ -0,0 +1,334 @@
+#!/usr/bin/env python
+
+"""
+    python davserver
+    Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""
+
+
+from xml.dom import ext
+from xml.dom.Document import Document
+
+import sys
+import string
+import urlparse
+import urllib
+from StringIO import StringIO
+
+import utils
+from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
+from errors import *
+
+class PROPFIND:
+    """ parse a propfind xml element and extract props 
+
+    It will set the following instance vars:
+
+    request_class   : ALLPROP | PROPNAME | PROP
+    proplist    : list of properties
+    nsmap       : map of namespaces
+
+    The list of properties will contain tuples of the form
+    (element name, ns_prefix, ns_uri)
+    
+    
+    """
+
+
+    def __init__(self,uri,dataclass,depth):
+        self.request_type=None
+        self.nsmap={}
+        self.proplist={}
+        self.default_ns=None
+        self.__dataclass=dataclass
+        self.__depth=str(depth)
+        self.__uri=uri
+        self.__has_body=None    # did we parse a body?
+
+    def read_propfind(self,xml_doc):
+        self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
+
+    def createResponse(self):
+        """ create the multistatus response 
+
+        This will be delegated to the specific method
+        depending on which request (allprop, propname, prop)
+        was found.
+
+        If we get a PROPNAME then we simply return the list with empty
+        values which we get from the interface class
+
+        If we get an ALLPROP we first get the list of properties and then
+        we do the same as with a PROP method.
+
+        """
+
+        if self.request_type==RT_ALLPROP:
+            return self.create_allprop()
+        
+        if self.request_type==RT_PROPNAME:
+            return self.create_propname()
+        
+        if self.request_type==RT_PROP:
+            return self.create_prop()
+        
+        # no body means ALLPROP!
+        return self.create_allprop()
+    
+    def create_propname(self):
+        """ create a multistatus response for the prop names """
+        
+        dc=self.__dataclass
+        # create the document generator
+        doc = Document(None)
+        ms=doc.createElement("D:multistatus")
+        ms.setAttribute("xmlns:D","DAV:")
+        doc.appendChild(ms)
+
+        if self.__depth=="0":
+            pnames=dc.get_propnames(self.__uri)
+            re=self.mk_propname_response(self.__uri,pnames)
+            ms.appendChild(re)
+
+        elif self.__depth=="1":
+            pnames=dc.get_propnames(self.__uri)
+            re=self.mk_propname_response(self.__uri,pnames)
+            ms.appendChild(re)
+        
+        for newuri in dc.get_childs(self.__uri):
+            pnames=dc.get_propnames(newuri)
+            re=self.mk_propname_response(newuri,pnames)
+            ms.appendChild(re)
+        # *** depth=="infinity"  
+
+        sfile=StringIO()
+        ext.PrettyPrint(doc,stream=sfile)
+        s=sfile.getvalue()
+        sfile.close()
+        return s
+
+    def create_allprop(self):
+        """ return a list of all properties """
+        self.proplist={}
+        self.namespaces=[]
+        for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
+            self.proplist[ns]=plist
+            self.namespaces.append(ns)
+        
+        return self.create_prop()
+
+    def create_prop(self):
+        """ handle a <prop> request
+
+        This will
+
+        1. set up the <multistatus>-Framework
+
+        2. read the property values for each URI 
+           (which is dependant on the Depth header)
+           This is done by the get_propvalues() method.
+        
+        3. For each URI call the append_result() method
+           to append the actual <result>-Tag to the result
+           document.
+
+        We differ between "good" properties, which have been
+        assigned a value by the interface class and "bad" 
+        properties, which resulted in an error, either 404
+        (Not Found) or 403 (Forbidden).
+
+        """
+
+
+        # create the document generator
+        doc = Document(None)
+        ms=doc.createElement("D:multistatus")
+        ms.setAttribute("xmlns:D","DAV:")
+        doc.appendChild(ms)
+
+        if self.__depth=="0":
+            gp,bp=self.get_propvalues(self.__uri)
+            res=self.mk_prop_response(self.__uri,gp,bp,doc)
+            ms.appendChild(res)
+        
+        elif self.__depth=="1":
+            gp,bp=self.get_propvalues(self.__uri)
+            res=self.mk_prop_response(self.__uri,gp,bp,doc)
+            ms.appendChild(res)
+        
+        for newuri in self.__dataclass.get_childs(self.__uri):
+            gp,bp=self.get_propvalues(newuri)
+            res=self.mk_prop_response(newuri,gp,bp,doc)
+            ms.appendChild(res)
+        
+        sfile=StringIO()
+        ext.PrettyPrint(doc,stream=sfile)
+        s=sfile.getvalue()
+        sfile.close()
+        return s
+
+
+    def mk_propname_response(self,uri,propnames,doc):
+        """ make a new <prop> result element for a PROPNAME request 
+
+        This will simply format the propnames list.
+        propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
+        
+        """
+        re=doc.createElement("D:response")
+
+        # write href information
+        uparts=urlparse.urlparse(uri)
+        fileloc=uparts[2]
+        href=doc.createElement("D:href")
+        huri=doc.createTextNode(urllib.quote(fileloc))
+        href.appendChild(huri)
+        re.appendChild(href)
+        
+        ps=doc.createElement("D:propstat")
+        nsnum=0
+
+        for ns,plist in propnames.items():
+            # write prop element
+            pr=doc.createElement("D:prop")
+            nsp="ns"+str(nsnum)
+            pr.setAttribute("xmlns:"+nsp,ns)
+            nsnum=nsnum+1
+
+        # write propertynames
+        for p in plist:
+            pe=doc.createElement(nsp+":"+p)
+            pr.appendChild(pe)
+        
+        ps.appendChild(pr)
+        re.appendChild(ps)
+
+        return re
+
+    def mk_prop_response(self,uri,good_props,bad_props,doc):
+        """ make a new <prop> result element 
+
+        We differ between the good props and the bad ones for
+        each generating an extra <propstat>-Node (for each error
+        one, that means).
+        
+        """
+        re=doc.createElement("D:response")
+        # append namespaces to response
+        nsnum=0
+        for nsname in self.namespaces:
+            re.setAttribute("xmlns:ns"+str(nsnum),nsname)
+            nsnum=nsnum+1
+        
+        # write href information
+        uparts=urlparse.urlparse(uri)
+        fileloc=uparts[2]
+        href=doc.createElement("D:href")
+        huri=doc.createTextNode(urllib.quote(fileloc))
+        href.appendChild(huri)
+        re.appendChild(href)
+
+        # write good properties
+        if good_props:
+            ps=doc.createElement("D:propstat")
+            re.appendChild(ps)
+
+        gp=doc.createElement("D:prop")
+        for ns in good_props.keys():
+            ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+            for p,v in good_props[ns].items():
+                pe=doc.createElement(ns_prefix+str(p))
+                if p=="resourcetype":
+                    if v=="1":
+                        ve=doc.createElement("D:collection")
+                        pe.appendChild(ve)
+                else:
+                    ve=doc.createTextNode(str(v))
+                    pe.appendChild(ve)
+
+                gp.appendChild(pe)
+        
+        ps.appendChild(gp)
+        s=doc.createElement("D:status")
+        t=doc.createTextNode("HTTP/1.1 200 OK")
+        s.appendChild(t)
+        ps.appendChild(s)
+        re.appendChild(ps)
+
+        # now write the errors!
+        if len(bad_props.items()):
+
+            # write a propstat for each error code
+            for ecode in bad_props.keys():
+                ps=doc.createElement("D:propstat")
+                re.appendChild(ps)
+                bp=doc.createElement("D:prop")
+                ps.appendChild(bp)
+
+                for ns in bad_props[ecode].keys():
+                    ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+                
+                for p in bad_props[ecode][ns]:
+                    pe=doc.createElement(ns_prefix+str(p))
+                    bp.appendChild(pe)
+                
+                s=doc.createElement("D:status")
+                t=doc.createTextNode(utils.gen_estring(ecode))
+                s.appendChild(t)
+                ps.appendChild(s)
+                re.appendChild(ps)
+
+        # return the new response element
+        return re
+
+    def get_propvalues(self,uri):
+        """ create lists of property values for an URI 
+
+        We create two lists for an URI: the properties for
+        which we found a value and the ones for which we
+        only got an error, either because they haven't been
+        found or the user is not allowed to read them.
+        
+        """
+        good_props={}
+        bad_props={}
+
+        for (ns,plist) in self.proplist.items():
+            good_props[ns]={}
+            bad_props={}
+            ec = 0
+            for prop in plist:
+                try:
+                    r=self.__dataclass.get_prop(uri,ns,prop)
+                    good_props[ns][prop]=str(r)
+                except DAV_Error, error_code:
+                    ec=error_code[0]
+                
+                # ignore props with error_code if 0 (invisible)
+                if ec==0: continue
+                
+                if bad_props.has_key(ec):
+                    if bad_props[ec].has_key(ns):
+                        bad_props[ec][ns].append(prop)
+                    else:
+                        bad_props[ec][ns]=[prop]
+                else:
+                    bad_props[ec]={ns:[prop]}
+
+        return good_props, bad_props
+
diff --git a/DAV/status.py b/DAV/status.py
new file mode 100644
index 0000000..47e1b32
--- /dev/null
+++ b/DAV/status.py
@@ -0,0 +1,30 @@
+"""
+
+status codes for DAV services 
+
+
+"""
+
+
+STATUS_CODES={
+        102:    "Processing",
+        200:    "Ok",
+        201:    "Created",
+        204:    "No Content",
+        207:    "Multi-Status",
+        201:    "Created",
+        400:    "Bad Request",
+        403:    "Forbidden",
+        404:    "Not Found",
+        405:    "Method Not Allowed",
+        409:    "Conflict",
+        412:    "Precondition failed",
+        423:    "Locked",
+        415:    "Unsupported Media Type",
+        507:    "Insufficient Storage",
+        422:    "Unprocessable Entity",
+        423:    "Locked",
+        424:    "Failed Dependency",
+        502:    "Bad Gateway",
+        507:    "Insufficient Storage"
+}
diff --git a/DAV/utils.py b/DAV/utils.py
new file mode 100755
index 0000000..910619a
--- /dev/null
+++ b/DAV/utils.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+"""
+
+UTILITIES
+
+- parse a propfind request body into a list of props
+
+"""
+
+from xml.dom import ext
+from xml.dom.Document import Document
+from xml.dom.ext.reader import PyExpat
+from xml.dom import Node
+from xml.dom import NodeIterator, NodeFilter
+
+from string import lower, split, atoi, joinfields
+import urlparse
+from StringIO import StringIO
+
+from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
+from status import STATUS_CODES
+
+VERSION = '0.6'
+AUTHOR  = 'Simon Pamies <s.pamies at banality.de>'
+
+
+def gen_estring(ecode):
+    """ generate an error string from the given code """
+    ec=atoi(str(ecode))
+    if STATUS_CODES.has_key(ec):
+        return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
+    else:
+        return "HTTP/1.1 %s" %(ec)
+
+def parse_propfind(xml_doc):
+    """ parse an propfind xml file and return a list of props 
+
+    returns:
+        
+        request_type            -- ALLPROP, PROPNAME, PROP
+        proplist            -- list of properties found
+        namespaces            -- list of namespaces found
+    
+    """
+    doc = PyExpat.Reader().fromString(xml_doc)
+    snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
+
+    request_type=None
+    props={}
+    namespaces=[]
+
+    while 1:
+        curr_elem = snit.nextNode()
+        if not curr_elem: break
+        ename=fname=lower(curr_elem.nodeName)
+        if ":" in fname:
+            ename=split(fname,":")[1]
+        if ename=="prop": request_type=RT_PROP; continue
+        if ename=="propfind": continue
+        if ename=="allprop": request_type=RT_ALLPROP; continue
+        if ename=="propname": request_type=RT_PROPNAME; continue
+
+        # rest should be names of attributes
+
+        ns = curr_elem.namespaceURI
+        if props.has_key(ns):
+            props[ns].append(ename)
+        else:
+            props[ns]=[ename]
+            namespaces.append(ns)
+
+    return request_type,props,namespaces
+
+
+def create_treelist(dataclass,uri):
+    """ create a list of resources out of a tree 
+
+    This function is used for the COPY, MOVE and DELETE methods
+
+    uri - the root of the subtree to flatten
+
+    It will return the flattened tree as list
+
+    """
+    queue=[uri]
+    list=[uri]
+    while len(queue):
+        element=queue[-1]
+        if dataclass.is_collection(element):
+            childs=dataclass.get_childs(element)
+        else:
+            childs=[]
+        if len(childs):
+            list=list+childs
+        # update queue
+        del queue[-1]
+        if len(childs):
+            queue=queue+childs
+    return list
+
+def is_prefix(uri1,uri2):
+    """ returns 1 of uri1 is a prefix of uri2 """
+    if uri2[:len(uri1)]==uri1:
+        return 1
+    else:
+        return None
+
+def quote_uri(uri):
+    """ quote an URL but not the protocol part """
+    import urlparse
+    import urllib
+
+    up=urlparse.urlparse(uri)
+    np=urllib.quote(up[2])
+    return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
+
+def get_uriparentpath(uri):
+    """ extract the uri path and remove the last element """
+    up=urlparse.urlparse(uri)
+    return joinfields(split(up[2],"/")[:-1],"/")
+
+def get_urifilename(uri):
+    """ extract the uri path and return the last element """
+    up=urlparse.urlparse(uri)
+    return split(up[2],"/")[-1]
+
+def get_parenturi(uri):
+    """ return the parent of the given resource"""
+    up=urlparse.urlparse(uri)
+    np=joinfields(split(up[2],"/")[:-1],"/")
+    return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
+
+### XML utilities
+
+def make_xmlresponse(result):
+    """ construct a response from a dict of uri:error_code elements """
+    doc = Document.Document(None)
+    ms=doc.createElement("D:multistatus")
+    ms.setAttribute("xmlns:D","DAV:")
+    doc.appendChild(ms)
+
+    for el,ec in result.items():
+        re=doc.createElement("D:response")
+        hr=doc.createElement("D:href")
+        st=doc.createElement("D:status")
+        huri=doc.createTextNode(quote_uri(el))
+        t=doc.createTextNode(gen_estring(ec))
+        st.appendChild(t)
+        hr.appendChild(huri)
+        re.appendChild(hr)
+        re.appendChild(st)
+        ms.appendChild(re)
+
+    sfile=StringIO()
+    ext.PrettyPrint(doc,stream=sfile)
+    s=sfile.getvalue()
+    sfile.close()
+    return s
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ecce054
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,23 @@
+How to install python WebDAV server
+-------------------------------
+
+1. Check prerequisites
+
+    + *nix OS (including Mac OS X)
+      Will not run on Windows!
+      
+    + Python 2.x
+    + PyXML
+
+2. Run setup.py
+
+    > sudo python setup.py install
+
+4. Change to the PyDAVServer directory and start server with 
+
+    > ./server.py -h
+
+5. Enjoy
+
+Please send bugs and feature requests to
+    s.pamies at banality.de
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..40f4b8a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,480 @@
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+

+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+

+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
diff --git a/PyDAVServer/daemonize.py b/PyDAVServer/daemonize.py
new file mode 100755
index 0000000..0c59417
--- /dev/null
+++ b/PyDAVServer/daemonize.py
@@ -0,0 +1,128 @@
+'''
+    This module is used to fork the current process into a daemon.
+    Almost none of this is necessary (or advisable) if your daemon 
+    is being started by inetd. In that case, stdin, stdout and stderr are 
+    all set up for you to refer to the network connection, and the fork()s 
+    and session manipulation should not be done (to avoid confusing inetd). 
+    Only the chdir() and umask() steps remain as useful.
+    References:
+        UNIX Programming FAQ
+            1.7 How do I get my program to act like a daemon?
+                http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+        Advanced Programming in the Unix Environment
+            W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7.
+
+    History:
+      2005/06/23 by Simon Pamies
+      2001/07/10 by Juergen Hermann
+      2002/08/28 by Noah Spurrier
+      2003/02/24 by Clark Evans
+      
+      http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
+'''
+import sys, os, time
+from signal import SIGTERM
+
+def deamonize(stdout='/dev/null', stderr=None, stdin='/dev/null',
+              pidfile=None, startmsg = 'started with pid %s' ):
+    '''
+        This forks the current process into a daemon.
+        The stdin, stdout, and stderr arguments are file names that
+        will be opened and be used to replace the standard file descriptors
+        in sys.stdin, sys.stdout, and sys.stderr.
+        These arguments are optional and default to /dev/null.
+        Note that stderr is opened unbuffered, so
+        if it shares a file with stdout then interleaved output
+        may not appear in the order that you expect.
+    '''
+    # Do first fork.
+    try: 
+        pid = os.fork() 
+        if pid > 0: sys.exit(0) # Exit first parent.
+    except OSError, e: 
+        sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
+        sys.exit(1)
+        
+    # Decouple from parent environment.
+    os.chdir("/") 
+    os.umask(0) 
+    os.setsid() 
+    
+    # Do second fork.
+    try: 
+        pid = os.fork() 
+        if pid > 0: sys.exit(0) # Exit second parent.
+    except OSError, e: 
+        sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
+        sys.exit(1)
+    
+    # Open file descriptors and print start message
+    if not stderr: stderr = stdout
+    si = file(stdin, 'r')
+    so = file(stdout, 'a+')
+    se = file(stderr, 'a+', 0)
+    pid = str(os.getpid())
+    sys.stderr.write("\n%s\n" % startmsg % pid)
+    sys.stderr.flush()
+    if pidfile: file(pidfile,'w+').write("%s\n" % pid)
+
+    if sys.stdin.closed: sys.stdin = open('/dev/null', 'r')
+    if sys.stdout.closed: sys.stdout = open('/dev/null', 'a+')
+    if sys.stderr.closed: sys.stderr = open('/dev/null', 'a+')
+    
+    # Redirect standard file descriptors.
+    os.dup2(si.fileno(), sys.stdin.fileno())
+    os.dup2(so.fileno(), sys.stdout.fileno())
+    os.dup2(se.fileno(), sys.stderr.fileno())
+
+def startstop(stdout='/dev/null', stderr=None, stdin='/dev/null',
+              pidfile='pid.txt', startmsg = 'started with pid %s', action='start' ):
+    if action:
+        try:
+            pf  = file(pidfile,'r')
+            pid = int(pf.read().strip())
+            pf.close()
+        except IOError:
+            pid = None
+         
+        if 'stop' == action or 'restart' == action:
+            if not pid:
+                mess = "Could not stop, pid file '%s' missing.\n"
+                sys.stderr.write(mess % pidfile)
+                if 'stop' == action:
+                    sys.exit(1)
+                action = 'start'
+                pid = None
+            else:
+               try:
+                  while 1:
+                      os.kill(pid,SIGTERM)
+                      time.sleep(1)
+               except OSError, err:
+                  err = str(err)
+                  if err.find("No such process") > 0:
+                      os.remove(pidfile)
+                      if 'stop' == action:
+                          sys.exit(0)
+                      action = 'start'
+                      pid = None
+                  else:
+                      print str(err)
+                      sys.exit(1)
+        
+        if 'start' == action:
+            if pid:
+                mess = "Start aborded since pid file '%s' exists.\n"
+                sys.stderr.write(mess % pidfile)
+                sys.exit(1)
+
+            deamonize(stdout,stderr,stdin,pidfile,startmsg)
+            return
+
+        if 'status' == action:
+            if not pid:
+                sys.stderr.write('Status: Stopped\n')
+
+            else: sys.stderr.write('Status: Running\n')
+            sys.exit(0)
+
diff --git a/PyDAVServer/fileauth.py b/PyDAVServer/fileauth.py
new file mode 100755
index 0000000..002612e
--- /dev/null
+++ b/PyDAVServer/fileauth.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+"""
+Python WebDAV Server.
+Copyright (C) 1999 Christian Scholz (ruebe at aachen.heimat.de)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+This is an example implementation of a DAVserver using the DAV package.
+
+"""
+
+
+from DAV.WebDAVServer import DAVRequestHandler
+from fshandler import FilesystemHandler
+import sys
+
+class DAVAuthHandler(DAVRequestHandler):
+    """
+    Provide real auth check based on filesystem files
+    """
+    
+    # Do not forget to set IFACE_CLASS by caller
+    # ex.: IFACE_CLASS = FilesystemHandler('/tmp', 'http://localhost/')
+
+    verbose = False
+
+    def _log(self, message):
+        if self.verbose:
+            print >>sys.stderr, '>> (DAVAuthHandler) %s' % message
+
+    def get_userinfo(self,user,pw):
+    	""" authenticate user """
+
+        if user == self.auth_user and pw == self.auth_pass:
+            self._log('Successfully authenticated user %s' % user)
+            return 1
+
+        self._log('Authentication failed for user %s' % user)
+        return None
+
diff --git a/PyDAVServer/fshandler.py b/PyDAVServer/fshandler.py
new file mode 100755
index 0000000..f7a1d8a
--- /dev/null
+++ b/PyDAVServer/fshandler.py
@@ -0,0 +1,386 @@
+
+import sys
+import urlparse
+import os
+import time
+from string import joinfields, split, lower
+
+from DAV.constants import COLLECTION, OBJECT
+from DAV.errors import *
+from DAV.iface import *
+
+from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
+
+class FilesystemHandler(dav_interface):
+    """ 
+    Model a filesystem for DAV
+
+    This class models a regular filesystem for the DAV server
+
+    The basic URL will be http://localhost/
+    And the underlying filesystem will be /tmp
+
+    Thus http://localhost/gfx/pix will lead
+    to /tmp/gfx/pix
+
+    """
+
+    def __init__(self, directory, uri, verbose=False):
+        self.setDirectory(directory)
+        self.setBaseURI(uri)
+
+        # should we be verbose?
+        self.verbose = verbose
+        self._log('Initialized with %s-%s' % (directory, uri))
+
+    def _log(self, message):
+        if self.verbose:
+            print >>sys.stderr, '>> (FilesystemHandler) %s' % message
+
+    def setDirectory(self, path):
+        """ Sets the directory """
+
+        if not os.path.isdir(path):
+            raise Exception, '%s not must be a directory!' % path
+        
+        self.directory = path
+
+    def setBaseURI(self, uri):
+        """ Sets the base uri """
+
+        self.baseuri = uri
+
+    def uri2local(self,uri):
+        """ map uri in baseuri and local part """
+
+        uparts=urlparse.urlparse(uri)
+        fileloc=uparts[2][1:]
+        filename=os.path.join(self.directory,fileloc)
+        return filename
+
+    def local2uri(self,filename):
+        """ map local filename to self.baseuri """
+
+        pnum=len(split(self.directory,"/"))
+        parts=split(filename,"/")[pnum:]
+        sparts="/"+joinfields(parts,"/")
+        uri=urlparse.urljoin(self.baseuri,sparts)
+        return uri
+
+
+    def get_childs(self,uri):
+        """ return the child objects as self.baseuris for the given URI """
+        
+        fileloc=self.uri2local(uri)
+        filelist=[]
+
+        if os.path.exists(fileloc):
+            if os.path.isdir(fileloc):
+                try:
+                    files=os.listdir(fileloc)
+                
+                # silently ignore directories
+                # not allowed.
+                except:
+                    raise DAV_Forbidden
+                
+                for file in files:
+                    newloc=os.path.join(fileloc,file)
+                    filelist.append(self.local2uri(newloc))
+     
+                self._log('get_childs: Childs %s' % filelist)
+
+        return filelist
+
+    def get_data(self,uri):
+        """ return the content of an object """
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            if os.path.isfile(path):
+                s=""
+                fp=open(path,"r")
+                while 1:
+                    a=fp.read()
+                    if not a: break
+                    s=s+a
+                fp.close()
+                self._log('Serving content of %s' % uri)
+                return s
+            else:
+                # also raise an error for collections
+                # don't know what should happen then..
+                self._log('get_data: %s not found' % path)
+        
+        raise DAV_NotFound
+
+    def _get_dav_resourcetype(self,uri):
+        """ return type of object """
+        path=self.uri2local(uri)
+        if os.path.isfile(path):
+            return OBJECT
+        else:
+            return COLLECTION
+        
+    def _get_dav_displayname(self,uri):
+        raise DAV_Secret    # do not show
+
+    def _get_dav_getcontentlength(self,uri):
+        """ return the content length of an object """
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            if os.path.isfile(path):
+                s=os.stat(path)
+                return str(s[6])
+        
+        return '0'
+
+    def get_lastmodified(self,uri):
+        """ return the last modified date of the object """
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            s=os.stat(path)
+            date=s[8]
+            return date
+
+        raise DAV_NotFound
+
+    def get_creationdate(self,uri):
+        """ return the last modified date of the object """
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            s=os.stat(path)
+            date=s[9]
+            return date
+
+        raise DAV_NotFound
+
+    def _get_dav_getcontenttype(self,uri):
+        """ find out yourself! """
+
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            if os.path.isfile(path):
+                return "application/octet-stream"
+            else:
+                return "httpd/unix-directory"
+
+        else:
+            raise DAV_NotFound, 'Could not find %s' % path
+
+    def put(self,uri,data,content_type=None):
+        """ put the object into the filesystem """
+        path=self.uri2local(uri)
+        try:
+            fp=open(path,"w+")
+            fp.write(data)
+            fp.close()
+            self._log('put: Created %s' % uri)
+        except:
+            self._log('put: Could not create %s' % uri)
+            raise DAV_Error, 424
+
+        return None
+
+    def mkcol(self,uri):
+        """ create a new collection """
+        path=self.uri2local(uri)
+        # remove leading slash
+        if path[-1]=="/": path=path[:-1]
+
+        # test if file already exists
+        if os.path.exists(path):
+            raise DAV_Error,405
+
+        # test if parent exists
+        h,t=os.path.split(path)
+        if not os.path.exists(h):
+            raise DAV_Error, 409
+
+        # test, if we are allowed to create it
+        try:
+            os.system("mkdir '%s'" % path)
+            self._log('mkcol: Created new collection %s' % path)
+            return 201
+        except:
+            self._log('mkcol: Creation of %s denied' % path)
+            raise DAV_Forbidden
+
+    ### ?? should we do the handler stuff for DELETE, too ?
+    ### (see below)
+
+    def rmcol(self,uri):
+        """ delete a collection """
+        path=self.uri2local(uri)
+        if not os.path.exists(path):
+            raise DAV_NotFound
+
+        if not os.system("rmdir '%s'" %path):
+            return 204
+        else:
+            raise DAV_Forbidden # forbidden
+
+    def rm(self,uri):
+        """ delete a normal resource """
+        path=self.uri2local(uri)
+        if not os.path.exists(path):
+            raise DAV_NotFound
+        if not os.system("rm -f '%s'" %path):
+            return 204
+        else:
+            raise DAV_Forbidden # forbidden
+
+    ###
+    ### DELETE handlers (examples)
+    ### (we use the predefined methods in davcmd instead of doing
+    ### a rm directly
+    ###
+
+    def delone(self,uri):
+        """ delete a single resource 
+
+        You have to return a result dict of the form
+        uri:error_code
+        or None if everything's ok
+        
+        """
+        return delone(self,uri)
+
+    def deltree(self,uri):
+        """ delete a collection 
+
+        You have to return a result dict of the form
+        uri:error_code
+        or None if everything's ok
+        """
+
+        return deltree(self,uri)
+
+
+    ###
+    ### MOVE handlers (examples)
+    ###
+
+    def moveone(self,src,dst,overwrite):
+        """ move one resource with Depth=0 
+
+        an alternative implementation would be
+
+        result_code=201
+        if overwrite: 
+            result_code=204
+            r=os.system("rm -f '%s'" %dst)
+            if r: return 412
+        r=os.system("mv '%s' '%s'" %(src,dst))
+        if r: return 412
+        return result_code
+       
+        (untested!). This would not use the davcmd functions
+        and thus can only detect errors directly on the root node.
+        """
+        return moveone(self,src,dst,overwrite)
+
+    def movetree(self,src,dst,overwrite):
+        """ move a collection with Depth=infinity
+
+        an alternative implementation would be
+
+        result_code=201
+        if overwrite: 
+            result_code=204
+            r=os.system("rm -rf '%s'" %dst)
+            if r: return 412
+        r=os.system("mv '%s' '%s'" %(src,dst))
+        if r: return 412
+        return result_code
+       
+        (untested!). This would not use the davcmd functions
+        and thus can only detect errors directly on the root node"""
+
+        return movetree(self,src,dst,overwrite)
+
+    ###
+    ### COPY handlers
+    ###
+
+    def copyone(self,src,dst,overwrite):
+        """ copy one resource with Depth=0 
+
+        an alternative implementation would be
+
+        result_code=201
+        if overwrite: 
+            result_code=204
+            r=os.system("rm -f '%s'" %dst)
+            if r: return 412
+        r=os.system("cp '%s' '%s'" %(src,dst))
+        if r: return 412
+        return result_code
+       
+        (untested!). This would not use the davcmd functions
+        and thus can only detect errors directly on the root node.
+        """
+        return copyone(self,src,dst,overwrite)
+
+    def copytree(self,src,dst,overwrite):
+        """ copy a collection with Depth=infinity
+
+        an alternative implementation would be
+
+        result_code=201
+        if overwrite: 
+            result_code=204
+            r=os.system("rm -rf '%s'" %dst)
+            if r: return 412
+        r=os.system("cp -r '%s' '%s'" %(src,dst))
+        if r: return 412
+        return result_code
+       
+        (untested!). This would not use the davcmd functions
+        and thus can only detect errors directly on the root node"""
+
+        return copytree(self,src,dst,overwrite)
+
+    ###
+    ### copy methods.
+    ### This methods actually copy something. low-level
+    ### They are called by the davcmd utility functions
+    ### copytree and copyone (not the above!)
+    ### Look in davcmd.py for further details.
+    ###
+
+    def copy(self,src,dst):
+        """ copy a resource from src to dst """
+        srcfile=self.uri2local(src)
+        dstfile=self.uri2local(dst)
+        try:
+            os.system("cp '%s' '%s'" %(srcfile,dstfile))
+        except:
+            raise DAV_Error, Forbidden
+
+    def copycol(self,src,dst):
+        """ copy a collection.
+
+        As this is not recursive (the davserver recurses itself)
+        we will only create a new directory here. For some more
+        advanced systems we might also have to copy properties from
+        the source to the destination.
+        """
+
+        return self.mkcol(dst)
+
+
+    def exists(self,uri):
+        """ test if a resource exists """
+        path=self.uri2local(uri)
+        if os.path.exists(path):
+            return 1
+        return None
+
+    def is_collection(self,uri):
+        """ test if the given uri is a collection """
+        path=self.uri2local(uri)
+        if os.path.isdir(path):
+            return 1
+        else:
+            return 0
+
diff --git a/PyDAVServer/server.py b/PyDAVServer/server.py
new file mode 100755
index 0000000..552e90e
--- /dev/null
+++ b/PyDAVServer/server.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+
+"""
+Python WebDAV Server.
+Copyright (C) 1999-2005 Christian Scholz (cs at comlounge.net)
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+This is an example implementation of a DAVserver using the DAV package.
+
+"""
+
+import getopt, sys, os
+import BaseHTTPServer
+
+try:
+    import DAV
+except ImportError:
+    print 'DAV package not found! Please install into site-packages or set PYTHONPATH!'
+    sys.exit(2)
+
+try:
+    from xml.dom import ext
+except ImportError:
+    print 'PyXML not found! Get it from http://pyxml.sourceforge.net/'
+    sys.exit(2)
+
+from DAV.utils import VERSION, AUTHOR
+__version__ = VERSION
+__author__  = AUTHOR
+
+from fileauth import DAVAuthHandler
+from fshandler import FilesystemHandler
+from daemonize import startstop
+
+def runserver(
+         port = 8008, host='localhost',
+         directory='/tmp',
+         verbose = False,
+         noauth = False,
+         user = '',
+         password = '',
+         handler = DAVAuthHandler,
+         server = BaseHTTPServer.HTTPServer):
+
+    directory = directory.strip()
+    host = host.strip()
+   
+    if not os.path.isdir(directory):
+        print >>sys.stderr, '>> ERROR: %s is not a valid directory!' % directory
+        return sys.exit(233)
+    
+    # basic checks against wrong hosts
+    if host.find('/') != -1 or host.find(':') != -1:
+        print >>sys.stderr, '>> ERROR: Malformed host %s' % host
+        return sys.exit(233)
+
+    # no root directory
+    if directory == '/':
+        print >>sys.stderr, '>> ERROR: Root directory not allowed!'
+        sys.exit(233)
+        
+    # dispatch directory and host to the filesystem handler
+    handler.IFACE_CLASS = FilesystemHandler(directory, 'http://%s:%s/' % (host, port), verbose )
+
+    # put some extra vars
+    handler.verbose = verbose
+    if noauth:
+        print >>sys.stderr, '>> ATTENTION: Authentication disabled!'
+        handler.DO_AUTH = False
+
+    else:
+        handler.auth_user = user
+        handler.auth_pass = password
+    
+    print >>sys.stderr, '>> Serving data from %s' % directory
+   
+    # initialize server on specified port
+    runner = server( (host, port), handler )
+    print >>sys.stderr, '>> Listening on %s (%i)' % (host, port)
+
+    if verbose:
+        print >>sys.stderr, '>> Verbose mode ON'
+
+    print ''
+
+    try:
+        runner.serve_forever()
+    except KeyboardInterrupt:
+        print >>sys.stderr, '\n>> Killed by user'
+
+usage = """PyWebDAV server (version %s)
+Standalone WebDAV server based on python
+
+Usage: ./server.py [OPTIONS]
+Parameters:
+    -D, --directory Directory where to serve data from
+                    The user that runs this server must have permissions
+                    on that directory. NEVER run as root!
+                    Default directory is /tmp
+
+    -H, --host      Host where to listen on (default: localhost)
+    -P, --port      Port to bind server to  (default: 8008)
+    -u, --user      Username for authentication
+    -p, --password  Password for given user
+    -n, --noauth    Pass parameter if server should not ask for authentication  
+    -d, --daemon    Make server act like a daemon. That means that it is going
+                    to the background mode. All messages are redirected to
+                    logfiles (default: /tmp/pydav.log and /tmp/pydav.err).
+                    You need to pass one of the following values to this parameter
+                        start   - Start daemon
+                        stop    - Stop daemon
+                        restart - Restart complete server
+                        status  - Returns status of server
+                        
+    -v, --verbose   Be verbose
+    -h, --help      Show this screen
+    
+Please send bug reports and feature requests to %s
+""" % (__version__, __author__)
+
+if __name__ == '__main__':
+
+    verbose = False
+    directory = '/tmp'
+    port = 8008
+    host = 'localhost'
+    noauth = False
+    user = ''
+    password = ''
+    daemonize = False
+    daemonaction = 'start'
+    
+    # parse commandline
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], 'P:D:H:d:u:p:nvh', 
+                ['host=', 'port=', 'directory=', 'user=', 'password=','daemon=', 'noauth', 'help', 'verbose'])     
+    except getopt.GetoptError, e:
+        print usage
+        print '>>>> ERROR: %s' % str(e)
+        sys.exit(2)
+    
+    for o,a in opts:
+        if o in ['-D', '--directory']:
+            directory = a
+
+        if o in ['-H', '--host']:
+            host = a
+
+        if o in ['-P', '--p']:
+            port = a
+
+        if o in ['-v', '--verbose']:
+            verbose = True
+
+        if o in ['-h', '--help']:
+            print usage
+            sys.exit(2)
+    
+        if o in ['-n', '--noauth']:
+            noauth = True
+
+        if o in ['-u', '--user']:
+            user = a
+
+        if o in ['-p', '--password']:
+            password = a
+
+        if o in ['-d', '--daemon']:
+            daemonize = True
+            daemonaction = a
+
+    print >>sys.stderr, 'Starting up PyWebDAV server (version %s)' % __version__
+    if not noauth and daemonaction != 'status':
+        if not user:
+            print >>sys.stderr, '>> ERROR: Please specify an username or pass parameter --noauth'
+            sys.exit(3)
+  
+    if daemonaction == 'status':
+        print >>sys.stdout, 'Checking for status...'
+   
+    if type(port) == type(''):
+        port = int(port.strip())
+   
+    if daemonize:
+        startstop(stdout='/tmp/pydav.log', 
+                    stderr='/tmp/pydav.err', 
+                    pidfile='/tmp/pydav.pid', 
+                    startmsg='>> Started PyWebDAV (PID: %s)',
+                    action=daemonaction)
+  
+    # start now
+    runserver(port, host, directory, verbose, noauth, user, password)
diff --git a/README b/README
new file mode 100644
index 0000000..aa10c2b
--- /dev/null
+++ b/README
@@ -0,0 +1,107 @@
+
+I want to introduce you to my webdav server implementation in Python. Actually
+it's supposed to be a generic class which can be used for all sorts of servers.
+Thus the actual webdav part and the data interface part is implemented in two
+modules. All you have to do in order to create a wevdav server for you 
+application is to write an interface class.
+
+A working WebDAV Server is included in the PyDAVServer package. Feel free to
+extend it to your needs. Please send suggestions and bugfixes to the author(s).
+
+AUTHOR(s)
+------
+
+Simon Pamies (Current maintainer)
+Bielefeld, Germany
+s.pamies at banality.de
+
+Christian Scholz
+Aachen, Germany
+mrtopf at webdav.de
+
+REQUIREMENTS
+------------
+
+- Python 2.0  or higher (www.python.org)
+- PyXML 0.66 (pyxml.sourceforge.net)
+
+INSTALLATION
+------------
+
+see INSTALL file
+
+What is WebDAV?
+---------------
+
+http://www.webdav.org.
+http://www.ietf.org/rfc/rfc2518.txt
+
+What is Python?
+---------------
+
+http://www.python.org.
+http://diveintopython.org/
+
+Contents
+--------
+
+Here is a little overview of the package:
+
+A. In the DAV/ package:
+
+    1. BufferingHTTPServer
+       
+       This is the same as the normal BasicHTTPServer but instead of
+       directly sending each string over the network this implementation
+       caches it and sends it at once after finishing one request.
+
+       This has the advantage that clients like cadaver don't break as
+       they want to peek at the next lines when encountering e.g. a header.
+
+    2. AuthHTTPServer
+       
+       This works on top of either the BasicHTTPServer or the 
+       BufferingHTTPServer and implements basic authentication.
+
+    3. WebDAVServer
+       This server uses AuthHTTPServer for the base functionality. It also uses
+       a dav interface class for interfacing with the actual data storage (e.g.
+       a filesystem or a database).
+
+B. In the PyDAVServer directory:
+
+    1. server.py
+       Main file for server. Serves as a start point for the WebDAV server
+
+    2. fshandler.py
+       Backend for the DAV server. Makes him serving content from the filesystem.
+       Have a deeper look at it if you want to implement backends to other data sources
+
+    3. fileauth.py
+       Handler for authentication. Nothing very special about it.
+
+
+NOTES
+-----
+
+Right now some parts are missing like handling of ALLPROP and PROPNAMES as 
+the whole proppatch method. Also not everything is tested and the return 
+codes might not be the right ones in every case.
+
+I plan to adjust things in order to work with my groupware project which 
+I then will release (actually it's my thesis I am working on). 
+
+Look inside the file TODO for things which needs to be done and will be done 
+in the near future. 
+
+If you find bugs (many!) or have suggestions then please email the maintainer
+
+Thanks :)
+
+LICENSE
+-------
+
+see LICENSE file
+
+That's all there is to it!
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..8fcde44
--- /dev/null
+++ b/TODO
@@ -0,0 +1,49 @@
+GENERAL
+-------
+
+- web page needs to get done:
+	- Download
+	- News
+	- TODO list
+	- Changes
+	- Name
+
+MOVE
+----
+
+needs to get implemented (will get used for renaming files and dirs)
+
+
+PROPFIND
+--------
+
+- PROPNAMES need to get implemented
+- only DAV properties are possible right now
+- Depth=infinity should be implemented
+
+
+GET
+---
+
+- guessing type?
+
+
+HEAD
+----
+
+to be done
+
+
+PROPPATCH
+---------
+
+to be done (not that important as only DAV props are supported right now which
+you cannot change)
+
+data.py
+-------
+
+- implement proppatch? As example we might use some sort of database for 
+  storing..  (to much trouble for a simple example? Better show it with 
+  the groupware stuff?)
+
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..5a2a580
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.6
diff --git a/doc/interface_class b/doc/interface_class
new file mode 100644
index 0000000..74946b4
--- /dev/null
+++ b/doc/interface_class
@@ -0,0 +1,133 @@
+How to write an interface class
+-------------------------------
+
+(this information might be a little out of date. See data.py for more
+details).
+
+The interface class of davserver is the interface between the actual data
+and the davserver. The davserver will ask this class every time it needs
+information about the underlying data (e.g. a filesystem or a database).
+
+So how do you write such a class?
+
+Simply take the existing class which models a normal unix filesystem
+and change it. You actually have implement the following methods:
+
+
+
+get_childs(self,uri)
+
+    This method should return a list of all childs for the
+    object specified by the given uri.
+
+    The childs should be specified as normal URIs.
+
+
+get_props(self,uri,values=None,all=None,proplist=[])
+
+    This method will be called when the davserver needs information
+    about properties for the object specified with the given uri.
+    The parameters are as follows:
+
+    values	-- ?? cannot remember ;-)
+    all		-- if set to 1 return all properties
+    proplist	-- alternatively you can give get a list of
+		   properties to return 
+
+    The result of this method should be a dictionary of the form
+
+    props[propname]=propvalue
+
+    Note that in the example class this one is simply a dummy class
+    as only DAV properties are handled which have their own methods
+    (see below).
+
+
+get_data(self,uri)
+
+    This method will be called when the content of an object is needed.
+    Thus this method should return a data string.
+
+
+get_dav(self,uri,propname)
+    
+    This method will be called when the server needs access to a DAV
+    property. In the example implementation it will simply delegate it
+    to the corresponding _get_dav_<propname> method. You maybe should
+    handle it the same way.
+
+
+_get_dav_<propname>(uri)
+    
+    These methods will be called by get_dav() when the value of a DAV
+    property is needed. The defined properties are:
+
+    - resourcetype (empty or <collection> if the object is a collection)
+    - getcontentlength 
+    - getcontenttype
+    - getlastmodified
+    - creationdate
+
+
+
+put(self,uri,data)
+
+    This method will write data into the given object.
+    It should return the result code (e.g. 424 if an error occured and
+    None if everythin was ok).
+
+
+mkcol(self,uri)
+
+    This method will be called when the MKCOL WEBDAV method was received
+    by the server. The interface class has to test
+    - if the parents of the uri all exists. If not, return 409
+    - if the object already exists. If so, return 405
+    - if it is allowed to create the collection. If not, return 403
+    If everything is ok, then create the new collection (aka directory)
+    and return 201. 
+
+
+rmcol(self,uri)
+
+    This method is called when a collection needs to be removed.
+    Only the collection should be removed, no children as the davserver
+    is automatically iterating over all children and calling rm/rmcol
+    for each of them (because it needs a result code for every deleted
+    object).
+    If the user is not allowed to delete the collection then an
+    403 should be returned
+
+
+rm(self,uri)
+    
+    This is the same for single objects, the same as above applies.
+
+
+is_collection(self,uri)
+
+    This one simply returns 1 if the object specified by the uri
+    is an object or 0 if it isn't.
+
+
+
+So these are basically the methods which need to get implemented. While writing
+this I also noticed some problems:
+
+- the actual user is not know to the interface class. This should be changed as
+  it might be important when testing if an action is allowed or not. Also some
+  implementations might need a user in order to decide what to return (e.g.
+  GROUP.lounge will need this.)
+
+- the return of result codes is not standardized throughout the interface class.
+  This should be changed.
+
+- The should be a super interface class to derive from in order to handle some
+  common things like get_dav() or property handling in general. Some things
+  then also might me moved from propfind.py/devserver.py into this class.
+
+
+
+As the changes above might break existing code you have been warned with this
+message :)
+
diff --git a/doc/walker b/doc/walker
new file mode 100644
index 0000000..cb33a27
--- /dev/null
+++ b/doc/walker
@@ -0,0 +1,50 @@
+Walker methods
+--------------
+
+In the COPY, DELETE and MOVE methods we need to walk over
+a tree of resources and collections in order to copy, delete
+or move them. 
+
+The difference between all these walks is only that we perform
+a different action on the resources we visit. Thus it might be
+the simplest solution to provide a walker class or method to
+do that work and give it a function to perform before starting.
+
+
+Way of walking
+--------------
+
+When we delete things we should do it bottom up but when we copy
+or move things we should create resources top down. Thus we actually
+need 2 methods.
+
+But the following method might work: We create a list of all the nodes
+in the tree in tree order (means top down, left to right). When
+we walk over this list from begin to end we can copy and when we
+move backwards we can delete.
+
+Thus we need an indicator for the direction and the method to
+perform on it.
+
+
+Here the iterative approach (in order to save memory):
+dc=dataclass
+queue=list=[start_uri]
+while len(queue):
+    element=queue[-1]
+    childs=dc.get_childs(element)
+    if childs:
+	list=list+childs
+    # update queue
+    del queue[-1]   
+    if childs:
+	queue=queue+childs
+
+
+(first try..)
+
+
+
+
+
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..ed160b8
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+setup(name='PyDAV',
+      description='WebDAV library and server for python',
+      author='Christian Scholz',
+      author_email='cs at comlounge.net',
+      maintainer='Simon Pamies',
+      maintainer_email='s.pamies at banality.de',
+      url='http://www.webdav.de',
+      version='0.6',
+      classifiers = [
+          'Development Status :: 0.6',
+          'Environment :: Console',
+          'Environment :: Web Environment',
+          'Intended Audience :: End Users',
+          'Intended Audience :: Developers',
+          'Intended Audience :: System Administrators',
+          'License :: GPL',
+          'Operating System :: MacOS :: MacOS X',
+          'Operating System :: POSIX',
+          'Programming Language :: Python',
+          ],
+      keywords = ['webdav',
+                  'server',
+                  'dav',
+                  'standalone',
+                  'library',
+                  'gpl',
+                  'http',
+                  'rfc2518',
+                  'rfc 2518'],
+      packages=['DAV', ],
+      )
-- 
pywebdav



More information about the tryton-debian-vcs mailing list