[tryton-debian-vcs] pywebdav branch debian created. 3a7a0033dfa5d25cfa853d9dca77043c409a22a1
Mathias Behrle
tryton-debian-vcs at alioth.debian.org
Wed Nov 27 16:48:26 UTC 2013
The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/pywebdav.git;a=commitdiff;h=3a7a0033dfa5d25cfa853d9dca77043c409a22a1
commit 3a7a0033dfa5d25cfa853d9dca77043c409a22a1
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Nov 24 19:15:23 2013 +0100
Updating to standards version 3.9.5, no changes needed.
diff --git a/debian/control b/debian/control
index 2fc50e6..7cea600 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
Uploaders: Mathias Behrle <mathiasb at m9s.biz>
Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
-Standards-Version: 3.9.4
+Standards-Version: 3.9.5
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://debian.tryton.org/gitweb/?p=packages/pywebdav.git
Vcs-Git: git://debian.tryton.org/packages/pywebdav.git
commit 4fbb6be6fb3286fef55f481221d21c2cd9614122
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Aug 6 16:20:35 2013 +0200
Releasing debian version 0.9.8-6.
diff --git a/debian/changelog b/debian/changelog
index f34c32e..eb39edf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.8-6) unstable; urgency=low
+
+ * Adapting the rules file to work also with git-buildpackage.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Tue, 06 Aug 2013 13:31:55 +0200
+
pywebdav (0.9.8-5) unstable; urgency=low
* Removing Daniel from Uploaders. Thanks for your work! (Closes: #704356).
commit c75520b403ab6f15e9a8c9ab3e245d5ee1c27e4e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Mon Aug 5 18:12:00 2013 +0200
Adapting the rules file to work also with git-buildpackage.
diff --git a/debian/rules b/debian/rules
index b918925..63f539a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,11 +1,23 @@
#!/usr/bin/make -f
+PACKAGE_NAME := $(shell python setup.py --name)
+
%:
dh ${@} --with python2
override_dh_auto_clean:
dh_auto_clean
+
+override_dh_auto_build:
+ mv $(PACKAGE_NAME).egg-info $(PACKAGE_NAME).hen-info
+ mv PKG-INFO PKG-INFO.hen
+ dh_auto_build
+
+override_dh_auto_install:
+ dh_auto_install
rm -rf *.egg-info
+ mv $(PACKAGE_NAME).hen-info $(PACKAGE_NAME).egg-info
+ mv PKG-INFO.hen PKG-INFO
override_dh_builddeb:
dh_builddeb -- -Zxz -z9
commit cffbccb28e54bb810c522b81945df68efbe7ff48
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri May 31 20:26:48 2013 +0200
Releasing debian version 0.9.8-5.
diff --git a/debian/changelog b/debian/changelog
index 55bc21f..f34c32e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+pywebdav (0.9.8-5) unstable; urgency=low
+
+ * Removing Daniel from Uploaders. Thanks for your work! (Closes: #704356).
+ * Removing needless empty line in rules.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Fri, 31 May 2013 15:11:55 +0200
+
pywebdav (0.9.8-4) experimental; urgency=low
* Removing obsolete Dm-Upload-Allowed
commit 59c592878e2f485d3c09ab084b452c803e5fb024
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Wed May 29 17:19:29 2013 +0200
Removing needless empty line in rules.
diff --git a/debian/rules b/debian/rules
index 1ae0776..b918925 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,7 +5,6 @@
override_dh_auto_clean:
dh_auto_clean
-
rm -rf *.egg-info
override_dh_builddeb:
commit b8bb4b8541ab6c0fa024337b3c41aa22fb2f94ac
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Apr 21 23:20:32 2013 +0200
Removing Daniel from Uploaders. Thanks for your work! (Closes: #704356).
diff --git a/debian/control b/debian/control
index c071589..2fc50e6 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
-Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
+Uploaders: Mathias Behrle <mathiasb at m9s.biz>
Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.4
Homepage: http://code.google.com/p/pywebdav/
commit 47b3f7ceb450c68e33d8290edfd3dd09c7e6ae96
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Feb 24 15:06:41 2013 +0100
Releasing debian version 0.9.8-4.
diff --git a/debian/changelog b/debian/changelog
index 1dd8b4a..55bc21f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+pywebdav (0.9.8-4) experimental; urgency=low
+
+ * Removing obsolete Dm-Upload-Allowed
+ * Updating to Standards-Version: 3.9.4, no changes needed.
+ * Updating Vcs-Git to correct address.
+ * Adding watch file.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Sun, 24 Feb 2013 14:25:03 +0100
+
pywebdav (0.9.8-3) unstable; urgency=low
* Correcting email address in previous upload.
commit f2068cc1bf85a2ecd8a6c4abed8a5292576b9ada
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Feb 24 13:24:27 2013 +0100
Adding watch file.
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..8a7da85
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
+version=3
+http://code.google.com/p/pywebdav/downloads/list?can=1 .*/PyWebDAV-(\d[\d.]*)\.(?:zip|tgz|tbz2|txz|tar\.gz|tar\.bz2|tar\.xz)
commit 14b5eaedd63b8f114b2129e638b0215fc6b8fb56
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sat Feb 23 20:08:07 2013 +0100
Updating Vcs-Git to correct address.
diff --git a/debian/control b/debian/control
index cd2847f..c071589 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.4
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://debian.tryton.org/gitweb/?p=packages/pywebdav.git
-Vcs-Git: git://debian.tryton.org/git/packages/pywebdav.git
+Vcs-Git: git://debian.tryton.org/packages/pywebdav.git
X-Python-Version: >= 2.4
Package: python-webdav
commit 1ae82a71956bb347b253801fb3cac26e0bc5c286
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 15 19:28:24 2013 +0100
Updating to Standards-Version: 3.9.4, no changes needed.
diff --git a/debian/control b/debian/control
index 535e05b..cd2847f 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
-Standards-Version: 3.9.3
+Standards-Version: 3.9.4
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://debian.tryton.org/gitweb/?p=packages/pywebdav.git
Vcs-Git: git://debian.tryton.org/git/packages/pywebdav.git
commit 020b69bd8539fbdde435262b27e03552a856781d
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Wed Feb 13 21:37:13 2013 +0100
Removing obsolete Dm-Upload-Allowed
diff --git a/debian/control b/debian/control
index b14de7c..535e05b 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,6 @@ Section: python
Priority: optional
Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
-Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.3
Homepage: http://code.google.com/p/pywebdav/
commit 71890b24b3b5192a21fe127d10ec53834c0dec8e
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 10 23:27:00 2012 +0200
Releasing debian version 0.9.8-3.
diff --git a/debian/changelog b/debian/changelog
index fba7dc3..1dd8b4a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.8-3) unstable; urgency=low
+
+ * Correcting email address in previous upload.
+
+ -- Daniel Baumann <daniel at debian.org> Tue, 10 Jul 2012 23:26:55 +0200
+
pywebdav (0.9.8-2) unstable; urgency=low
* Updating maintainers field.
commit 41997abb48b9c44c51442cca298fb0e6be581c63
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 10 23:26:48 2012 +0200
Correcting email address in previous upload.
diff --git a/debian/changelog b/debian/changelog
index 2faaf4a..fba7dc3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,7 +6,7 @@ pywebdav (0.9.8-2) unstable; urgency=low
* Updating to debhelper version 9.
* Correcting copyright file to match format version 1.0.
- -- Daniel Baumann <daniel at 127011.net> Sat, 30 Jun 2012 17:15:02 +0200
+ -- Daniel Baumann <daniel at debian.org> Sat, 30 Jun 2012 17:15:02 +0200
pywebdav (0.9.8-1) unstable; urgency=low
commit 8ac6d72784d510a97a7c433bea1b10c62674fb69
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 17:15:59 2012 +0200
Releasing debian version 0.9.8-2.
diff --git a/debian/changelog b/debian/changelog
index 4d7e984..2faaf4a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+pywebdav (0.9.8-2) unstable; urgency=low
+
+ * Updating maintainers field.
+ * Updating vcs fields.
+ * Switching to xz compression.
+ * Updating to debhelper version 9.
+ * Correcting copyright file to match format version 1.0.
+
+ -- Daniel Baumann <daniel at 127011.net> Sat, 30 Jun 2012 17:15:02 +0200
+
pywebdav (0.9.8-1) unstable; urgency=low
* Removing deprecated XB-Python-Version for dh_python2.
commit 89746686551e220d565ff13424dfbc7e4e5ddd99
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 17:00:01 2012 +0200
Correcting copyright file to match format version 1.0.
diff --git a/debian/copyright b/debian/copyright
index 848fedb..dc4dc41 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,16 +1,14 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files: *
-Copyright:
- (C) 2009 Simon Pamies <s.pamies at banality.de>
- (C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
- (C) 2009 Vince Spicer <vince at vince.ca>
+Copyright: 2009 Simon Pamies <s.pamies at banality.de>
+ 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
+ 2009 Vince Spicer <vince at vince.ca>
License: GPL-2+
Files: debian/*
-Copyright:
- (C) 2009-2012 Daniel Baumann <daniel at debian.org>
- (C) 2012 Mathias Behrle <mathiasb at m9s.biz>
+Copyright: 2009-2012 Daniel Baumann <daniel at debian.org>
+ 2012 Mathias Behrle <mathiasb at m9s.biz>
License: GPL-2+
License: GPL-2+
commit ab6fb11f5e16345e4db9dfa06e3d87dabc35eef5
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 16:59:57 2012 +0200
Updating to debhelper version 9.
diff --git a/debian/compat b/debian/compat
index 45a4fb7..ec63514 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-8
+9
diff --git a/debian/control b/debian/control
index da71c3f..b14de7c 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
-Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~), python-setuptools
+Build-Depends: debhelper (>= 9), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://debian.tryton.org/gitweb/?p=packages/pywebdav.git
commit a8e563b80dc141184849c7489bc334861fc7d5b0
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 16:59:54 2012 +0200
Switching to xz compression.
diff --git a/debian/rules b/debian/rules
index e32b791..1ae0776 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,3 +7,6 @@ override_dh_auto_clean:
dh_auto_clean
rm -rf *.egg-info
+
+override_dh_builddeb:
+ dh_builddeb -- -Zxz -z9
diff --git a/debian/source/options b/debian/source/options
index d053b65..22a4de9 100644
--- a/debian/source/options
+++ b/debian/source/options
@@ -1,2 +1,2 @@
-compression = gzip
+compression = xz
compression-level = 9
commit a2e559ff0679005139cf22bfa22b6a51115a41b6
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 16:55:07 2012 +0200
Updating vcs fields.
diff --git a/debian/control b/debian/control
index a0c26f1..da71c3f 100644
--- a/debian/control
+++ b/debian/control
@@ -7,8 +7,8 @@ Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.3
Homepage: http://code.google.com/p/pywebdav/
-Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
-Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
+Vcs-Browser: http://debian.tryton.org/gitweb/?p=packages/pywebdav.git
+Vcs-Git: git://debian.tryton.org/git/packages/pywebdav.git
X-Python-Version: >= 2.4
Package: python-webdav
commit 08ac9326cfd1cb8d7ac7b99a979eff38cca21620
Author: Daniel Baumann <daniel at 127011.net>
Date: Sat Jun 30 16:47:48 2012 +0200
Updating maintainers field.
diff --git a/debian/control b/debian/control
index 6eeed2e..a0c26f1 100644
--- a/debian/control
+++ b/debian/control
@@ -1,7 +1,7 @@
Source: pywebdav
Section: python
Priority: optional
-Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
+Maintainer: Debian Tryton Maintainers <maintainers at debian.tryton.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~), python-setuptools
commit be973421393a6c635057d75c379007469f96d19e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Mar 27 13:35:21 2012 +0200
Releasing debian version 0.9.8-1.
diff --git a/debian/changelog b/debian/changelog
index b3fc17c..4d7e984 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,17 @@
+pywebdav (0.9.8-1) unstable; urgency=low
+
+ * Removing deprecated XB-Python-Version for dh_python2.
+ * Updating to Standards-Version: 3.9.3, no changes needed.
+ * Updating year in copyright.
+ * Adding myself to copyright.
+ * Adding Format header for DEP5.
+ * Merging upstream version 0.9.8.
+ * Updating examples path to new pywebdav structure > 0.9.4.1.
+ * Adding NEWS file with upgrade hints for 0.9.8.
+ * Adding Breaks for old reverse dependencies (Closes: #665810).
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Tue, 27 Mar 2012 13:31:17 +0200
+
pywebdav (0.9.4.1-1) unstable; urgency=low
[ Mathias Behrle ]
commit 214f438e73c5ff6f1379f556b80af461b0ad49e5
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Mar 27 13:29:14 2012 +0200
Adding Breaks for old reverse dependencies (Closes: #665810).
diff --git a/debian/control b/debian/control
index 5327cb4..6eeed2e 100644
--- a/debian/control
+++ b/debian/control
@@ -14,6 +14,12 @@ X-Python-Version: >= 2.4
Package: python-webdav
Architecture: all
Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources
+Breaks:
+ tryton-modules-calendar (<= 2.2.0-1),
+ tryton-modules-calendar-scheduling (<= 2.2.1-1),
+ tryton-modules-calendar-todo (<= 2.2.0-1),
+ tryton-modules-party-vcarddav (<= 2.2.0-1),
+ tryton-server (<= 2.2.1-1)
Description: WebDAV server implementation in Python
PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a
simple interface to webdav services to any application which needs it. It can
commit 0f7fe73f507e6060ed1cce81b13466d89ef68976
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Mar 27 13:01:17 2012 +0200
Adding NEWS file with upgrade hints for 0.9.8.
diff --git a/debian/NEWS b/debian/NEWS
new file mode 100644
index 0000000..0974373
--- /dev/null
+++ b/debian/NEWS
@@ -0,0 +1,7 @@
+pywebdav (0.9.8-1) unstable; urgency=low
+
+ PyWebDAV was restructured: the DAV package moved to pywebdav.lib.
+ Integrators should be done in most cases by simply replacing
+ ''from DAV'' imports to ''from pywebdav.lib''.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Tue, 27 Mar 2012 12:25:30 +0200
commit 0850edbc901fb57ef15fb194c71660ada19caac4
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Mar 27 12:14:13 2012 +0200
Updating examples path to new pywebdav structure > 0.9.4.1.
diff --git a/debian/python-webdav.examples b/debian/python-webdav.examples
index 3a49740..9314bbc 100644
--- a/debian/python-webdav.examples
+++ b/debian/python-webdav.examples
@@ -1 +1 @@
-DAVServer/config.ini
+pywebdav/server/config.ini
commit ad77ede6bf796ffc0343d129d6998086edaa66ac
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Mar 27 12:10:23 2012 +0200
Merging 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 0a192bf7dcba4bcabe12df764c31831aa97f66dd
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Mar 25 15:28:17 2012 +0200
Adding Format header for DEP5.
diff --git a/debian/copyright b/debian/copyright
index 04f20b2..848fedb 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,3 +1,5 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
Files: *
Copyright:
(C) 2009 Simon Pamies <s.pamies at banality.de>
commit 1b7352859600b4c617215bdfdb0c40c798e177c5
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 24 14:09:11 2012 +0100
Adding myself to copyright.
diff --git a/debian/copyright b/debian/copyright
index 96ff6b0..04f20b2 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -6,7 +6,9 @@ Copyright:
License: GPL-2+
Files: debian/*
-Copyright: (C) 2009-2012 Daniel Baumann <daniel at debian.org>
+Copyright:
+ (C) 2009-2012 Daniel Baumann <daniel at debian.org>
+ (C) 2012 Mathias Behrle <mathiasb at m9s.biz>
License: GPL-2+
License: GPL-2+
commit a74d7405244058b87d9d6175741e9c73d5c7c2e8
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 24 14:01:03 2012 +0100
Updating year in copyright.
diff --git a/debian/copyright b/debian/copyright
index e4e1cc7..96ff6b0 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -6,7 +6,7 @@ Copyright:
License: GPL-2+
Files: debian/*
-Copyright: (C) 2009-2011 Daniel Baumann <daniel at debian.org>
+Copyright: (C) 2009-2012 Daniel Baumann <daniel at debian.org>
License: GPL-2+
License: GPL-2+
commit 488a8f3b07ac02fe54d456b78e61b59ec7f105ee
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 24 13:47:00 2012 +0100
Updating to Standards-Version: 3.9.3, no changes needed.
diff --git a/debian/control b/debian/control
index fd07150..5327cb4 100644
--- a/debian/control
+++ b/debian/control
@@ -5,7 +5,7 @@ Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~), python-setuptools
-Standards-Version: 3.9.2
+Standards-Version: 3.9.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit f1164a605d4cad0980403e13585b33b9dd042cf9
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Jul 22 12:26:53 2011 +0200
Removing deprecated XB-Python-Version for dh_python2.
diff --git a/debian/control b/debian/control
index d0d923d..fd07150 100644
--- a/debian/control
+++ b/debian/control
@@ -14,7 +14,6 @@ X-Python-Version: >= 2.4
Package: python-webdav
Architecture: all
Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources
-XB-Python-Version: ${python:Versions}
Description: WebDAV server implementation in Python
PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a
simple interface to webdav services to any application which needs it. It can
commit 7b6e9f169ef66b2fe86c419889166303137b87e3
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Jul 17 19:38:43 2011 +0200
Releasing debian version 0.9.4.1-1.
diff --git a/debian/changelog b/debian/changelog
index 54ae576..b3fc17c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,23 @@
+pywebdav (0.9.4.1-1) unstable; urgency=low
+
+ [ Mathias Behrle ]
+ * Updating Copyright.
+ * Updating to standards version 3.9.2.
+
+ [ Daniel Baumann ]
+ * Moving to source format 3.0 (quilt).
+ * Correcting indenting in 0.9.4-3 changelog entry.
+ * Not wrapping uploaders field, it does not exceed 80 chars.
+ * Compacting copyright file.
+ * Updating manpage.
+
+ [ Mathias Behrle ]
+ * Moving from deprecated python-support to dh_python2.
+ * Setting Debian version to correct upstream version missed
+ in last release.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Sun, 17 Jul 2011 19:36:28 +0200
+
pywebdav (0.9.4-3) unstable; urgency=high
* Changing my email address.
commit db75edfe938d2b6343741a22a52e61d07c5655e1
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sun Jul 17 18:44:02 2011 +0200
Moving from deprecated python-support to dh_python2.
diff --git a/debian/control b/debian/control
index 20adc6a..d0d923d 100644
--- a/debian/control
+++ b/debian/control
@@ -4,11 +4,12 @@ Priority: optional
Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
-Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
+Build-Depends: debhelper (>= 8), python (>= 2.6.6-3~), python-setuptools
Standards-Version: 3.9.2
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
+X-Python-Version: >= 2.4
Package: python-webdav
Architecture: all
diff --git a/debian/pycompat b/debian/pycompat
deleted file mode 100644
index 0cfbf08..0000000
--- a/debian/pycompat
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/debian/pyversions b/debian/pyversions
deleted file mode 100644
index 8b253bc..0000000
--- a/debian/pyversions
+++ /dev/null
@@ -1 +0,0 @@
-2.4-
diff --git a/debian/rules b/debian/rules
index 000210b..e32b791 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,4 +1,9 @@
#!/usr/bin/make -f
%:
- dh ${@}
+ dh ${@} --with python2
+
+override_dh_auto_clean:
+ dh_auto_clean
+
+ rm -rf *.egg-info
commit 2ec728a5bdf13b3dc4dde841a43442fe6550e2ca
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Jul 10 14:56:37 2011 +0200
Updating manpage.
diff --git a/debian/manpages/davserver.1 b/debian/manpages/davserver.1
index 135b3cd..e05795b 100644
--- a/debian/manpages/davserver.1
+++ b/debian/manpages/davserver.1
@@ -1,4 +1,4 @@
-.TH DAVSERVER 1 "2010\-04\-08" "0.9.3" "WebDAV server"
+.TH DAVSERVER 1 "2011\-07\-10" "0.9.4" "WebDAV server"
.SH NAME
davserver \-WebDAV server implementation in Python
@@ -46,4 +46,4 @@ More information about davserver and the PyWebDAV project can be found at <\fIht
PyWebDAV was written by Simon Pamies <\fIs.pamies at banality.de\fR>, Christian Scholz <\fIruebe at aachen.heimat.de\fR>, and Vince Spicer <\fIvince at vince.ca\fR>.
L
.PP
-This manual page was written by Daniel Baumann <\fIdaniel at debian.org\fR>, for the Debian project (but may be used by others).
+This manual page was written by Daniel Baumann <\fIdaniel at debian.org\fR>.
commit 5f733247b288e84ac2151296cc09167e380c04ff
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Jul 10 14:55:30 2011 +0200
Compacting copyright file.
diff --git a/debian/copyright b/debian/copyright
index 0eb5081..e4e1cc7 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,34 +1,15 @@
-Upstream-Contact: Pywebdav Project <pywebdav at googlegroups.com>
-Upstream-Homepage: http://code.google.com/p/pywebdav/
-Maintainer-Contact: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
-Maintainer-Homepage: http://tryton.debian-maintainers.org/
-
Files: *
Copyright:
(C) 2009 Simon Pamies <s.pamies at banality.de>
(C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
(C) 2009 Vince Spicer <vince at vince.ca>
License: GPL-2+
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- .
- This program 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 General Public License for more details.
- .
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- .
- On Debian systems, the complete text of the GNU General Public License
- can be found in /usr/share/common-licenses/GPL-2 file.
Files: debian/*
Copyright: (C) 2009-2011 Daniel Baumann <daniel at debian.org>
License: GPL-2+
+
+License: GPL-2+
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
@@ -43,5 +24,5 @@ License: GPL-2+
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
.
- On Debian systems, the complete text of the GNU General Public License
+ The complete text of the GNU General Public License
can be found in /usr/share/common-licenses/GPL-2 file.
commit b616d8adc3cf4745dc3f0b4229986a96c09a0b1f
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Jul 10 14:54:43 2011 +0200
Not wrapping uploaders field, it does not exceed 80 chars.
diff --git a/debian/control b/debian/control
index ed129b4..20adc6a 100644
--- a/debian/control
+++ b/debian/control
@@ -2,9 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
-Uploaders:
- Daniel Baumann <daniel at debian.org>,
- Mathias Behrle <mathiasb at m9s.biz>
+Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
Standards-Version: 3.9.2
commit 72f6301d509ff0785e554f68c4d8cc70940b917d
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Jul 10 14:54:30 2011 +0200
Correcting indenting in 0.9.4-3 changelog entry.
diff --git a/debian/changelog b/debian/changelog
index 282c52c..54ae576 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,10 +1,10 @@
pywebdav (0.9.4-3) unstable; urgency=high
* Changing my email address.
- * Merging upstream version 0.9.4.1.
- New bugfix upstream release including security fixes for
- MySQL injection possibility in MySQLAuthHandler
- found by Teeed <op.pl> filed under CVE-2011-C0432
+ * Merging upstream version 0.9.4.1:
+ - New bugfix upstream release including security fixes for
+ MySQL injection possibility in MySQLAuthHandler
+ found by Teeed <op.pl> filed under CVE-2011-C0432
-- Mathias Behrle <mathiasb at m9s.biz> Thu, 18 Feb 2011 17:16:54 +0100
commit c763f365759555cb22c9b022fc98129e842316fd
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Jul 10 14:53:22 2011 +0200
Moving to source format 3.0 (quilt).
diff --git a/debian/README.source b/debian/README.source
deleted file mode 100644
index 18b6b24..0000000
--- a/debian/README.source
+++ /dev/null
@@ -1,37 +0,0 @@
-Package Repositories
---------------------
-
-Backports for the current stable debian distribution as well as snapshots of
-unreleased versions may be available in repositories listed on the maintainers
-homepage. The current URL of the maintainer homepage can be seen in
-debian/copyright.
-
-
-Source Access
--------------
-
-You can obtain the sources of this package with:
-
- $ apt-get source ${PACKAGE}
-
-whereas '${PACKAGE}' has to be replaced with the actual name of the package.
-
-This package is maintained with the Git version control system. The current git
-source tree can be obtained with:
-
- $ git clone ${GIT_URI}
-
-whereas '${GIT_URI}' has to be replaced with the actual URI for the Git
-repository. The current Git URI can be seen in debian/control in the extracted
-package sources.
-
-More information about Git can be found in the git-core package.
-
-This package may use the Quilt patch system to manage all modifications to the
-upstream source. Changes, if any, are stored in the source package as diffs in
-debian/diff and are applied during the build. Current modifications can be
-applied to the source tree with:
-
- $ QUILT_PATCHES=debian/patches quilt push -a
-
-More information about Quilt can be found in the quilt package.
diff --git a/debian/source/options b/debian/source/options
new file mode 100644
index 0000000..d053b65
--- /dev/null
+++ b/debian/source/options
@@ -0,0 +1,2 @@
+compression = gzip
+compression-level = 9
commit 0248357b82ca0e44ff2bc7f9a85fa578b1545ae8
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Tue Apr 26 21:08:51 2011 +0200
Updating to standards version 3.9.2.
diff --git a/debian/control b/debian/control
index 8baee31..ed129b4 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Uploaders:
Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
-Standards-Version: 3.9.1
+Standards-Version: 3.9.2
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit 3c505880ef0a5478ca3f62dcf39ab6d8683dd97e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Mon Mar 21 11:35:04 2011 +0100
Updating Copyright.
diff --git a/debian/copyright b/debian/copyright
index a729681..0eb5081 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -27,7 +27,7 @@ License: GPL-2+
can be found in /usr/share/common-licenses/GPL-2 file.
Files: debian/*
-Copyright: (C) 2009-2010 Daniel Baumann <daniel at debian.org>
+Copyright: (C) 2009-2011 Daniel Baumann <daniel at debian.org>
License: GPL-2+
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
commit d809f6343529683bb52118e6a97c58d4ac3fd43e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 18 17:17:23 2011 +0100
Releasing debian version 0.9.4-3.
diff --git a/debian/changelog b/debian/changelog
index 716980c..282c52c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+pywebdav (0.9.4-3) unstable; urgency=high
+
+ * Changing my email address.
+ * Merging upstream version 0.9.4.1.
+ New bugfix upstream release including security fixes for
+ MySQL injection possibility in MySQLAuthHandler
+ found by Teeed <op.pl> filed under CVE-2011-C0432
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Thu, 18 Feb 2011 17:16:54 +0100
+
pywebdav (0.9.4-2) experimental; urgency=low
* Updating standards version to 3.9.0.
commit 0ee5f6bcc3c959a56e1e8b546b0c17106ab3e883
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Feb 18 17:14:45 2011 +0100
Merging 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 84a43e22ed7d464d1e2ed63c316efd81bdc0b1c4
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Sat Nov 20 11:48:50 2010 +0100
Changing my email address.
diff --git a/debian/control b/debian/control
index 51206bd..8baee31 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
Uploaders:
Daniel Baumann <daniel at debian.org>,
- Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
+ Mathias Behrle <mathiasb at m9s.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
Standards-Version: 3.9.1
commit 38940baa30f85bc6043ec43d99316c2c0248fc72
Author: Daniel Baumann <daniel at debian.org>
Date: Fri Nov 12 14:16:21 2010 +0100
Releasing debian version 0.9.4-2.
diff --git a/debian/changelog b/debian/changelog
index 6b6392a..716980c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+pywebdav (0.9.4-2) experimental; urgency=low
+
+ * Updating standards version to 3.9.0.
+ * Updating to debhelper version 8.
+ * Updating to standards version 3.9.1.
+ * Switching to source format 3.0 (quilt).
+
+ -- Daniel Baumann <daniel at debian.org> Fri, 12 Nov 2010 14:16:14 +0100
+
pywebdav (0.9.4-1) unstable; urgency=low
* Adding Dm-Upload-Allowed in control in preparation for Mathias.
commit 256394e585d9bb1c8dfcc974632780bc571a5431
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Nov 2 14:23:21 2010 +0100
Switching to source format 3.0 (quilt).
diff --git a/debian/source/format b/debian/source/format
index d3827e7..163aaf8 100644
--- a/debian/source/format
+++ b/debian/source/format
@@ -1 +1 @@
-1.0
+3.0 (quilt)
commit f7c4971208c47530a9f26fde1b324060c95de525
Author: Daniel Baumann <daniel at debian.org>
Date: Thu Sep 30 01:04:54 2010 +0200
Updating to standards version 3.9.1.
diff --git a/debian/control b/debian/control
index 010f9ae..51206bd 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Uploaders:
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
-Standards-Version: 3.9.0
+Standards-Version: 3.9.1
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit c6c22314efbeb3e5f6ea47503a16754b04d479ad
Author: Daniel Baumann <daniel at debian.org>
Date: Thu Sep 30 01:04:04 2010 +0200
Updating to debhelper version 8.
diff --git a/debian/compat b/debian/compat
index 7f8f011..45a4fb7 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-7
+8
diff --git a/debian/control b/debian/control
index 811e98d..010f9ae 100644
--- a/debian/control
+++ b/debian/control
@@ -6,7 +6,7 @@ Uploaders:
Daniel Baumann <daniel at debian.org>,
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Dm-Upload-Allowed: yes
-Build-Depends: debhelper (>= 7), python, python-setuptools, python-support
+Build-Depends: debhelper (>= 8), python, python-setuptools, python-support
Standards-Version: 3.9.0
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
commit 0d8281e5065613d71591c2b0516e3452e77698e8
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Jun 28 20:35:28 2010 +0200
Updating standards version to 3.9.0.
diff --git a/debian/control b/debian/control
index 4ad4a69..811e98d 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Uploaders:
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 7), python, python-setuptools, python-support
-Standards-Version: 3.8.4
+Standards-Version: 3.9.0
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit 9c158f26c6376489e948e85a9b698dd73a3fc5c2
Author: Daniel Baumann <daniel at debian.org>
Date: Fri Apr 23 07:13:14 2010 +0200
Releasing debian version 0.9.4-1.
diff --git a/debian/changelog b/debian/changelog
index fe38c6f..6b6392a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+pywebdav (0.9.4-1) unstable; urgency=low
+
+ * Adding Dm-Upload-Allowed in control in preparation for Mathias.
+ * Merging upstream version 0.9.4.
+
+ -- Daniel Baumann <daniel at debian.org> Fri, 23 Apr 2010 07:12:59 +0200
+
pywebdav (0.9.3-8) unstable; urgency=low
* Adding a manpage for davserver.
commit 4531465c8d9bd35c5a00491fcc3294cf47775628
Author: Daniel Baumann <daniel at debian.org>
Date: Fri Apr 23 07:12:00 2010 +0200
Merging 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 b970610a43eed435839f684eb4ec93b0a06003a0
Author: Daniel Baumann <daniel at debian.org>
Date: Fri Apr 9 14:31:32 2010 +0200
Adding Dm-Upload-Allowed in control in preparation for Mathias.
diff --git a/debian/control b/debian/control
index 92ce73f..4ad4a69 100644
--- a/debian/control
+++ b/debian/control
@@ -5,6 +5,7 @@ Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
Uploaders:
Daniel Baumann <daniel at debian.org>,
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
+Dm-Upload-Allowed: yes
Build-Depends: debhelper (>= 7), python, python-setuptools, python-support
Standards-Version: 3.8.4
Homepage: http://code.google.com/p/pywebdav/
commit 9f2a79bf6dbd1be7f28ec4390448fd48ecedb603
Author: Daniel Baumann <daniel at debian.org>
Date: Thu Apr 8 12:59:58 2010 +0200
Releasing debian version 0.9.3-8.
diff --git a/debian/changelog b/debian/changelog
index 2a4cb47..fe38c6f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.3-8) unstable; urgency=low
+
+ * Adding a manpage for davserver.
+
+ -- Daniel Baumann <daniel at debian.org> Thu, 08 Apr 2010 12:59:51 +0200
+
pywebdav (0.9.3-7) unstable; urgency=low
* Removing unneeded python-all-dev from build-depends.
commit 8741219d0af8d90d93ea5d676bfef09d25d7be68
Author: Daniel Baumann <daniel at debian.org>
Date: Thu Apr 8 12:58:03 2010 +0200
Adding a manpage for davserver.
diff --git a/debian/manpages/davserver.1 b/debian/manpages/davserver.1
new file mode 100644
index 0000000..135b3cd
--- /dev/null
+++ b/debian/manpages/davserver.1
@@ -0,0 +1,49 @@
+.TH DAVSERVER 1 "2010\-04\-08" "0.9.3" "WebDAV server"
+
+.SH NAME
+davserver \-WebDAV server implementation in Python
+
+.SH SYNOPSIS
+\fBdavserver\fR \fIOPTIONS\fR
+
+.SH DESCRIPTION
+ PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a simple interface to webdav services to any application which needs it. It can be run as a daemon.
+
+.SH OPTIONS
+.IP "\-c, \-\-config" 4
+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.
+.IP "\-D, \-\-directory" 4
+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
+.IP "\-H, \-\-host" 4
+Host where to listen on (default: localhost).
+.IP "\-P, \-\-port" 4
+Port to bind server to (default: 8008).
+.IP "\-u, \-\-user" 4
+Username for authentication.
+.IP "\-p, \-\-password" 4
+Password for given user.
+.IP "\-n, \-\-noauth" 4
+Pass parameter if server should not ask for authentication. This means that every user has access.
+.IP "\-m, \-\-mysql" 4
+Pass this parameter if you want MySQL based authentication. If you want to use MySQL then the usage of a configuration file is mandatory.
+.IP "\-J, \-\-lockemu" 4
+Activate experimental LOCK and UNLOCK mode (WebDAV Version 2). Currently know to work but needs more tests. Default is ON.
+.IP "\-M, \-\-nomime" 4
+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.
+.IP "\-i, \-\-icounter" 4
+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.
+.IP "\-d, \-\-daemon \fIACTION\fR" 4
+Make server act like a daemon. That means that it is going 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 actions to this parameter, 'start' to start the daemon, 'stop' to stop the daemon, 'restart' to restart the server, or, 'status' to return the status of the server.
+.IP "\-v, \-\-verbose" 4
+Be verbose.
+.IP "\-h, \-\-help" 4
+Show this screen.
+
+.SH HOMEPAGE
+More information about davserver and the PyWebDAV project can be found at <\fIhttp://code.google.com/p/pywebdav/\fR>.
+
+.SH AUTHOR
+PyWebDAV was written by Simon Pamies <\fIs.pamies at banality.de\fR>, Christian Scholz <\fIruebe at aachen.heimat.de\fR>, and Vince Spicer <\fIvince at vince.ca\fR>.
+L
+.PP
+This manual page was written by Daniel Baumann <\fIdaniel at debian.org\fR>, for the Debian project (but may be used by others).
diff --git a/debian/python-webdav.manpages b/debian/python-webdav.manpages
new file mode 100644
index 0000000..2fb19fb
--- /dev/null
+++ b/debian/python-webdav.manpages
@@ -0,0 +1 @@
+debian/manpages/*
commit 4c6771466ed0a5f5a6ed15b5d9f90a3c4473391f
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Feb 20 09:35:14 2010 +0100
Releasing debian version 0.9.3-7.
diff --git a/debian/changelog b/debian/changelog
index 243d1b5..2a4cb47 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+pywebdav (0.9.3-7) unstable; urgency=low
+
+ * Removing unneeded python-all-dev from build-depends.
+ * Updating to standards 3.8.4.
+ * Updating README.source.
+
+ -- Daniel Baumann <daniel at debian.org> Sat, 20 Feb 2010 09:35:09 +0100
+
pywebdav (0.9.3-6) unstable; urgency=low
* Adding explicit debian source version 1.0 until switch to 3.0.
commit 4dae29fde06e3aea98c7baf2c01c6f76458864a9
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Feb 20 09:34:39 2010 +0100
Updating README.source.
diff --git a/debian/README.source b/debian/README.source
index dcc7ba3..18b6b24 100644
--- a/debian/README.source
+++ b/debian/README.source
@@ -32,6 +32,6 @@ upstream source. Changes, if any, are stored in the source package as diffs in
debian/diff and are applied during the build. Current modifications can be
applied to the source tree with:
- $ quilt push -a
+ $ QUILT_PATCHES=debian/patches quilt push -a
More information about Quilt can be found in the quilt package.
commit 66dfd5f5decafdd27256f342ff5b09ef23b51577
Author: Daniel Baumann <daniel at debian.org>
Date: Thu Jan 28 07:53:20 2010 +0100
Updating to standards 3.8.4.
diff --git a/debian/control b/debian/control
index f488627..92ce73f 100644
--- a/debian/control
+++ b/debian/control
@@ -6,7 +6,7 @@ Uploaders:
Daniel Baumann <daniel at debian.org>,
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Build-Depends: debhelper (>= 7), python, python-setuptools, python-support
-Standards-Version: 3.8.3
+Standards-Version: 3.8.4
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit beea4bc655cad94d07da522f9f9282e02adfde59
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Jan 25 10:12:02 2010 +0100
Removing unneeded python-all-dev from build-depends.
diff --git a/debian/control b/debian/control
index b3952c9..f488627 100644
--- a/debian/control
+++ b/debian/control
@@ -5,9 +5,7 @@ Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
Uploaders:
Daniel Baumann <daniel at debian.org>,
Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
-Build-Depends:
- debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools,
- python-support
+Build-Depends: debhelper (>= 7), python, python-setuptools, python-support
Standards-Version: 3.8.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
commit 96d8f6a6f8942d903962921e8e3f8174f7e8bccc
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Jan 9 18:17:10 2010 +0100
Releasing debian version 0.9.3-6.
diff --git a/debian/changelog b/debian/changelog
index ef6c19a..243d1b5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+pywebdav (0.9.3-6) unstable; urgency=low
+
+ * Adding explicit debian source version 1.0 until switch to 3.0.
+ * Updating year in copyright file.
+
+ -- Daniel Baumann <daniel at debian.org> Sat, 09 Jan 2010 18:17:00 +0100
+
pywebdav (0.9.3-5) unstable; urgency=low
* Adding maintainer homepage field to control.
commit 923506211f717a442b9c68928678715afa5b7149
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Jan 2 11:34:49 2010 +0100
Updating year in copyright file.
diff --git a/debian/copyright b/debian/copyright
index ee1e2f1..a729681 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -27,7 +27,7 @@ License: GPL-2+
can be found in /usr/share/common-licenses/GPL-2 file.
Files: debian/*
-Copyright: (C) 2009 Daniel Baumann <daniel at debian.org>
+Copyright: (C) 2009-2010 Daniel Baumann <daniel at debian.org>
License: GPL-2+
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
commit 52a2523e68d56660a742abc98a7ab5ff54873002
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Dec 12 10:06:05 2009 +0100
Adding explicit debian source version 1.0 until switch to 3.0.
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..d3827e7
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+1.0
commit c59e089c5cf4d22b37c9970ca783067adba38163
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Sep 5 09:35:19 2009 +0200
Releasing debian version 0.9.3-5.
diff --git a/debian/changelog b/debian/changelog
index 63a2183..ef6c19a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+pywebdav (0.9.3-5) unstable; urgency=low
+
+ * Adding maintainer homepage field to control.
+ * Adding README.source.
+ * Moving maintainer homepage field to copyright.
+ * Updating README.source.
+
+ -- Daniel Baumann <daniel at debian.org> Sat, 05 Sep 2009 09:35:05 +0200
+
pywebdav (0.9.3-4) unstable; urgency=low
[ Daniel Baumann ]
commit 5d636523eab9aed23bcb7d0012de558df3beb13c
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Sep 5 09:31:18 2009 +0200
Updating README.source.
diff --git a/debian/README.source b/debian/README.source
index 259f923..dcc7ba3 100644
--- a/debian/README.source
+++ b/debian/README.source
@@ -4,7 +4,7 @@ Package Repositories
Backports for the current stable debian distribution as well as snapshots of
unreleased versions may be available in repositories listed on the maintainers
homepage. The current URL of the maintainer homepage can be seen in
-debian/control.
+debian/copyright.
Source Access
commit 04ad3089039dd221a48ac1d57f8ce889f7c85f5b
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Sep 5 08:48:47 2009 +0200
Moving maintainer homepage field to copyright.
diff --git a/debian/control b/debian/control
index 93cad78..b3952c9 100644
--- a/debian/control
+++ b/debian/control
@@ -12,7 +12,6 @@ Standards-Version: 3.8.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
-XSBC-Maintainer-Homepage: http://tryton.debian-maintainers.org/
Package: python-webdav
Architecture: all
diff --git a/debian/copyright b/debian/copyright
index b2c218f..ee1e2f1 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,8 +1,7 @@
-Authors:
- Simon Pamies <s.pamies at banality.de>
- Christian Scholz <mrtopf at webdav.de>
- Vince Spicer <vince at vince.ca>
-Download: http://code.google.com/p/pywebdav/
+Upstream-Contact: Pywebdav Project <pywebdav at googlegroups.com>
+Upstream-Homepage: http://code.google.com/p/pywebdav/
+Maintainer-Contact: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
+Maintainer-Homepage: http://tryton.debian-maintainers.org/
Files: *
Copyright:
commit d13483ecf9a18b83f074a63f13bb427bc87d2708
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Aug 25 08:18:11 2009 +0200
Adding README.source.
diff --git a/debian/README.source b/debian/README.source
new file mode 100644
index 0000000..259f923
--- /dev/null
+++ b/debian/README.source
@@ -0,0 +1,37 @@
+Package Repositories
+--------------------
+
+Backports for the current stable debian distribution as well as snapshots of
+unreleased versions may be available in repositories listed on the maintainers
+homepage. The current URL of the maintainer homepage can be seen in
+debian/control.
+
+
+Source Access
+-------------
+
+You can obtain the sources of this package with:
+
+ $ apt-get source ${PACKAGE}
+
+whereas '${PACKAGE}' has to be replaced with the actual name of the package.
+
+This package is maintained with the Git version control system. The current git
+source tree can be obtained with:
+
+ $ git clone ${GIT_URI}
+
+whereas '${GIT_URI}' has to be replaced with the actual URI for the Git
+repository. The current Git URI can be seen in debian/control in the extracted
+package sources.
+
+More information about Git can be found in the git-core package.
+
+This package may use the Quilt patch system to manage all modifications to the
+upstream source. Changes, if any, are stored in the source package as diffs in
+debian/diff and are applied during the build. Current modifications can be
+applied to the source tree with:
+
+ $ quilt push -a
+
+More information about Quilt can be found in the quilt package.
commit 836493ab4007d67f842ef4099380e789dc053591
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Aug 24 17:49:44 2009 +0200
Adding maintainer homepage field to control.
diff --git a/debian/control b/debian/control
index b3952c9..93cad78 100644
--- a/debian/control
+++ b/debian/control
@@ -12,6 +12,7 @@ Standards-Version: 3.8.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
+XSBC-Maintainer-Homepage: http://tryton.debian-maintainers.org/
Package: python-webdav
Architecture: all
commit bbcf0b9f47726ae2da1afdaaf598fcc5759b7aa8
Author: Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Date: Tue Aug 18 15:17:40 2009 +0200
Releasing debian version 0.9.3-4.
diff --git a/debian/changelog b/debian/changelog
index d466384..63a2183 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+pywebdav (0.9.3-4) unstable; urgency=low
+
+ [ Daniel Baumann ]
+ * Updating to standards version 3.8.3.
+
+ [ Mathias Behrle ]
+ * Removing old dependency on python-xml (Closes: #542203).
+
+ -- Mathias Behrle <mathiasb at obelix.violadagamba.myfqdn.de> Tue, 18 Aug 2009 15:16:45 +0200
+
pywebdav (0.9.3-3) unstable; urgency=low
* Updating maintainer field.
commit b84bc7e4d49dc7508ed8d4588cd0bb98bd7a73ab
Author: Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Date: Tue Aug 18 14:53:12 2009 +0200
Removing old dependency on python-xml (Closes: #542203).
diff --git a/debian/control b/debian/control
index be15b8b..b3952c9 100644
--- a/debian/control
+++ b/debian/control
@@ -15,7 +15,7 @@ Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
Package: python-webdav
Architecture: all
-Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, python-xml
+Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources
XB-Python-Version: ${python:Versions}
Description: WebDAV server implementation in Python
PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a
commit c814a199ffd561c56e8294d72ff0926a0a9bb9d7
Author: Daniel Baumann <daniel at debian.org>
Date: Sun Aug 16 10:12:35 2009 +0200
Updating to standards version 3.8.3.
diff --git a/debian/control b/debian/control
index 9c60d62..be15b8b 100644
--- a/debian/control
+++ b/debian/control
@@ -8,7 +8,7 @@ Uploaders:
Build-Depends:
debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools,
python-support
-Standards-Version: 3.8.2
+Standards-Version: 3.8.3
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
commit 15cb14a3412442af5ab98a7490f542aa16ef7752
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Aug 10 19:06:20 2009 +0200
Releasing debian version 0.9.3-3.
diff --git a/debian/changelog b/debian/changelog
index 430e5ea..d466384 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+pywebdav (0.9.3-3) unstable; urgency=low
+
+ * Updating maintainer field.
+ * Updating vcs fields.
+ * Adding Mathias as co-maintainer.
+ * Wrapping lines in control.
+
+ -- Daniel Baumann <daniel at debian.org> Mon, 10 Aug 2009 19:06:17 +0200
+
pywebdav (0.9.3-2) unstable; urgency=low
* Minimizing rules file.
commit 03b7266d8717a411ab0c7fab01f2c1365cbb05c8
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Aug 10 19:05:23 2009 +0200
Wrapping lines in control.
diff --git a/debian/control b/debian/control
index 0561d45..9c60d62 100644
--- a/debian/control
+++ b/debian/control
@@ -2,8 +2,12 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
-Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
-Build-Depends: debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools, python-support
+Uploaders:
+ Daniel Baumann <daniel at debian.org>,
+ Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
+Build-Depends:
+ debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools,
+ python-support
Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
commit 4ae17baf6d03ab29fb82afc461e6171d1828dd30
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Aug 10 12:34:48 2009 +0200
Adding Mathias as co-maintainer.
diff --git a/debian/control b/debian/control
index 87ab2ee..0561d45 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
-Uploaders: Daniel Baumann <daniel at debian.org>
+Uploaders: Daniel Baumann <daniel at debian.org>, Mathias Behrle <mathiasb at mbsolutions.selfip.biz>
Build-Depends: debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools, python-support
Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
commit 37c278056f72a87279a67e09b60ed85f94eb6578
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Aug 8 23:26:50 2009 +0200
Updating vcs fields.
diff --git a/debian/control b/debian/control
index 2c25130..87ab2ee 100644
--- a/debian/control
+++ b/debian/control
@@ -6,8 +6,8 @@ Uploaders: Daniel Baumann <daniel at debian.org>
Build-Depends: debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools, python-support
Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
-Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
-Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
+Vcs-Browser: http://git.debian-maintainers.org/?p=tryton/pywebdav.git
+Vcs-Git: git://git.debian-maintainers.org/git/tryton/pywebdav.git
Package: python-webdav
Architecture: all
commit 4e0846baff322f0662efdb90327a6c577cc72336
Author: Daniel Baumann <daniel at debian.org>
Date: Sat Aug 8 23:19:30 2009 +0200
Updating maintainer field.
diff --git a/debian/control b/debian/control
index 09d3d89..2c25130 100644
--- a/debian/control
+++ b/debian/control
@@ -1,7 +1,8 @@
Source: pywebdav
Section: python
Priority: optional
-Maintainer: Daniel Baumann <daniel at debian.org>
+Maintainer: Debian Tryton Maintainers <tryton at lists.debian-maintainers.org>
+Uploaders: Daniel Baumann <daniel at debian.org>
Build-Depends: debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools, python-support
Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
commit 0efb5b6b9b9a149fd91d1ff1e1151ae227eb6654
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Jul 27 14:53:24 2009 +0200
Releasing debian version 0.9.3-2.
diff --git a/debian/changelog b/debian/changelog
index 91451c8..430e5ea 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.3-2) unstable; urgency=low
+
+ * Minimizing rules file.
+
+ -- Daniel Baumann <daniel at debian.org> Mon, 27 Jul 2009 14:53:22 +0200
+
pywebdav (0.9.3-1) unstable; urgency=low
* Merging upstream version 0.9.3.
commit a857f0a9e03e284c51640d8303c269624b9053ab
Author: Daniel Baumann <daniel at debian.org>
Date: Mon Jul 27 14:53:17 2009 +0200
Minimizing rules file.
diff --git a/debian/rules b/debian/rules
index 7f91e66..000210b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,43 +1,4 @@
#!/usr/bin/make -f
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp
-
- python setup.py clean
- rm -rf dist build PyWebDAV.egg-info
- find -type f -name "*.pyc" | xargs rm -f
-
- dh_clean
-
-build:
-
-install:
- dh_testdir
- dh_testroot
- dh_prep
-
- python setup.py install --single-version-externally-managed --root=$(CURDIR)/debian/python-webdav --install-layout=deb --install-lib /usr/share/python-support/python-webdav
-
- find debian/python-webdav -type f -name "*.pyc" | xargs rm -f
-
-binary: binary-indep
-
-binary-arch:
-
-binary-indep: install
- dh_testdir
- dh_testroot
- dh_installchangelogs doc/Changes
- dh_installdocs
- dh_installexamples
- dh_pysupport
- dh_compress
- dh_fixperms
- dh_installdeb
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-.PHONY: clean build install binary binary-arch binary-indep
+%:
+ dh ${@}
commit 77bcbebd57e0a09b8fb2832759772d335b183a19
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 7 16:34:52 2009 +0200
Releasing debian version 0.9.3-1.
diff --git a/debian/changelog b/debian/changelog
index c463798..91451c8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+pywebdav (0.9.3-1) unstable; urgency=low
+
+ * Merging upstream version 0.9.3.
+ * Updating package to standards version 3.8.2.
+ * Tidy rules file.
+ * Removing path.patch and status.patch, went upstream.
+
+ -- Daniel Baumann <daniel at debian.org> Tue, 07 Jul 2009 16:34:47 +0200
+
pywebdav (0.9.2-6) unstable; urgency=low
* Adding missing build-depends for quilt (Closes: #531017).
commit b9fb1e9896c0d59af57e0a5779416a63aa255d62
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 7 16:34:39 2009 +0200
Removing path.patch and status.patch, went upstream.
diff --git a/debian/control b/debian/control
index f0c99a0..09d3d89 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
-Build-Depends: debhelper (>= 7), quilt, python, python-all-dev (>= 2.5.4), python-setuptools, python-support
+Build-Depends: debhelper (>= 7), python, python-all-dev (>= 2.5.4), python-setuptools, python-support
Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
diff --git a/debian/patches/01-path.patch b/debian/patches/01-path.patch
deleted file mode 100644
index 554a2fd..0000000
--- a/debian/patches/01-path.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-Author: Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
-Description: Fixes that dav server does not list any files (Closes: #529256).
-
-diff -Naurp pywebdav.orig/DAVServer/server.py pywebdav/DAVServer/server.py
---- pywebdav.orig/DAVServer/server.py 2009-05-11 21:43:08.000000000 +0000
-+++ pywebdav/DAVServer/server.py 2009-05-19 10:42:09.000000000 +0000
-@@ -52,6 +52,7 @@ def runserver(
- server = BaseHTTPServer.HTTPServer):
-
- directory = directory.strip()
-+ directory = directory.rstrip('/')
- host = host.strip()
-
- if not os.path.isdir(directory):
diff --git a/debian/patches/02-status.patch b/debian/patches/02-status.patch
deleted file mode 100644
index 4f861b3..0000000
--- a/debian/patches/02-status.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-Author: Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
-Description:
- Fixes that daemonaction status does not work when daemon is running
- (Closes: #529262).
-
-diff -Naurp pywebdav.orig/DAVServer/server.py pywebdav/DAVServer/server.py
---- pywebdav.orig/DAVServer/server.py 2009-05-12 20:24:28.000000000 +0000
-+++ pywebdav/DAVServer/server.py 2009-05-18 12:00:11.000000000 +0000
-@@ -272,7 +272,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/debian/patches/series b/debian/patches/series
deleted file mode 100644
index fa15d29..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,2 +0,0 @@
-01-path.patch
-02-status.patch
diff --git a/debian/rules b/debian/rules
index df3eb1e..7f91e66 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,8 +1,6 @@
#!/usr/bin/make -f
-include /usr/share/quilt/quilt.make
-
-clean: unpatch
+clean:
dh_testdir
dh_testroot
rm -f build-stamp
@@ -15,7 +13,7 @@ clean: unpatch
build:
-install: patch
+install:
dh_testdir
dh_testroot
dh_prep
commit 26008646e08dc00ed06d2de5c63b60d072163106
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 7 16:32:02 2009 +0200
Tidy rules file.
diff --git a/debian/rules b/debian/rules
index d5571c7..df3eb1e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -19,7 +19,6 @@ install: patch
dh_testdir
dh_testroot
dh_prep
- dh_installdirs
python setup.py install --single-version-externally-managed --root=$(CURDIR)/debian/python-webdav --install-layout=deb --install-lib /usr/share/python-support/python-webdav
commit 0601b6561482f89b6c7f3d72b2d4352ba2e219b4
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 7 16:31:57 2009 +0200
Updating package to standards version 3.8.2.
diff --git a/debian/control b/debian/control
index 8ec87a3..f0c99a0 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: python
Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
Build-Depends: debhelper (>= 7), quilt, python, python-all-dev (>= 2.5.4), python-setuptools, python-support
-Standards-Version: 3.8.1
+Standards-Version: 3.8.2
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
commit a54654ed3c04f3e2802ff30a6cdcbfe024526f04
Author: Daniel Baumann <daniel at debian.org>
Date: Tue Jul 7 16:30:24 2009 +0200
Merging 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 070d3cf55cd6d4ad168ab3438ac8747a730efdbf
Author: Daniel Baumann <daniel at debian.org>
Date: Fri May 29 14:33:10 2009 +0200
Releasing debian version 0.9.2-6.
diff --git a/debian/changelog b/debian/changelog
index 7ac5498..c463798 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+pywebdav (0.9.2-6) unstable; urgency=low
+
+ * Adding missing build-depends for quilt (Closes: #531017).
+ * Making pywebdev build with python2.6, thanks to Alessio Treglia
+ <quadrispro at ubuntu.com> (Closes: #531016).
+
+ -- Daniel Baumann <daniel at debian.org> Fri, 29 May 2009 14:33:01 +0200
+
pywebdav (0.9.2-5) unstable; urgency=low
* Updating patch.patch (Closes: #529256).
commit 701c7be645ce5233ab7494c0ee1f824089310bc9
Author: Daniel Baumann <daniel at debian.org>
Date: Fri May 29 14:32:48 2009 +0200
Making pywebdev build with python2.6, thanks to Alessio Treglia <quadrispro at ubuntu.com> (Closes: #531016).
diff --git a/debian/control b/debian/control
index 083afab..8ec87a3 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
-Build-Depends: debhelper (>= 7), quilt, python, python-all-dev, python-setuptools, python-support
+Build-Depends: debhelper (>= 7), quilt, python, python-all-dev (>= 2.5.4), python-setuptools, python-support
Standards-Version: 3.8.1
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
diff --git a/debian/rules b/debian/rules
index af98602..d5571c7 100755
--- a/debian/rules
+++ b/debian/rules
@@ -21,7 +21,7 @@ install: patch
dh_prep
dh_installdirs
- python setup.py install --single-version-externally-managed --root=$(CURDIR)/debian/python-webdav --install-lib /usr/share/python-support/python-webdav
+ python setup.py install --single-version-externally-managed --root=$(CURDIR)/debian/python-webdav --install-layout=deb --install-lib /usr/share/python-support/python-webdav
find debian/python-webdav -type f -name "*.pyc" | xargs rm -f
commit fd748348dda7032d976e6428119a0df3b3863575
Author: Daniel Baumann <daniel at debian.org>
Date: Fri May 29 14:31:23 2009 +0200
Adding missing build-depends for quilt (Closes: #531017).
diff --git a/debian/control b/debian/control
index 1abdcfe..083afab 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: pywebdav
Section: python
Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
-Build-Depends: debhelper (>= 7), python, python-all-dev, python-setuptools, python-support
+Build-Depends: debhelper (>= 7), quilt, python, python-all-dev, python-setuptools, python-support
Standards-Version: 3.8.1
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
commit 29ec66a2106b5f2efa647ab64a14a026c73db917
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 19 12:43:23 2009 +0200
Releasing debian version 0.9.2-5.
diff --git a/debian/changelog b/debian/changelog
index 27a59f1..7ac5498 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.2-5) unstable; urgency=low
+
+ * Updating patch.patch (Closes: #529256).
+
+ -- Daniel Baumann <daniel at debian.org> Tue, 19 May 2009 12:43:19 +0200
+
pywebdav (0.9.2-4) unstable; urgency=low
* Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
commit 05fc64211f93e95b707501933bd16d97a15c4c52
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 19 12:43:03 2009 +0200
Updating patch.patch (Closes: #529256).
diff --git a/debian/patches/01-path.patch b/debian/patches/01-path.patch
index e3b65c1..554a2fd 100644
--- a/debian/patches/01-path.patch
+++ b/debian/patches/01-path.patch
@@ -1,15 +1,14 @@
Author: Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
Description: Fixes that dav server does not list any files (Closes: #529256).
-diff -Naurp pywebdav.orig/DAVServer/fshandler.py pywebdav/DAVServer/fshandler.py
---- pywebdav.orig/DAVServer/fshandler.py 2009-05-12 20:24:28.000000000 +0000
-+++ pywebdav/DAVServer/fshandler.py 2009-05-18 09:35:36.000000000 +0000
-@@ -62,7 +62,7 @@ class FilesystemHandler(dav_interface):
- """ map local filename to self.baseuri """
+diff -Naurp pywebdav.orig/DAVServer/server.py pywebdav/DAVServer/server.py
+--- pywebdav.orig/DAVServer/server.py 2009-05-11 21:43:08.000000000 +0000
++++ pywebdav/DAVServer/server.py 2009-05-19 10:42:09.000000000 +0000
+@@ -52,6 +52,7 @@ def runserver(
+ server = BaseHTTPServer.HTTPServer):
- pnum=len(split(self.directory,"/"))
-- parts=split(filename,"/")[pnum:]
-+ parts=split(filename,"/")[pnum-1:]
- sparts="/"+joinfields(parts,"/")
- uri=urlparse.urljoin(self.baseuri,sparts)
- return uri
+ directory = directory.strip()
++ directory = directory.rstrip('/')
+ host = host.strip()
+
+ if not os.path.isdir(directory):
commit 8b0d9e9222923474c52f33b8e4f35198382ff5a7
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 14:05:20 2009 +0200
Releasing debian version 0.9.2-4.
diff --git a/debian/changelog b/debian/changelog
index 7ecdcb8..27a59f1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+pywebdav (0.9.2-4) unstable; urgency=low
+
+ * Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
+ to fix that daemonaction status does not work when daemon is running
+ (Closes: #529262).
+
+ -- Daniel Baumann <daniel at debian.org> Mon, 18 May 2009 14:05:15 +0200
+
pywebdav (0.9.2-3) unstable; urgency=low
* Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
commit 3b52e049f9a4c10aea115bdf1b3249953685b02a
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 14:04:56 2009 +0200
Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de> to fix that daemonaction status does not work when daemon is running (Closes: #529262).
diff --git a/debian/patches/02-status.patch b/debian/patches/02-status.patch
new file mode 100644
index 0000000..4f861b3
--- /dev/null
+++ b/debian/patches/02-status.patch
@@ -0,0 +1,17 @@
+Author: Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
+Description:
+ Fixes that daemonaction status does not work when daemon is running
+ (Closes: #529262).
+
+diff -Naurp pywebdav.orig/DAVServer/server.py pywebdav/DAVServer/server.py
+--- pywebdav.orig/DAVServer/server.py 2009-05-12 20:24:28.000000000 +0000
++++ pywebdav/DAVServer/server.py 2009-05-18 12:00:11.000000000 +0000
+@@ -272,7 +272,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/debian/patches/series b/debian/patches/series
index 844003c..fa15d29 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,2 @@
01-path.patch
+02-status.patch
commit 060e1ee0aa42dec7c46248f2603bbc82a78c4099
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 11:38:43 2009 +0200
Releasing debian version 0.9.2-3.
diff --git a/debian/changelog b/debian/changelog
index 471206e..7ecdcb8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+pywebdav (0.9.2-3) unstable; urgency=low
+
+ * Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
+ to fix that dav server does not list any files (Closes: #529256).
+
+ -- Daniel Baumann <daniel at debian.org> Mon, 18 May 2009 11:38:32 +0200
+
pywebdav (0.9.2-2) unstable; urgency=low
* Installing server config.ini as example (Closes: #529247).
commit 056bac4a26acf53f167db6a2bc164556c65e5d8b
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 11:38:13 2009 +0200
Adding patch from Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de> to fix that dav server does not list any files (Closes: #529256).
diff --git a/debian/patches/01-path.patch b/debian/patches/01-path.patch
new file mode 100644
index 0000000..e3b65c1
--- /dev/null
+++ b/debian/patches/01-path.patch
@@ -0,0 +1,15 @@
+Author: Joerg Friedrich <Joerg.Friedrich at friedrich-kn.de>
+Description: Fixes that dav server does not list any files (Closes: #529256).
+
+diff -Naurp pywebdav.orig/DAVServer/fshandler.py pywebdav/DAVServer/fshandler.py
+--- pywebdav.orig/DAVServer/fshandler.py 2009-05-12 20:24:28.000000000 +0000
++++ pywebdav/DAVServer/fshandler.py 2009-05-18 09:35:36.000000000 +0000
+@@ -62,7 +62,7 @@ class FilesystemHandler(dav_interface):
+ """ map local filename to self.baseuri """
+
+ pnum=len(split(self.directory,"/"))
+- parts=split(filename,"/")[pnum:]
++ parts=split(filename,"/")[pnum-1:]
+ sparts="/"+joinfields(parts,"/")
+ uri=urlparse.urljoin(self.baseuri,sparts)
+ return uri
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..844003c
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+01-path.patch
diff --git a/debian/rules b/debian/rules
index 521eb23..af98602 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,6 +1,8 @@
#!/usr/bin/make -f
-clean:
+include /usr/share/quilt/quilt.make
+
+clean: unpatch
dh_testdir
dh_testroot
rm -f build-stamp
@@ -13,7 +15,7 @@ clean:
build:
-install:
+install: patch
dh_testdir
dh_testroot
dh_prep
commit 6931a5b2db5be857f20aafa1917fe0616d74eb58
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 11:31:16 2009 +0200
Releasing debian version 0.9.2-2.
diff --git a/debian/changelog b/debian/changelog
index 62d1304..471206e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.2-2) unstable; urgency=low
+
+ * Installing server config.ini as example (Closes: #529247).
+
+ -- Daniel Baumann <daniel at debian.org> Mon, 18 May 2009 11:30:58 +0200
+
pywebdav (0.9.2-1) unstable; urgency=low
* Merging upstream version 0.9.2.
commit 8a5505c3adabcbd858526703c192abfce89543cd
Author: Daniel Baumann <daniel at debian.org>
Date: Mon May 18 11:30:39 2009 +0200
Installing server config.ini as example (Closes: #529247).
diff --git a/debian/python-webdav.examples b/debian/python-webdav.examples
new file mode 100644
index 0000000..3a49740
--- /dev/null
+++ b/debian/python-webdav.examples
@@ -0,0 +1 @@
+DAVServer/config.ini
diff --git a/debian/rules b/debian/rules
index 43e7711..521eb23 100755
--- a/debian/rules
+++ b/debian/rules
@@ -32,6 +32,7 @@ binary-indep: install
dh_testroot
dh_installchangelogs doc/Changes
dh_installdocs
+ dh_installexamples
dh_pysupport
dh_compress
dh_fixperms
commit d3a506c0499507949c036ff12bfcb36290f72f93
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:25:54 2009 +0200
Releasing debian version 0.9.2-1.
diff --git a/debian/changelog b/debian/changelog
index 7b780ae..62d1304 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pywebdav (0.9.2-1) unstable; urgency=low
+
+ * Merging upstream version 0.9.2.
+
+ -- Daniel Baumann <daniel at debian.org> Tue, 12 May 2009 22:25:46 +0200
+
pywebdav (0.9.1-1) unstable; urgency=low
* Merging upstream version 0.9.1.
commit e47fe40d275a46abf678dd1e4cd7e19310ef6d59
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:24:29 2009 +0200
Merging 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 cd2897d23e30e2c9846c37bf446a02cbc072e9fe
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:14:59 2009 +0200
Releasing debian version 0.9.1-1.
diff --git a/debian/changelog b/debian/changelog
index 9c888cb..7b780ae 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,19 @@
+pywebdav (0.9.1-1) unstable; urgency=low
+
+ * Merging upstream version 0.9.1.
+ * Updating upstream homepage.
+ * Upgrading package to standards 3.8.1.
+ * Installing upstream documentation.
+ * Updating location of upstream changelog.
+ * Removing debhelper examples file, there are no examples anymore.
+ * Updating rules for new upstream.
+ * Updating year in copyright file.
+ * Updating author information in copyright file.
+ * Adding manual depends to python-xml.
+ * Using correct rfc-2822 date formats in changelog.
+
+ -- Daniel Baumann <daniel at debian.org> Tue, 12 May 2009 22:14:43 +0200
+
pywebdav (0.6-1) unstable; urgency=low
* Initial release.
commit 85781305a673d9be3302ef3992f9f1f7455389ef
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:14:35 2009 +0200
Using correct rfc-2822 date formats in changelog.
diff --git a/debian/changelog b/debian/changelog
index f25eef1..9c888cb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,4 +2,4 @@ pywebdav (0.6-1) unstable; urgency=low
* Initial release.
- -- Daniel Baumann <daniel at debian.org> Wed, 4 Feb 2009 21:56:00 +0100
+ -- Daniel Baumann <daniel at debian.org> Wed, 04 Feb 2009 21:56:00 +0100
commit f09dd7c6b25c8ac0fe654c80e5ef7434c78ba73e
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:14:01 2009 +0200
Adding manual depends to python-xml.
diff --git a/debian/control b/debian/control
index 0d725f8..1abdcfe 100644
--- a/debian/control
+++ b/debian/control
@@ -10,7 +10,7 @@ Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
Package: python-webdav
Architecture: all
-Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources
+Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, python-xml
XB-Python-Version: ${python:Versions}
Description: WebDAV server implementation in Python
PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a
commit 50d4dbc3709f6177b3168964532fe64a33b3e842
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:10:07 2009 +0200
Updating author information in copyright file.
diff --git a/debian/copyright b/debian/copyright
index bffbb98..b2c218f 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,8 +1,14 @@
-Author: Christian Scholz <ruebe at aachen.heimat.de>
+Authors:
+ Simon Pamies <s.pamies at banality.de>
+ Christian Scholz <mrtopf at webdav.de>
+ Vince Spicer <vince at vince.ca>
Download: http://code.google.com/p/pywebdav/
Files: *
-Copyright: (C) 1999-2009 Christian Scholz <ruebe at aachen.heimat.de>
+Copyright:
+ (C) 2009 Simon Pamies <s.pamies at banality.de>
+ (C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
+ (C) 2009 Vince Spicer <vince at vince.ca>
License: GPL-2+
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
commit 9dbfbad6bee6d1be91e7a47ebbf105b9edd707be
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:03:19 2009 +0200
Updating year in copyright file.
diff --git a/debian/copyright b/debian/copyright
index b02d7e7..bffbb98 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -2,7 +2,7 @@ Author: Christian Scholz <ruebe at aachen.heimat.de>
Download: http://code.google.com/p/pywebdav/
Files: *
-Copyright: (C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
+Copyright: (C) 1999-2009 Christian Scholz <ruebe at aachen.heimat.de>
License: GPL-2+
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
commit 85667b65128c7f9ba22c46e125b2beecc1e81e49
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 22:02:01 2009 +0200
Updating rules for new upstream.
diff --git a/debian/rules b/debian/rules
index f238e53..43e7711 100755
--- a/debian/rules
+++ b/debian/rules
@@ -6,8 +6,8 @@ clean:
rm -f build-stamp
python setup.py clean
- rm -rf dist build
- find . -type f -name "*.pyc" | xargs rm -f
+ rm -rf dist build PyWebDAV.egg-info
+ find -type f -name "*.pyc" | xargs rm -f
dh_clean
@@ -19,7 +19,7 @@ install:
dh_prep
dh_installdirs
- python setup.py install --root=$(CURDIR)/debian/python-webdav --install-lib /usr/share/python-support/python-webdav
+ python setup.py install --single-version-externally-managed --root=$(CURDIR)/debian/python-webdav --install-lib /usr/share/python-support/python-webdav
find debian/python-webdav -type f -name "*.pyc" | xargs rm -f
commit fc9368d8eb37aff02cc9981f9c666b0af95090bc
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:59:27 2009 +0200
Removing debhelper examples file, there are no examples anymore.
diff --git a/debian/python-webdav.examples b/debian/python-webdav.examples
deleted file mode 100644
index 30d29de..0000000
--- a/debian/python-webdav.examples
+++ /dev/null
@@ -1 +0,0 @@
-doc/*
diff --git a/debian/rules b/debian/rules
index 15ebd4c..f238e53 100755
--- a/debian/rules
+++ b/debian/rules
@@ -32,7 +32,6 @@ binary-indep: install
dh_testroot
dh_installchangelogs doc/Changes
dh_installdocs
- dh_installexamples
dh_pysupport
dh_compress
dh_fixperms
commit bf02a18102cec3bd35d2ae00fdcf15b4a58f3144
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:58:23 2009 +0200
Updating location of upstream changelog.
diff --git a/debian/rules b/debian/rules
index 7488401..15ebd4c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -30,7 +30,7 @@ binary-arch:
binary-indep: install
dh_testdir
dh_testroot
- dh_installchangelogs Changes
+ dh_installchangelogs doc/Changes
dh_installdocs
dh_installexamples
dh_pysupport
commit aa067f328306274fdac27f90e9b8601464946bb9
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:58:13 2009 +0200
Installing upstream documentation.
diff --git a/debian/python-webdav.docs b/debian/python-webdav.docs
new file mode 100644
index 0000000..c7c92e9
--- /dev/null
+++ b/debian/python-webdav.docs
@@ -0,0 +1,5 @@
+README
+doc/ARCHITECTURE
+doc/interface_class
+doc/TODO
+doc/walker
commit 653bc8cb7c1c7af0365efd5e4104f2fac84e2036
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:55:48 2009 +0200
Upgrading package to standards 3.8.1.
diff --git a/debian/control b/debian/control
index 00c921c..0d725f8 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: python
Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
Build-Depends: debhelper (>= 7), python, python-all-dev, python-setuptools, python-support
-Standards-Version: 3.8.0
+Standards-Version: 3.8.1
Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
commit 1fc1ad0ac3dc6d3f8b2e97206701d63292d8144e
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:55:32 2009 +0200
Updating upstream homepage.
diff --git a/debian/control b/debian/control
index a072ed1..00c921c 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Daniel Baumann <daniel at debian.org>
Build-Depends: debhelper (>= 7), python, python-all-dev, python-setuptools, python-support
Standards-Version: 3.8.0
-Homepage: http://pywebdav.sourceforge.net/
+Homepage: http://code.google.com/p/pywebdav/
Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
diff --git a/debian/copyright b/debian/copyright
index 14808c5..b02d7e7 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,5 +1,5 @@
Author: Christian Scholz <ruebe at aachen.heimat.de>
-Download: http://pywebdav.sourceforge.net/
+Download: http://code.google.com/p/pywebdav/
Files: *
Copyright: (C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
commit 64dcf6f4dda81ddc6c902109987eb9145369825c
Author: Daniel Baumann <daniel at debian.org>
Date: Tue May 12 21:54:37 2009 +0200
Merging 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 fea5e0b7cad9fdb4ce3b4fc56749833b1f89b61c
Author: Daniel Baumann <daniel at debian.org>
Date: Wed Feb 4 22:52:22 2009 +0100
Adding debian version 0.6-1.
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/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..f25eef1
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+pywebdav (0.6-1) unstable; urgency=low
+
+ * Initial release.
+
+ -- Daniel Baumann <daniel at debian.org> Wed, 4 Feb 2009 21:56:00 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..a072ed1
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,18 @@
+Source: pywebdav
+Section: python
+Priority: optional
+Maintainer: Daniel Baumann <daniel at debian.org>
+Build-Depends: debhelper (>= 7), python, python-all-dev, python-setuptools, python-support
+Standards-Version: 3.8.0
+Homepage: http://pywebdav.sourceforge.net/
+Vcs-Browser: http://git.debian.net/?p=debian/pywebdav.git
+Vcs-Git: git://git.debian.net/git/debian/pywebdav.git
+
+Package: python-webdav
+Architecture: all
+Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources
+XB-Python-Version: ${python:Versions}
+Description: WebDAV server implementation in Python
+ PyWebDAV is a WebDAV server implementation in Python. It's aim is to provide a
+ simple interface to webdav services to any application which needs it. It can
+ be run as a daemon.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..14808c5
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,42 @@
+Author: Christian Scholz <ruebe at aachen.heimat.de>
+Download: http://pywebdav.sourceforge.net/
+
+Files: *
+Copyright: (C) 1999-2006 Christian Scholz <ruebe at aachen.heimat.de>
+License: GPL-2+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+ .
+ This program 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 General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ .
+ On Debian systems, the complete text of the GNU General Public License
+ can be found in /usr/share/common-licenses/GPL-2 file.
+
+Files: debian/*
+Copyright: (C) 2009 Daniel Baumann <daniel at debian.org>
+License: GPL-2+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+ .
+ This program 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 General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ .
+ On Debian systems, the complete text of the GNU General Public License
+ can be found in /usr/share/common-licenses/GPL-2 file.
diff --git a/debian/pycompat b/debian/pycompat
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/debian/pycompat
@@ -0,0 +1 @@
+2
diff --git a/debian/python-webdav.examples b/debian/python-webdav.examples
new file mode 100644
index 0000000..30d29de
--- /dev/null
+++ b/debian/python-webdav.examples
@@ -0,0 +1 @@
+doc/*
diff --git a/debian/pyversions b/debian/pyversions
new file mode 100644
index 0000000..8b253bc
--- /dev/null
+++ b/debian/pyversions
@@ -0,0 +1 @@
+2.4-
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..7488401
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,44 @@
+#!/usr/bin/make -f
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp
+
+ python setup.py clean
+ rm -rf dist build
+ find . -type f -name "*.pyc" | xargs rm -f
+
+ dh_clean
+
+build:
+
+install:
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs
+
+ python setup.py install --root=$(CURDIR)/debian/python-webdav --install-lib /usr/share/python-support/python-webdav
+
+ find debian/python-webdav -type f -name "*.pyc" | xargs rm -f
+
+binary: binary-indep
+
+binary-arch:
+
+binary-indep: install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs Changes
+ dh_installdocs
+ dh_installexamples
+ dh_pysupport
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+.PHONY: clean build install binary binary-arch binary-indep
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