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

Features

Notes

The rules for determining the content type of a resource are:
  1. If the client has stored a value for DAV:getcontenttype, that value is returned.
  2. 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.
  3. 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

  1. Pick a directory for WebDAV's persistent store. This is where it will store its properties database.
  2. Pick a file system directory for the root collection WebDAV will serve. This is not the same as the persistent store.
  3. Copy webdav.ini.template to webdav.ini
  4. Edit webdav.ini
    1. set working to the persistent store directory
    2. set rootmap to include the root Web directory
    3. optionally, edit port.
    4. You may wish to edit other fields in the ini file.

Running it

  1. 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

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

  1. The OPTIONS method returns the DAV header, and the value is either 1 or 2
  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.
  3. Attempt to create a collection whose parent does not exist signals an error.
  4. Put works.
  5. After a Put, the same MIME type specified in the Put is returned by a GetProp
  6. The size of the resource returned by Get is the same as the size that was put
  7. The Content-Type and Content-Length headers returned by HEAD are the same as those returned by Get.
  8. The DAV:getcontentlength properyt is supported.
  9. PropPatch can set a dead property.
  10. After setting a property, GetProp can read it.
  11. GetProps with no arguments gets all properties, and the response element is well-formed.
  12. Attempt to set a read-only property (DAV:getcreationdate) raises an error.
  13. A property may be set to an empty value.
  14. The xml:lang attribute is stored.
  15. Other attributes are also stored. (this is not mandatory in WebDAV but it's nice.)
  16. 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:

  1. Lock supports Exclusive Write locks with Infinite timeout.
  2. The lock discovery property returns the same token that the lock method did.
  3. Locking a resource prevents PROPPATCH from setting or deleting a property wihout the token.
  4. It is possible to set or delete a property on a locked resource with the token. Both tagged and no-tagged productions are suppored.
  5. Locking a resource prevents prevents PUT without the token.
  6. Locking a resource prevents locking it again.
  7. Locked resources can be unlocked.
  8. Attempt to use an unsupported timeout type does something reasonable.
  9. Locking a collection is possible.
  10. lock on collection (depth 0) prevents creation of a new resource without the token.
  11. a newly added resource inherits lock of parent. When the parent is unlocked, so is the newly created resource.
  12. But one can still write to an existing resource without lock token
  13. lock on a collection also prevents creation of sub-collections without token
  14. Lock on a collection prevents deletion of a resource without token
  15. if a resource is locked and the parent is also locked (with a different lock) then must pass both to delete it
  16. 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