Python WebDAV server and client
7 November 1999
This release includes
Also included is an XML API.
Legal Notice
Copyright (c) 1997-1999 Xerox Corporation. All Rights Reserved.
Permission is hereby granted to store, compile and execute this code
for NON-COMMERCIAL purposes only. The above copyright notice and this and
the following paragraph must be retained in all files and in
any derivative works.
This code is based on Xerox research technology and is made available AS IS.
Xerox Corporation makes no representations whatsoever with respect
to the use or execution of this object code. XEROX DISCLAIMS ALL
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE, AND ALSO INCLUDING WITHOUT LIMITATION ANY EXPRESS OR
IMPLIED WARRANTIES OF NON-INFRINGEMENT. NOTWITHSTANDING ANY OTHER
PROVISION CONTAINED HEREIN, ANY LIABILITY FOR DAMAGES RESULTING FROM
THIS OBJECT CODE OR ITS USE IS EXPRESSLY DISCLAIMED, WHETHER ARISING
IN CONTRACT, TORT (INCLUDING NEGLIGENCE), OR STRICT LIABILITY, EVEN
IF XEROX CORPORATION IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
Historical Note
This software was first written by me at Xerox PARC in 1997-1999.
It reflects the state of the standard, as I understood it at that time.
Distribution
Distributed as a zip archive.
Feedback
Send comments to jrd 3 at alum dot mit dot edu.
Server
The server is a class 2 server, supporting locking.
The current version is PyDAV 1.2.
Limitations and bugs of 1.2
- No authentication whatsoever.
- No support for Etags.
- No lock refreshing.
- The opaque lock tokens are non-standard (just time stamps)
- No UTF-16 support.
Features
- Temp-locked null resources (section 7.4) are supported. The
listing returned by GET on a collection includes such resources.
- Server supports exclusive write locks, with both Infinite and
Seconds timeouts.
- DAV:getcontenttype is a writable property. No syntax checking is performed.
- Generic architecture: The server implementation has a very clean
boundary between the portion that handles WebDAV protocol and the
underlying persistent store for resources and properties. Hence it
should be possible to move the WebDAV front end to other kinds of
stores, e.g. to LDAP, a relational database, etc.
Notes
The rules for determining the content type of a resource are:
- If the client has stored a value for DAV:getcontenttype, that value is returned.
- If the name of the extension of the file is known to the server (e.g. .html, .gif, etc), then an appropriate MIME type is returned.
- Otherwise, text/plain is returned
About the implementation
The server is implemented in Python, and runs on Python 1.4 or later.
It runs on Unix and Windows. The persistent store for resources is
is a Posix file, properties are stored in a dbm database.
The COPY operation requires a Posix-style command named "cp" that understands Posix pathnames, the Windows COPY command won't work. Installing Cygwin might work, I have not tried.
Installation
- Pick a directory for WebDAV's persistent store. This is where it will store its properties database.
- Pick a file system directory for the root collection WebDAV will serve. This is not the same as the persistent store.
- Copy webdav.ini.template to webdav.ini
- Edit webdav.ini
- set working to the persistent store directory
- set rootmap to include the root Web directory
- optionally, edit port.
- You may wish to edit other fields in the ini file.
Running it
- python webdav.py
client library davlib
davlib defines the class DAV. The constructor requires two
arguments, the host name (string) and the port (integer).
ds = davlib.DAV ('sandbox.parc.xerox.com', 8080)
The DAV class defines the following methods
- Get uri
- returns a stream and a dictionary of headers
- Head uri
- returns the dictionary of headers.
- Put uri, body, mediatype, state
- stores an arbitrary string (body) on the URI. mediatype is the mime type. State is the resource state, see below. no return value.
- PutFile uri, pathname, mediatype, state
- For convenience, an alternate form of Put where the body is specifie as a pathname of a file in the local file system.
- Delete uri, state
- deletes a resource.
- GetProp uri, pname
- returns a single property of the resource
- GetProps uri, pnames, depth
- returns a set of response XML elements. This corresponds to the WebDAV PROPFIND method.
- PropPatch uri, setList, delList, state
- This corresponds to the PROPPATCH method. It sets and deletes a set of properties, atomically. setList is a sequence of XML elements, where the element name is the name of the property and the value is the property value. delList is a sequence of XML elements where the name is the name of a propery to be deleted, and the value us empty.
- MakeCollection uri, state
- Correponds to MKCOL. Creates a collection.
- Lock uri,
locktype=LockType.Write,
lockscope=LockScope.Exclusive,
timeouts=["infinite"],
ownerinfo=None,
depth='infinity'
- Locks a resource.
locktype must be LockType.Write or LockType.Read.
lockscope must be LockScope.Exclusive or LockScope.Shared,
timeouts is a sequence of timeouts, each of which is a string.
ownerinfo is either None, or an XML href element.
depth must be either the string "0" or the string "infinity".
The return value is an XML element for the lock discovery property.
- Unlock uri, token
- unlocks a lock, given the URI and the token
- Copy src, dst, overwritep=1, depth=0, live=[], state:
- copies a resource from src to dst.
- Move src, dst, overwritep=1, depth="infinity", live=[], state:
- Authenticate user, password:
- Authenticates the client to the server. Only Basic authetication is suppored.
- Close
- closes the connection.
The state arguement, used in several of the methods, is defined as follows:
- state
represents the state that a resource must be in in order for the method to be invoked. It may be either
-
- string
- in which case it is a lock token for the no-tag-list production.
- a dictionary
- in this case the keys are either:
- string: an absolute URI for tagged-list production
- None: specifies no-tag-list production
and the values are either:
- string: a lock token
- tuple: reserved for future use (e.g. etags)
davlib also defines
- a set of exceptions corresponding to the various Web return codes. Among these are AuthenticationRequired,
Forbidden and so forth.
- A set of named constants for the various names used in XML elements in WebDAV, e.g. Prop_creationdate is the constant DAV:creationdate,
Prop_getcontenttype is DAV:getcontenttype and so on.
- The convenience function
CollectionMembers ( ds, coll) which takes two arguments, ds (an instance of DAV) and coll (the URI of a collection). This returns a list of URLs of the members of the collection. This is simply a more convenient form of the same information one can obtain from GetProps.
For examples of use of davlib, see tdav.py
testing webdav servers
tday.py is a Python program that tests a webdav server for compliance with the RFC. It has a number of optional command line arguments:
python tdav.py
[-host host]
[-port port]
[-verbose]
[-user name]
[-password pword]
[-collection uri]
Where
- -host
- is the fully qualified name of the host. If omitted, the local host is used
- -port
- is the port number. If omitted, port 8080 is used
- -user
- is the user name to be used in the testing
- -password
- is the password for that user. If omitted, no authentication is attempted.
- -collection
- is the relative URL of the collection (on the WebDAV server) under which testing is performed. This is most useful for those servers where the root collection is not writable
- -verbose
- makes output verbose
In addition, the optional argument -help lists all these.
The program tests the following
- The OPTIONS method returns the DAV header, and the value is either 1 or 2
- If the test collection exists, the value of the resource type property is DAV:collection. If it does not exist, the collection is created.
- Attempt to create a collection whose parent does not exist signals an error.
- Put works.
- After a Put, the same MIME type specified in the Put is returned by a GetProp
- The size of the resource returned by Get is the same as the size that was put
- The Content-Type and Content-Length headers returned by HEAD are the same as those returned by Get.
- The DAV:getcontentlength properyt is supported.
- PropPatch can set a dead property.
- After setting a property, GetProp can read it.
- GetProps with no arguments gets all properties, and the response element is well-formed.
- Attempt to set a read-only property (DAV:getcreationdate) raises an error.
- A property may be set to an empty value.
- The xml:lang attribute is stored.
- Other attributes are also stored. (this is not mandatory in WebDAV but it's nice.)
- dead properties are not preserved across deletion of the resource. (That is, if you delete a resource, then re-create it, it does not have any of the dead properties it did previously)
On servers that advertise class 2 support, the following tests are also done:
- Lock supports Exclusive Write locks with Infinite timeout.
- The lock discovery property returns the same token that the lock method did.
- Locking a resource prevents PROPPATCH from setting or deleting a property wihout the token.
- It is possible to set or delete a property on a locked resource with the token. Both tagged and no-tagged productions are suppored.
- Locking a resource prevents prevents PUT without the token.
- Locking a resource prevents locking it again.
- Locked resources can be unlocked.
- Attempt to use an unsupported timeout type does something reasonable.
- Locking a collection is possible.
- lock on collection (depth 0) prevents creation of a new resource without the token.
- a newly added resource inherits lock of parent. When the parent is unlocked, so is the newly created resource.
- But one can still write to an existing resource without lock token
- lock on a collection also prevents creation of sub-collections without token
- Lock on a collection prevents deletion of a resource without token
- if a resource is locked and the parent is also locked (with a
different lock) then must pass both to delete it
- Locking a null resource is allowed. Such a resource appears in the members of the parent collection. If one does not Put to the null resource, then after unlocking the parent, the resource no longer exists.
Files in this release
Generic modules
These modules are 'generic' in the sense that they have nothing to do
with WebDAV per se but define utilities one might use in many other
programs as well.
- utils.py
- some generic utilities. They reveal the influence of the Lisp Machine programming envionment on the author.
- xml.py
- an XML API. See the self test code at the end of the file for (what amounts to) documentation.
- simple_log.py
- A trivial logging API. Log messages go to standard error.
- init.py
- Parses a configuration file in the style used on Windows (.ini)
- web_utils.py
- web_server.py
- A simple Web server in Python
WebDAV
- gwebdav.py
- generic WebDAV server, adds WebDAV methods to web_server above.
- webdav.ini
- The configuration file for it.
- defs.py
- abstract data types, named constants for all WebDAV properties, and definitions for all exceptions used in this program
- collection_helps.py
- Utilties for travsersing WebDAV collections.
- backend.py
- A generic WebDAV backend, implementing all functionality that does not depend on the storage system for resources and properties.
- propstore.py
- Property storage using the public domain dbhash module.
- filestore.py
- A specialization of the backend for a store that uses a file system for resource content and propstore for property storage.
- webdav.py
- WebDAV server using a file system as store
Client side
- davlib.py
- client side library. This should work with anyone's WebDAV, not just the one provided here.
WebDAV protocol test code
- tdav.py
- tests most WebDAV features
- tcopy.py
- tests Copy and Move
- tcommon.py
- common code for all tests
the file tfilestore.py is for testing filestore itself