Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV Documentation

Project:WsgiDAV, https://github.com/mar10/wsgidav/
Copyright:Licensed under The MIT License
Author:Martin Wendt
Release:1.3
Date:May 19, 2019

WsgiDAV is a generic WebDAV server written in Python and based on WSGI.

(WsgiDAV is a refactored version of PyFileServer written by Ho Chun Wei.)

Status

1.1.0 is released.

See also

The Change Log.

Main Features

  • Comes bundled with a server and a file system provider, so we can share a directory right away from the command line.
  • Designed to run behind any WSGI compliant server.
  • Tested with different clients on different platforms (Windows, Unix, Mac).
  • Supports online editing of MS Office documents.
  • Contains a simple web browser interface.
  • SSL support
  • Support for authentication using Basic or Digest scheme.
  • Passes litmus test suite.
  • Open architecture allows to write custom providers (i.e. storage, locking, authentication, …).

Quickstart

Install

Releases are hosted on PyPI. Install like:

$ pip install -U wsgidav

Or install the latest (potentially unstable) development version:

$ pip install git+https://github.com/mar10/wsgidav.git

See also

Installing WsgiDAV for details and how to install with developer access.

Run Server

To serve the /tmp folder as WebDAV / share, simply run:

$ wsgidav --host=0.0.0.0 --port=80 --root=/tmp

Much more options are available when a configuration file is specified. By default wsgidav.conf is searched in the local directory. Otherwise a file name can be specified:

$ wsgidav --config=my_config.conf

Supported Clients

WsgiDAV comes with a web interface and was tested with different clients (Windows File Explorer and drive mapping, MS Office, Ubuntu, Mac OS X, …).

https://raw.github.com/mar10/wsgidav/master/doc/teaser.png

More info

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Installing WsgiDAV

This document describes, how a WsgiDAV server is installed.

WsgiDAV server was tested with these operating systems:
  • Linux (Ubuntu 13)
  • Mac OS X 10.9
  • Windows (Win7, Vista, XP)

Preconditions

WsgiDAV requires
  • Python version 2.6 or later
  • Optionally lxml (will fall back to xml)

Details

Unix / Linux

The following examples were tested on Ubuntu 13.04.

Install lxml (optional):

~$ sudo apt-get install python-lxml

Install the latest release:

~$ sudo pip install -U wsgidav

or install the latest (potentially unstable) development version:

~$ pip install git+https://github.com/mar10/wsgidav.git

If you want to participate, check it out from the repository

$ git clone https://github.com/mar10/wsgidav.git wsgidav
$ cd wsgidav
$ setup.py develop
$ setup.py test
$ wsgidav --help
Windows

Install the preconditions, if neccessary. Basically the same as for Unix / Linux

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Configure and Run WsgiDAV Server

This document describes, how to configure and run a WsgiDAV server.

The WsgiDAV server was tested with these platforms

  • Mac OS X 10.9
  • Ubuntu 13
  • Windows (Win7, Vista, XP)

and these WSGI servers

  • cherrypy.wsgiserver
  • paste.httpserver
  • Pylons
  • wsgidav.ext_wsgiutils_server (bundled with WsgiDAV)
  • wsgiref.simple_server

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Sample configuration file

The configuration file uses Python syntax to specify these options:
  • Server options (hostname, port, SSL cert, …)
  • List of share-name / WebDAV provider mappings
  • List of users for authentication
  • Optional custom DAV providers (i.e. other than FilesystemProvider)
  • Optional custom lock manager, property manager and domain controller
  • Advanced debugging options
  • (and more)

For a start, you should copy Sample Configuration or Annotated Sample Configuration and edit it to your needs.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

# Note: This file is in Python syntax and format

################################################################################
# WsgiDAV configuration file
# 
################################################################################

# HELPERS - Do not modify this section

provider_mapping = {}
user_mapping = {}

def addShare(shareName, davProvider):
    provider_mapping[shareName] = davProvider

    
def addUser(realmName, user, password, description, roles=[]):
    realmName = "/" + realmName.strip(r"\/")
    userDict = user_mapping.setdefault(realmName, {}).setdefault(user, {})
    userDict["password"] = password
    userDict["description"] = description
    userDict["roles"] = roles


################################################################################
# SERVER OPTIONS
#===============================================================================
# 3rd party servers
# Try to run WsgiDAV inside these WSGI servers, in that order
ext_servers = (
#              "paste", 
#              "cherrypy",
#              "wsgiref",
               "cherrypy-bundled",
               "wsgidav",
               )

# Server port (use --port on command line)
port = 8080

# Server hostname (use --host on command line)
host = "localhost"

#===============================================================================
# Enable SSL support
# (The certificate should match the servers hostname, so the bogus certs will not
# work in all scenarios.)
# ssl_certificate = "wsgidav/server/sample_bogo_server.crt"
# ssl_private_key = "wsgidav/server/sample_bogo_server.key"
# ssl_certificate_chain = None

# Add the MS-Author-Via Response Header to OPTIONS command to allow editing
# with Microsoft Office (default: False)
add_header_MS_Author_Via = True

#================================================================================
# Misc. setings
#

# Block size in bytes
#block_size = 8192

#===============================================================================
# Middlewares
# 
# Use this section to modify the default middleware stack

#from wsgidav.dir_browser import WsgiDavDirBrowser
#from debug_filter import WsgiDavDebugFilter
#from http_authenticator import HTTPAuthenticator
#from error_printer import ErrorPrinter
#middleware_stack = [ WsgiDavDirBrowser, HTTPAuthenticator, ErrorPrinter, WsgiDavDebugFilter ]

#===============================================================================
# Debugging

verbose = 1          # 0 - no output (excepting application exceptions)         
                     # 1 - show single line request summaries (HTTP logging)
                     # 2 - show additional events
                     # 3 - show full request/response header info (HTTP Logging)
                     #     request body and GET response bodies not shown


# Enable specific module loggers
# E.g. ["lock_manager", "property_manager", "http_authenticator", ...]
enable_loggers = []

# Enable max. logging for certain http methods
# E.g. ["COPY", "DELETE", "GET", "HEAD", "LOCK", "MOVE", "OPTIONS", "PROPFIND", "PROPPATCH", "PUT", "UNLOCK"]
debug_methods = []

# Enable max. logging during  litmus suite tests that contain certain strings
# E.g. ["lock_excl", "notowner_modify", "fail_cond_put_unlocked", ...]
debug_litmus = []


################################################################################
# WsgiDavDirBrowser

dir_browser = {
    "enable": True,               # Render HTML listing for GET requests on collections
    "response_trailer": "",       # Raw HTML code, appended as footer
    "davmount": False,            # Send <dm:mount> response if request URL contains '?davmount'
    "ms_mount": False,            # Add an 'open as webfolder' link (requires Windows)
    "ms_sharepoint_plugin": True, # Invoke MS Offce documents for editing using WebDAV
    "ms_sharepoint_urls": False,  # Prepend 'ms-word:ofe|u|' to URL for MS Offce documents
#    "app_class": MyBrowser,      # Used instead of WsgiDavDirBrowser
}


################################################################################
# DAV Provider

#===============================================================================
# Property Manager
#
# Uncomment this lines to specify your own property manager.
# Default:        no support for dead properties
# Also available: wsgidav.property_manager.PropertyManager
#                 wsgidav.property_manager.ShelvePropertyManager
#
# Check the documentation on how to develop custom property managers.
# Note that the default PropertyManager works in-memory, and thus is NOT 
# persistent.

### Use in-memory property manager (NOT persistent)
# (this is the same as passing 'propsmanager = True')
#from wsgidav.property_manager import PropertyManager
#propsmanager = PropertyManager()

### Use persistent shelve based property manager
#from wsgidav.property_manager import ShelvePropertyManager
#propsmanager = ShelvePropertyManager("wsgidav-props.shelve")

### Use persistent MongoDB based property manager
#from wsgidav.addons.mongo_property_manager import MongoPropertyManager
#prop_man_opts = {}
#propsmanager = MongoPropertyManager(prop_man_opts)

### Use persistent CouchDB based property manager
#from wsgidav.addons.couch_property_manager import CouchPropertyManager
#prop_man_opts = {}
#propsmanager = CouchPropertyManager(prop_man_opts)

### Use in-memory property manager (NOT persistent)
propsmanager = True


### Optional additional live property modification
# Note: by default live properties like file size and last-modified time are
# read-only, but that can be overriden here if the underlying DAV provider
# supports it. For now only the FileSystemProvider supports it and only namely
# changes to the last-modified timestamp. Enable it with the mutable_live_props
# list as below to allow clients to use the utime system call or e.g. the
# touch or cp / rsync commands with the preserve-timestamp flags on a mounted
# DAV share.
# Please note that the timestamp is set on the actual file or directory, so it
# is persistent even for in-memory property managers. It should also be noted
# that mutable last-modified may not be compliant with the RFC 4918.
#mutable_live_props = ["{DAV:}getlastmodified"]


#===============================================================================
# Lock Manager
#
# Uncomment this lines to specify your own locks manager.                    
# Default:        wsgidav.lock_storage.LockStorageDict
# Also available: wsgidav.lock_storage.LockStorageShelve
#
# Check the documentation on how to develop custom lock managers.
# Note that the default LockStorageDict works in-memory, and thus is NOT 
# persistent.
                 
# Example: Use in-memory lock storage
#          (this is the same as passing 'locksmanager = True', which is default)
#from wsgidav.lock_storage import LockStorageDict
#locksmanager = LockStorageDict()


# Example: Use PERSISTENT shelve based lock manager
#from wsgidav.lock_storage import LockStorageShelve
#locksmanager = LockStorageShelve("wsgidav-locks.shelve")


################################################################################
# SHARES
#
# If you would like to publish files in the location '/v_root' through a
# WsgiDAV share 'files', so that it can be accessed by this URL:  
#     http://server:port/files 
# insert the following line:
#     addShare("files", "/v_root")
# or, on a Windows box:
#     addShare("files", "c:\\v_root")
#
# To access the same directory using a root level share 
#     http://server:port/
# insert this line: 
#     addShare("", "/v_root")
#
# The above examples use wsgidav.fs_dav_provider.FilesystemProvider, which is 
# the default provider implementation.
# 
# If you wish to use a custom provider, an object must be passed as second 
# parameter. See the examples below.  


### Add a read-write file share: 
addShare("dav", r"C:\temp")

### Add a read-only file share: 
#from wsgidav.fs_dav_provider import FilesystemProvider
#addShare("tmp", FilesystemProvider("/tmp", readonly=True))


### Publish an MySQL 'world' database as share '/world-db' 
#from wsgidav.addons.mysql_dav_provider import MySQLBrowserProvider
#addShare("world-db", MySQLBrowserProvider("localhost", "root", "test", "world"))


### Publish a virtual structure
#from wsgidav.samples.virtual_dav_provider import VirtualResourceProvider
#addShare("virtres", VirtualResourceProvider())


### Publish a Mercurial repository
#from wsgidav.addons.hg_dav_provider import HgResourceProvider
#addShare("hg", HgResourceProvider("PATH_OR_URL"))


### Publish a MongoDB
#from wsgidav.samples.mongo_dav_provider import MongoResourceProvider
#mongo_dav_opts = {}
#addShare("mongo", MongoResourceProvider(mongo_dav_opts))


################################################################################
# AUTHENTICATION
#===============================================================================
# HTTP Authentication Options

acceptbasic = True    # Allow basic authentication, True or False
acceptdigest = True   # Allow digest authentication, True or False
defaultdigest = True  # True (default digest) or False (default basic)

# Enter the name of a header field that will be accepted as authorized user.
# Including quotes, for example: trusted_auth_header = "REMOTE_USER"
trusted_auth_header = None


#===============================================================================
# Domain Controller
# Uncomment this line to specify your own domain controller
# Default: wsgidav.domain_controller, which uses the USERS section below
#
# Example: 
#   use a domain controller that allows users to authenticate against a 
#   Windows NT domain or a local computer.
#   Note: NTDomainController requires basic authentication:
#         Set acceptbasic=True, acceptdigest=False, defaultdigest=False 

#from wsgidav.addons.nt_domain_controller import NTDomainController
#domaincontroller = NTDomainController(presetdomain=None, presetserver=None)
#acceptbasic = True
#acceptdigest = False
#defaultdigest = False


#===============================================================================
# USERS
#
# This section is ONLY used by the DEFAULT Domain Controller.
#
# Users are defined per realm: 
#     addUser(<realm>, <user>, <password>, <description>)  
#
# Note that the default Domain Controller uses the share name as realm name.   
# 
# If no users are specified for a realm, no authentication is required.
# Thus granting read-write access to anonymous! 
#
# Note: If you wish to use Windows WebDAV support (such as Windows XP's My 
# Network Places), you need to include the domain of the user as part of the 
# username (note the DOUBLE slash), such as:
# addUser("v_root", "domain\\user", "password", "description")

addUser("", "tester", "secret", "")
addUser("", "tester2", "secret2", "")

#addUser("dav", "tester", "secret", "")
#addUser("dav", "tester2", "secret2", "")

#addUser("virtres", "tester", "secret", "")

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Running as Pylons controller

This example shows how configure a Pylons application, so that URLs starting with /dav/… are handled by WsgiDAV. (Tested with Pylons 0.9.7)

First, we create a URL mapping by adding these lines to <pylons_project>/config/routing.py:

# CUSTOM ROUTES HERE
# Add WsgiDAV server at /dav
map.connect('wdavroot', '/dav', path_info='/', controller='wdav')
map.connect('wdavres', '/dav/{path_info:.*}', controller='wdav')

Then add the controller by creating a new file <pylons_project>/controllers/wdav.py with this content:

# -*- coding: utf-8 -*-
from tempfile import gettempdir
from wsgidav.fs_dav_provider import FilesystemProvider
from wsgidav.wsgidav_app import DEFAULT_CONFIG, WsgiDAVApp

def _make_app():
    rootpath = gettempdir()
    provider = FilesystemProvider(rootpath)

    config = DEFAULT_CONFIG.copy()
    config.update({
        "mount_path": "/dav",
        "provider_mapping": {"/": provider},
        "user_mapping": {},
        "verbose": 1,
        })
    return WsgiDAVApp(config)

WdavController = _make_app()

Note that we have to use the mount_path option to tell WsgiDAV about the application root.

In the example above, we used a root share (‘/’), so WebDAV resources will be available as

http://192.168.0.2:5000/dav/resource.bin

If the provider is configured with a share name:

config.update({
    "mount_path": "/dav",
    "provider_mapping": {"/my_share": provider},
    ...
    })

the WebDAV resources will be available as

http://192.168.0.2:5000/dav/my_share/resource.bin

Run as stand-alone server

WsgiDAV is a WSGI application, that can be run by any WSGI compliant server.

This package comes with a built-in WSGI server called wsgidav (See wsgidav.wsgidav_server.run_server.py for details).

In the most simple case, no configuration file is required. The following line starts publishing the local folder /tmp for anonymous WebDAV access:

~/wsgidav$ wsgidav --host=0.0.0.0 --port=80 --root=/tmp

To test it, you may start a browser on http://127.0.0.1/.

However, most of the time we want to specify a configuration file with advanced settings:

~/wsgidav$ wsgidav --host=0.0.0.0 --port=80 --config=./wsgidav.conf

By default, WsgiDAV will search for a file called wsgidav.conf in the current working directory. Use the -h option for a list of additional commands:

~/wsgidav$ wsgidav -h

Configuration file

The configuration file uses Python syntax to specify these options:
  • Server options (hostname, port, SSL cert, …)
  • List of share-name / WebDAV provider mappings
  • List of users for authentication
  • Optional custom DAV providers (i.e. other than FilesystemProvider)
  • Optional custom lock manager, property manager and domain controller
  • Advanced debugging options
  • (and more)

For a start, you should copy Sample Configuration or Annotated Sample Configuration and edit it to your needs.

Run inside a 3rd-party WSGI server

Setup up the configuration dictionary, create a WsgiDAVApp object and pass it to your favorite WSGI server:

Run inside Pylons

See Running as Pylons controller for an example how WsgiDAV can be configured as Pylons controller.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Access WsgiDAV with WebDAV Clients

This document describes, how a WsgiDAV server can be accessed with different clients.

WsgiDAV was tested with these clients

  • Windows 7, 8
  • Windows Vista (Microsoft-WebDAV-!MiniRedir/6.0.6002)
  • Window XP (Microsoft-WebDAV-!MiniRedir/5.1.2600)
  • DAVExplorer
  • MS Office 2013
  • OpenOffice 3.1
  • Ubuntu Nautilus / gvfs
  • Mac OS/X Finder

The following examples assume, that we have a running WsgiDAV server on a remote machine with this configuration:

  • Server is running on a machine with IP address 192.168.0.2
  • Server is listening on port 80
  • A user called ‘tester’ with password ‘secret’ is configured to have access. (Or the share allows anonymous access.)

Windows clients

Redirector

The easiest way to access a WebDAV share from a Windows client is probably to map it as a network drive, and then use the File Explorer.

If the share allows anonymous access, type this at the command promt:

> net use W: http://192.168.0.2/
> dir W:

For access controlled shares, we must provide a user name:

> net use W: http://192.168.0.2/ /USER:tester

Windows will then prompt for a password. Alternatively we can pass password with the command line:

> net use W: http://192.168.0.2/ /USER:tester secret

To make this connection persistent between reboots:

> net use W: http://192.168.0.2/ /USER:tester secret /PERSISTENT:YES

To stop a connection:

> net use W: /DELETE

Note

Some known quirks of Windows redirector are listed below.

Known issues on all Windows versions

  • See als greenbytes WebDAV Mini-Redirector (MRXDAV.SYS) Versions and Issues List.

  • The WebDAV server must respond to PROPFIND and OPTIONS requests at the root share (‘/’). So when running behind another web server, WsgiDAV must be mounted at top level.

  • Digest authentication is supported by default. Basic authentication is disabled, when HTTP is used instead of SSL/HTTPS. (This can be changed by editing the registry: http://support.microsoft.com/kb/841215)

    Basic authentication sends passwords unencrypted, so it is generally a good thing to do this only over an SSL encrypted channel.

    Problems may arise, when we cannot provide Digest authentication (maybe because a custom WsgiDAV domain controller has no access to the users passwords). Or when our server does not provide HTTPS support.

Additional issues on Windows 7

  • By default Basic authentication is only allowed, when SSL (HTTPS) is used. (See previous notes.)

  • Reportedly on Windows 7, WebDAV requests receive a 3 second delay in the Windows explorer. To fix this, you may change IE’s proxy settings:

    Open IE -> Go to Tools menu -> Internet Options -> Connections
    -> LAN settings -> Un-check Automatically detect settings
    -> Click Ok -> Click Ok
    

Additional issues on Windows Vista:

  • By default Basic authentication is only allowed, when SSL (HTTPS) is used. (See previous notes.)

Additional issues on Windows XP:

  • Windows XP cannot map ‘/’ shares, so we have to connect to an existing sub folder (for example /dav):

    > net use W: http://192.168.0.2/dav
    
  • No custom port is accepted in the URL, like http://192.168.0.2:8001/dav. So WsgiDAV must run on port 80. This also means, that SSL won’t work (This may help: http://www.stunnel.org/).

  • The URL must start with http://. HTTPS is not supported.

    This in turn means that we have to enable Digest authentication, because Basic authentication is not allowed over HTTP (see common Windows issues above).

    However at least on SP3 the redirector seems to follow 302 Redirect responses to a https location. And then Basic authentication worked.

  • There have been problems reported, when the NET USE command prompts you for a name/password. (Servicepack 3 seems to work fine.) In this case, try to pass username and password on the command line with the /USER option:

    > net use W: http://192.168.0.2/dav /USER:tester secret
    
WebFolders

Microsoft’s “WebFolder” client predates Windows XP’s WebDAV Redirector.

  • TODO

Note

Some known quirks of Microsoft’s “WebFolder” client are listed below.

See als greenbytes Web Folder Client (MSDAIPP.DLL) Versions and Issues List.

Linux clients

Nautilus / gvfs

From the Nautilus File menu choose ‘Connect to server…’. In the dialog box enter

  • Service type: ‘WebDAV (HTTP)’
  • Server: ‘192.168.0.2’
  • Folder: ‘ro_docs’ or whatever the share name is (leave empty for root share).
  • Port: the port number (leave empty for default port 80)
  • User Name: leave this empty: do not enter anything here.

Then click ‘Connect’ and enter username and password.

Known issues:

davfs2

On Ubuntu we can mount a WebDAV file system. First make sure, that davfs2 is installed:

$ sudo apt-get install davfs2

Then create the mount point:

$ sudo mkdir /mnt/wsgidav_temp
$ sudo chmod 0766 /mnt/wsgidav_temp
$ sudo mount -t davfs http://192.168.0.2/dav /mnt/wsgidav -o rw
Please enter the username to authenticate with server
http://192.168.0.2/dav or hit enter for none.
Username: tester
Please enter the password to authenticate user tester with server
http://192.168.0.2/dav or hit enter for none.
Password:

To unmount:

sudo unmount /mnt/wsgidav

Mac

OS/X Finder

TODO

Browser clients

WsgiDAV enables HTTP browsing by default, so it is always possible to enter:

http://192.168.0.2/dav

in the address bar of your favorite web browser.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Samples and addons for WsgiDAV

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Mercurial WebDAV provider

Examples

This screenshot shows how a Mercurial repository appears in Microsoft Windows File Explorer:

_images/Explorer_Mercurial.png

Some new live properties are available:

_images/DAVExplorer_Mercurial.png
Usage

Note

This is not production code.

To publish a Mercurial repository by the share name ‘hg’, simply add thes lines to the configuration file:

# Publish a Mercurial repository
from wsgidav.addons.hg_dav_provider import HgResourceProvider
addShare("hg", HgResourceProvider("REPO_PATH_OR_URL"))
Details

DAV provider that publishes a Mercurial repository.

Note: This is not production code!

The repository is rendered as three top level collections.

edit:
Contains the working directory, i.e. all files. This includes uncommitted changes and untracked new files. This folder is writable.
released:
Contains the latest committed files, also known as ‘tip’. This folder is read-only.
archive:
Contains the last 10 revisions as sub-folders. This folder is read-only.

Sample layout:

/<share>/
    edit/
        server/
            ext_server.py
        README.txt
    released/
    archive/
        19/
        18/
        ...

Supported features:

  1. Copying or moving files from /edit/.. to the /edit/.. folder will result in a hg copy or hg rename.
  2. Deleting resources from /edit/.. will result in a hg remove.
  3. Copying or moving files from /edit/.. to the /released folder will result in a hg commit. Note that the destination path is ignored, instead the source path is used. So a user can drag a file or folder from somewhere under the edit/.. directory and drop it directly on the released directory to commit changes.
  4. To commit all changes, simply drag’n’drop the /edit folder on the /released folder.
  5. Creating new collections results in creation of a file called .directory, which is then hg add ed since Mercurial doesn’t track directories.
  6. Some attributes are published as live properties, such as {hg:}date.

Known limitations:

  1. This ‘commit by drag-and-drop’ only works, if the WebDAV clients produces MOVE or COPY requests. Alas, some clients will send PUT, MKCOL, … sequences instead.
  2. Adding and then removing a file without committing after the ‘add’ will leave this file on disk (untracked) This happens for example whit lock files that Open Office Write and other applications will create.
  3. Dragging the ‘edit’ folder onto ‘released’ with Windows File Explorer will remove the folder in the explorer view, although WsgiDAV did not delete it. This seems to be done by the client.
See:
http://mercurial.selenic.com/wiki/MercurialApi
Requirements:
easy_install mercurial or install the API as non-standalone version from here: http://mercurial.berkwood.com/ http://mercurial.berkwood.com/binaries/mercurial-1.4.win32-py2.6.exe

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

MongoDB WebDAV provider

Module description

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

MySQL WebDAV provider

Examples

This screenshot shows how the country table of MySQL’s world-db sample database is published as a collection.

All table rows are rendered as non-collections (text files) that contain the CSV formatted columns.

An additional virtual text file _ENTIRE_CONTENTS is created, that contains th whole CSV formatted table content.

_images/Browser_MySQL.gif

The table’s columns are mad accessible as live properties: .. image:: _static/img/DAVExplorer_MySQL.gif

Usage

To publish an MySQL database, simply add thes lines to the configuration file:

### Publish an MySQL 'world' database as share '/world-db'
from wsgidav.addons.mysql_dav_provider import MySQLBrowserProvider
addShare("world-db", MySQLBrowserProvider("localhost", "root", "test", "world"))
Module description

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Virtual WebDAV provider

Examples

Given these 3 ‘resources’:

Title Orga Status Tags Attachments
My doc 1 development draft cool, hot MySpec.doc MySpec.pdf
My doc 2 development published cool, nice MyURS.doc
My doc (.) marketing published nice MyURS.doc

this dynamic structure is published:

_images/Explorer_virtual.png

A resource is served as an collection, which is generated on-the-fly and contains some virtual files with additional information:

_images/Browser_virtual.png
Usage

To publish the sample virtual resources, simply add thes lines to the configuration file:

# Publish a virtual structure
from wsgidav.samples.virtual_dav_provider import VirtualResourceProvider
addShare("virtres", VirtualResourceProvider())
Module description

Sample implementation of a DAV provider that provides a browsable, multi-categorized resource tree.

Note that this is simply an example with no concrete real world benefit. But it demonstrates some techniques to customize WsgiDAV.

Compared to a published file system, we have these main differences:

  1. A resource like My doc 1 has several attributes like key, orga, tags, status, description. Also there may be a list of attached files.
  2. These attributes are used to dynamically create a virtual hierarchy. For example, if status is draft, a collection <share>/by_status/draft/ is created and the resource is mapped to <share>/by_status/draft/My doc 1.
  3. The resource My doc 1 is rendered as a collection, that contains some virtual descriptive files and the attached files.
  4. The same resource may be referenced using different paths For example <share>/by_tag/cool/My doc 1, <share>/by_tag/hot/My doc 1, and <share>/by_key/1 map to the same resource. Only the latter is considered the real-path, all others are virtual-paths.
  5. The attributes are exposed as live properties, like “{virtres:}key”, “{virtres:}tags”, and “{virtres:}description”. Some of them are even writable. Note that modifying an attribute may also change the dynamically created tree structure. For example changing “{virtres:}status” from ‘draft’ to ‘published’ will make the resource appear as <share>/by_status/published/My doc 1.
  6. This provider implements native delete/move/copy methods, to change the semantics of these operations for the virtual ‘/by_tag/’ collection. For example issuing a DELETE on <share>/by_tag/cool/My doc 1 will simply remove the ‘cool’ tag from that resource.
  7. Virtual collections and artifacts cannot be locked. However a resource can be locked. For example locking <share>/by_tag/cool/My doc 1 will also lock <share>/by_key/1.
  8. Some paths may be hidden, i.e. by_key is not browsable (but can be referenced) TODO: is this WebDAV compliant?

The database is a simple hard coded variable _resourceData, that contains a list of resource description dictionaries.

A resource is served as an collection, which is generated on-the-fly and contains some virtual files (artifacts).

In general, a URL is interpreted like this:

<share>/<category-type>/<category-key>/<resource-name>/<artifact-name>

An example layout:

<share>/
    by_tag/
        cool/
            My doc 1/
                .Info.html
                .Info.txt
                .Description.txt
                MySpec.pdf
                MySpec.doc
            My doc 2/
        hot/
            My doc 1/
            My doc 2/
        nice/
            My doc 2/
            My doc 3
    by_orga/
        development/
            My doc 3/
        marketing/
            My doc 1/
            My doc 2/
    by_status/
        draft/
            My doc 2
        published/
            My doc 1
            My doc 3
    by_key/
        1/
        2/
        3/

When accessed using WebDAV, the following URLs both return the same resource ‘My doc 1’:

<share>/by_tag/cool/My doc 1 
<share>/by_tag/hot/My doc 1
<share>/by_key/1

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

NT Domain Controller

Examples

TODO

Usage

TODO

Module description

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

MongoDB property manager

Note

This is not production code.

Module description

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

CouchDB property manager

Note

This is not production code.

Module description
Mercurial WebDAV provider
WebDAV provider that publishes a Mercurial repository.
MongoDB WebDAV provider
WebDAV provider that publishes a mongoDB database.
MySQL WebDAV provider
Implementation of a WebDAV provider that provides a very basic, read-only resource layer emulation of a MySQL database.
Google App Engine provider
Implementation of a WebDAV provider that implements a virtual file system built on Google App Engine’s data store (‘Bigtable’). This project also implements a lock storage provider that uses memcache.
Virtual WebDAV provider
Sample implementation of a DAV provider that provides a browsable, multi-categorized resource tree.
NT Domain Controller
Implementation of a domain controller that allows users to authenticate against a Windows NT domain or a local computer (used by HTTPAuthenticator).
MongoDB property manager
Implementation of a property manager, that stores dead properties in mongoDB (used by WebDAV providers).
CouchDB property manager
Implementation of a property manager, that stores dead properties in CouchDB (used by WebDAV providers).

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV Developers Guide

This section gives a brief introduction to the WsgiDAV application package (targeted to developers).

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV Architecture

This document gives a brief introduction to the WsgiDAV application package (targeted to developers).

See also

WsgiDAV API Doc

WSGI application stack

WsgiDAV is a WSGI application.

WSGI <http://www.python.org/peps/pep-0333.html> stands for Web Server Gateway Interface, a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers. If you are unfamiliar with WSGI, do take a moment to read the PEP.

As most WSGI applications, WsgiDAV consists of middleware which serve as pre-filters and post-processors, and the actual application.

WSGI application stack:

<Request>
  |
<Server> -> wsgidav_app.WsgiDavApp (container)
             |
             +-> debug_filter.WsgiDavDebugFilter (middleware, optional)
                   |
                 error_printer.ErrorPrinter (middleware)
                   |
                 http_authenticator.HTTPAuthenticator (middleware)
                   |           \- Uses a domain controller object
                   |
                 dir_browser.WsgiDavDirBrowser (middleware, optional)
                   |
                 request_resolver.RequestResolver (middleware)
                   |
                   *-> request_server.RequestServer (application)
                               \- Uses a DAVProvider object
                                   \- Uses a lock manager object
                                      and  a property  manager object

This is default stack. Middleware order can be configured using middleware_stack option. You can write your own or extend existing middleware and place it on middleware stack.

See the following sections for details.

WsgiDavApp

WSGI container, that handles the HTTP requests. This object is passed to the WSGI server and represents our WsgiDAV application to the outside.

On init:

Use the configuration dictionary to initialize lock manager, property manager, domain controller.

Create a dictionary of share-to-provider mappings.

Initialize middleware objects and RequestResolver and setup the WSGI application stack.

For every request:

Find the registered DAV provider for the current request.

Add or modify info in the WSGI environ:

environ[“SCRIPT_NAME”]
Mount-point of the current share.
environ[“PATH_INFO”]
Resource path, relative to the mount path.
environ[“wsgidav.provider”]
DAVProvider object that is registered for handling the current request.
environ[“wsgidav.config”]
Configuration dictionary.
environ[“wsgidav.verbose”]
Debug level [0-3].

Log the HTTP request, then pass the request to the first middleware.

Note: The OPTIONS method for the ‘*’ path is handled directly.

See Developers info for more information about the WsgiDAV architecture.

WsgiDavDebugFilter

WSGI middleware used for debugging (optional).

This module dumps request and response information to the console, depending on current debug configuration.

On init:
Define HTTP methods and litmus tests, that should turn on the verbose mode (currently hard coded).
For every request:

Increase value of environ['verbose'], if the request should be debugged. Also dump request and response headers and body.

Then pass the request to the next middleware.

These configuration settings are evaluated:

verbose

This is also used by other modules. This filter adds additional information depending on the value.

verbose Effect
0 No additional output.
1 No additional output (only standard request logging).
2 Dump headers of all requests and responses.
3 Dump headers and bodies of all requests and responses.
debug_methods

Boost verbosity to 3 while processing certain request methods. This option is ignored, when verbose < 2.

Configured like:

debug_methods = ["PROPPATCH", "PROPFIND", "GET", "HEAD","DELETE",
                 "PUT", "COPY", "MOVE", "LOCK", "UNLOCK",
                 ]
debug_litmus

Boost verbosity to 3 while processing litmus tests that contain certain substrings. This option is ignored, when verbose < 2.

Configured like:

debug_litmus = ["notowner_modify", "props: 16", ]

See Developers info for more information about the WsgiDAV architecture.

ErrorPrinter

Middleware error_printer.ErrorPrinter Handle DAV exceptions and internal errors.

On init:
Store error handling preferences.
For every request:
Pass the request to the next middleware. If a DAV exception occurs, log info, then pass it on. Internal exceptions are converted to HTTP_INTERNAL_ERRORs.
HTTPAuthenticator

Middleware http_authenticator.HTTPAuthenticator Uses a domain controller to establish HTTP authentication.

On init:
Store the domain controller object that is used for authentication.
For every request:

if authentication is required and user is not logged in: return authentication response.

Else set these values:

``environ['httpauthentication.realm']``
``environ['httpauthentication.username']``
WsgiDavDirBrowser

Middleware dir_browser.WsgiDavDirBrowser Handles GET requests on collections to display a HTML directory listing.

On init:

For every request:

If path maps to a collection:
Render collection members as directory (HTML table).
RequestResolver

Middleware request_resolver.RequestResolver Find the mapped DAV-Provider, create a new RequestServer instance, and dispatch the request.

On init:

Store URL-to-DAV-Provider mapping.

For every request:

Setup environ["SCRIPT_NAME"] to request realm and and environ["PATH_INFO"] to resource path.

Then find the registered DAV-Provider for this realm, create a new RequestServer instance, and pass the request to it.

Note: The OPTIONS method for ‘*’ is handled directly.

RequestServer

Application request_server.RequestServer Handles one single WebDAV request.

On init:

Store a reference to the DAV-Provider object.

For every request:

Handle one single WebDAV method (PROPFIND, PROPPATCH, LOCK, …) using a DAV-Provider instance. Then return the response body or raise an DAVError.

Note: this object only handles one single request.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV Modules

This document gives a brief introduction to the WsgiDAV application package (targeted to developers).

See also

WsgiDAV API Doc

DAV providers

DAV providers are abstractions layers that are used by the RequestServer to access and manipulate DAV resources.

All DAV providers must implement a common interface. This is usually done by deriving from the abstract base class dav_provider.DAVProvider.

WsgiDAV comes with a DAV provider for file systems, called fs_dav_provider.FilesystemProvider. That is why WsgiDAV is a WebDAV file server out-of-the-box.

There are also a few other modules that may serve as examples on how to plug-in your own custom DAV providers: Samples and addons for WsgiDAV. See also Writing custom providers.

FilesystemProvider

Implementation of a DAV provider that serves resource from a file system.

ReadOnlyFilesystemProvider implements a DAV resource provider that publishes a file system for read-only access. Write attempts will raise HTTP_FORBIDDEN.

FilesystemProvider inherits from ReadOnlyFilesystemProvider and implements the missing write access functionality.

See Developers info for more information about the WsgiDAV architecture.

Property Managers

DAV providers may use a property manager to support persistence for dead properties.

WsgiDAV comes with two default implementations, one based on a in-memory dictionary, and a persistent one based in shelve:

property_manager.PropertyManager
property_manager.ShelvePropertyManager

PropertyManager is used by default, but ShelvePropertyManager can be enabled by uncommenting two lines in the configuration file.

In addition, this may be replaced by a custom version, as long as the required interface is implemented.

Implements two property managers: one in-memory (dict-based), and one persistent low performance variant using shelve.

The properties dictionaray is built like:

{ ref-url1: {propname1: value1,
             propname2: value2,
             },
  ref-url2: {propname1: value1,
             propname2: value2,
             },
  }

See Developers info for more information about the WsgiDAV architecture.

Lock Managers

DAV providers may use a lock manager to support exclusive and shared write locking.

WsgiDAV comes with two default implementations, one based on a in-memory dictionary, and a persistent one based in shelve:

lock_manager.LockManager
lock_manager.ShelveLockManager

LockManager is used by default, but ShelveLockManager can be enabled by uncommenting two lines in the configuration file.

In addition, this may be replaced by a custom version, as long as the required interface is implemented.

Implements the LockManager object that provides the locking functionality.

The LockManager requires a LockStorage object to implement persistence. Two alternative lock storage classes are defined in the lock_storage module:

  • wsgidav.lock_storage.LockStorageDict
  • wsgidav.lock_storage.LockStorageShelve

The lock data model is a dictionary with these fields:

root:
Resource URL.
principal:
Name of the authenticated user that created the lock.
type:
Must be ‘write’.
scope:
Must be ‘shared’ or ‘exclusive’.
depth:
Must be ‘0’ or ‘infinity’.
owner:
String identifying the owner.
timeout:
Seconds remaining until lock expiration. This value is passed to create() and refresh()
expire:
Converted timeout for persistence: expire = time() + timeout.
token:
Automatically generated unique token.

See Developers info for more information about the WsgiDAV architecture.

Domain controllers

A domain controller provides user/password checking for a realm to the HTTPAuthenticator.

WsgiDAV comes with a default implementation that reads a user/password list from the config file.

However, this may be replaced by a custom version, as long as the required interface is implemented.

wsgidav.addons.nt_domain_controller is an example for such an extension.

Other objects
wsgidav.domain_controller.WsgiDAVDomainController
Default implementation of a domain controller as used by HTTPAuthenticator.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV Glossary

This document defines some terms gives a brief introduction to the WsgiDAV application package (targeted to developers).

See also

You can find more information about WebDAV terms and naming convention in official WebDAV specification documentation.

You will find this terms / naming conventions in the source:

URL:

In general URLs follow these rules:

  • Byte strings, using ISO-8859-1 encoding
  • Case sensitive
  • Quoted, i.e. special characters are escaped
  • Collections have a trailing ‘/’ (but we also accept request URLs, that omit them.)
When we use the term URL in WsgiDAV variables, we typically mean absolute URLs:
/<mount>/<path>
When we use the term full URL, we typically mean complete URLs:
http://<server>:<port>/<mount>/<path>
Constructed like
fullUrl = util.makeCompleteURL(environ)
Example
http://example.com:8080/dav/public/my%20nice%20doc.txt
Path (in general):

When we use the term Path in WsgiDAV variables, we typically mean unquoted URLs, relative to the mount point.

Example
“/public/my nice doc.txt”
mount point (also ‘mount path’, ‘approot’):

Unquoted, ISO-8859-1 encoded byte string.

The application’s mount point. Starts with a ‘/’ (if not empty).

This is the virtual directory, where the web server mounted the WsgiDAV application. So it is the environ[SCRIPT_NAME] that the server had set, before calling WsgiDAVApp.

Example
“”
share path (also ‘share’, ‘domain’):

Unquoted, ISO-8859-1 encoded byte string.

The application’s share path, relative to the mount point. Starts with a ‘/’ (if not empty).

For every request, WsgiDAVApp tries to find the registered provider for the URL (after the mount path was popped). The share path is the common URL prefix of this URL.

TODO: do we need to ditinguish between server mount points (‘mount path’) and WsgiDAV mount points (‘share path’)?

Constructed like
mountPath = environ[SCRIPT_NAME]
Example
“/dav”
realm:

Unquoted, ISO-8859-1 encoded byte string.

The domain name, that a resource belongs to.

This string is used for HTTP authentication.

Each realm would have a set of username and password pairs that would allow access to the resources.

Examples
“Marketing Department” “Windows Domain Authentication”

The domain_controller.WsgiDAVDomainController implementation uses the mount path as realm name.

path

Unquoted, ISO-8859-1 encoded byte string.

The resource URL, relative to the application’s mount point. Starts with a ‘/’. Collections also should have a trailing ‘/’.

Constructed like:
path = environ[PATH_INFO]
Examples:
“/public/my nice doc.txt” “/public/”
preferred path:

Unquoted, ISO-8859-1 encoded byte string.

The preferred or normalized path.

Depending on case sensitivity of the OS file system, all these paths may map to the same collection resource:

/public/my folder/
/public/my folder   (missing '/')
/public/MY FOLDER/  (on a Windows server, which is not case sensitive)

provider.getPreferredPath(path) will return:

/public/my folder/

for all of these variants.

reference URL:

Quoted, UTF-8 encoded byte string.

This is basically the same as an URL, that was build from the preferred path. But this deals with ‘virtual locations’ as well.

Since it is always unique for one resource, <refUrl> is used as key for the lock- and property storage.

A resource has always one ‘real location’ and may have 0..n ‘virtual locations’.

For example:

/dav/public/my%20folder/file1.txt
/dav/by_key/1234
/dav/by_status/approved/file1.txt

may map to the same resource, but only:

/dav/by_key/1234

is the refUrl.

Constructed like:
realUrl = quote(mountPath + reference path)
Examples:
“/dav/by_key/1234”
href:

Quoted, UTF-8 encoded byte string.

Used in XML responses. We are using the path-absolute option. i.e. starting with ‘/’. (See http://www.webdav.org/specs/rfc4918.html#rfc.section.8.3)

Constructed like:
href = quote(mountPath + preferredPath)
Example:
“/dav/public/my%20nice%20doc.txt”
filePath:

Unicode

Used by fs_dav_provider when serving files from the file system. (At least on Vista) os.path.exists(filePath) returns False, if a file name contains special characters, even if it is correctly UTF-8 encoded. So we convert to unicode.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

Writing custom providers

Note

This documentation is under construction.

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

WsgiDAV API Doc

This section gives a brief introduction to the WsgiDAV application package (targeted to developers).

Overview

wsgidav package:

Classes
Inheritance diagram of wsgidav.dav_provider, wsgidav.fs_dav_provider
Packages
wsgidav
wsgidav.dav_error Implements a DAVError class that is used to signal WebDAV and HTTP errors.
wsgidav.dav_provider Abstract base class for DAV resource providers.
wsgidav.debug_filter WSGI middleware used for debugging (optional).
wsgidav.dir_browser WSGI middleware that handles GET requests on collections to display directories.
wsgidav.domain_controller Implementation of a domain controller that uses realm/username/password mappings from the configuration file and uses the share path as realm name.
wsgidav.error_printer WSGI middleware to catch application thrown DAVErrors and return proper responses.
wsgidav.fs_dav_provider Implementation of a DAV provider that serves resource from a file system.
wsgidav.http_authenticator WSGI middleware for HTTP basic and digest authentication.
wsgidav.lock_manager Implements the LockManager object that provides the locking functionality.
wsgidav.lock_storage Implements two storage providers for LockManager.
wsgidav.middleware Abstract base middleware class
wsgidav.property_manager Implements two property managers: one in-memory (dict-based), and one persistent low performance variant using shelve.
wsgidav.request_resolver WSGI middleware that finds the registered mapped DAV-Provider, creates a new RequestServer instance, and dispatches the request.
wsgidav.request_server WSGI application that handles one single WebDAV request.
wsgidav.rw_lock ReadWriteLock
wsgidav.util Miscellaneous support functions for WsgiDAV.
wsgidav.version Current WsgiDAV version number.
wsgidav.wsgidav_app WSGI container, that handles the HTTP requests.
wsgidav.xml_tools Small wrapper for different etree packages.
wsgidav.samples
wsgidav.samples.dav_provider_tools
wsgidav.samples.mongo_dav_provider
wsgidav.samples.virtual_dav_provider Sample implementation of a DAV provider that provides a browsable, multi-categorized resource tree.
wsgidav.addons
wsgidav.addons.couch_property_manager
wsgidav.addons.hg_dav_provider
wsgidav.addons.mongo_property_manager
wsgidav.addons.mysql_dav_provider
wsgidav.addons.nt_domain_controller
wsgidav.server
wsgidav.server.ext_wsgiutils_server ext_wsgiutils_server.py is an extension of the wsgiutils server in Paste.
wsgidav.server.run_reloading_server Wrapper for run_server, that restarts the server when source code is modified.
wsgidav.server.run_server run_server
wsgidav.server.server_sample

Attention

You are looking at outdated documentation for version 1.x. A newer version is available.

FAQ

What do I need to run WsgiDAV?
See Installing WsgiDAV for details.
Which web servers are supported?

WsgiDAV comes with a standalone server, to run out of the box. There is also built-in support for CherryPy, Paste, and wsgiref servers, as long as these packages are installed. (Fast)CGI should be possible using flup.

Basically, it runs with all WSGI servers. Currently we tested with Pylons, CherryPy, Paste server.

See Configure and Run WsgiDAV Server for details.

Which configuration do you recommend?

Currently CherryPy seems to be very robust. Also installing lxml is recommended.

But since WsgiDAV is pretty new, please provide feedback on your experience.

Which WebDAV clients are supported?

Basically all WebDAV clients on all platforms, though some of them show odd behaviors.

See Access WsgiDAV with WebDAV Clients for details.

I found a bug, what should I do?

First, check the issue list, if this is a known bug. If not, open a new issue and provide detailed information how to reproduce it.

Then fix it and send me the patch ;-)

How do you pronounce WsgiDAV?
Don’t care really, but I would say ‘Whiskey Dove’. (Still looking for a cool logo ;) Edit: thanks to Markus Majer for providing one!

contents

Indices and tables