Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/journal2webdav
diff options
context:
space:
mode:
authorSascha Silbe <sascha-pgp@silbe.org>2013-06-01 15:52:32 (GMT)
committer Sascha Silbe <sascha-pgp@silbe.org>2013-06-01 15:52:32 (GMT)
commitf2608a44767b97817168dbe8507689657cf06572 (patch)
tree9340bcdfedf18eeb567a5d2c5af18914515552d3 /journal2webdav
parent6cbc55666a045c802c1b42c14a5ba950cb561ce7 (diff)
Add another pywebdav work-around, fix up one work-around
Add a work-around for compatibility with HTTP/1.0 clients that don't have persistent connections support and expect the connection to be closed by the server. Fix up the work-around for the <allprops/> response with depth!=0. It was returning 404 for properties that are on _some_ resource, but not the current one. Make the work-arounds conditional, so we can deactivate them for upstream versions that have the bugs fixed already. Otherwise we risk breaking with future upstream versions that change implementation details.
Diffstat (limited to 'journal2webdav')
-rwxr-xr-xjournal2webdav298
1 files changed, 200 insertions, 98 deletions
diff --git a/journal2webdav b/journal2webdav
index f8b7d88..cf29f28 100755
--- a/journal2webdav
+++ b/journal2webdav
@@ -16,6 +16,8 @@
from BaseHTTPServer import HTTPServer
from SocketServer import ThreadingMixIn
import cgi
+import cStringIO as StringIO
+import gzip
import logging
import optparse
import os
@@ -27,19 +29,26 @@ from urlparse import urljoin, urlparse
try:
import pywebdav.lib as DAV
except ImportError:
- # old names
+ # pywebdav < 0.9.8
import DAV
from DAV import propfind
from DAV.constants import COLLECTION, OBJECT
from DAV.iface import dav_interface
from DAV.errors import DAV_Error, DAV_NotFound, DAV_Requested_Range_Not_Satisfiable
from DAVServer.fileauth import DAVAuthHandler
+ _PYWEBDAV_BUGS = set(['PROPFIND_NS', 'ALLPROP_RECURSE'])
else:
from pywebdav.lib import propfind
from pywebdav.lib.constants import COLLECTION, OBJECT
from pywebdav.lib.iface import dav_interface
from pywebdav.lib.errors import DAV_Error, DAV_NotFound, DAV_Requested_Range_Not_Satisfiable
from pywebdav.lib.WebDAVServer import DAVRequestHandler as DAVAuthHandler
+ _PYWEBDAV_BUGS = set(['ALLPROP_RECURSE', 'HTTP10_KEEPALIVE'])
+
+if 'ALLPROP_RECURSE' in _PYWEBDAV_BUGS:
+ import xml.dom.minidom
+ domimpl = xml.dom.minidom.getDOMImplementation()
+
# from sugar.logger import trace
@@ -67,70 +76,92 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
class PROPFIND(propfind.PROPFIND):
- # pylint: disable=C0324,C0322
- def mk_propname_response(self,uri,propnames,doc):
- # copy of original, but with bug fix for multiple namespaces
- re=doc.createElement("D:response")
-
- # write href information
- uparts=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
-
- 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 create_allprop(self):
- """ return a list of all properties """
- # modified to recurse over children if applicable
- self.proplist={}
- rel_path_q = ['']
- if self._depth == 'infinity':
- max_depth = sys.maxint
- else:
- max_depth = int(self._depth)
-
- while rel_path_q:
- rel_path = rel_path_q.pop()
- if rel_path:
- uri = '%s/%s' % (self._uri, rel_path)
- else:
- uri = self._uri
-
- self.proplist.update(self._dataclass.get_propnames(uri))
-
- if rel_path.count('/') >= max_depth:
- continue
-
- if not self._dataclass.is_collection(uri):
- continue
-
- rel_path_q += [child_uri[len(self._uri) + 1:]
- for child_uri in self._dataclass.get_childs(uri)]
-
- self.namespaces=self.proplist.keys()
- return self.create_prop()
+ if 'PROPFIND_NS' in _PYWEBDAV_BUGS:
+ # pylint: disable=C0324,C0322
+ def mk_propname_response(self,uri,propnames,doc):
+ # copy of original, but with bug fix for multiple namespaces
+ re=doc.createElement("D:response")
+
+ # write href information
+ uparts=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
+
+ 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
+
+ if 'ALLPROP_RECURSE' in _PYWEBDAV_BUGS:
+ # work-around for Debian#710690
+
+ def create_allprop(self):
+ return self.create_prop(True)
+
+ def create_prop(self, allprop=False):
+ # 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 allprop:
+ self.proplist = self._dataclass.get_propnames(self._uri)
+ self.namespaces = self.proplist.keys()
+ 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 allprop:
+ self.proplist = self._dataclass.get_propnames(self._uri)
+ self.namespaces = self.proplist.keys()
+ 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):
+ if allprop:
+ self.proplist = self._dataclass.get_propnames(newuri)
+ self.namespaces = self.proplist.keys()
+ 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 allprop:
+ self.proplist = self._dataclass.get_propnames(uri)
+ self.namespaces = self.proplist.keys()
+ 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")
class JournalObjectResource(object):
@@ -429,40 +460,111 @@ def setupDummyConfig(**kw):
class RequestHandler(DAVAuthHandler):
- # pylint: disable=W0402,W0404,C0324,W0612
- def do_PROPFIND(self):
- from string import atoi
- # exact copy of original, just to override the PROPFIND class
-
- 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 = urljoin(self.get_baseuri(dc), self.path)
- uri = urllib.unquote(uri)
-
- pf = PROPFIND(uri, dc, self.headers.get('Depth', 'infinity'), body)
+ if 'ALLPROP_RECURSE' in _PYWEBDAV_BUGS or 'PROPFIND_NS' in _PYWEBDAV_BUGS:
+ # pylint: disable=W0402,W0404,C0324,W0612
+ def do_PROPFIND(self):
+ from string import atoi
+ # exact copy of original, just to override the PROPFIND class
+
+ 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 'Content-Length' in self.headers:
+ l = self.headers['Content-Length']
+ body = self.rfile.read(atoi(l))
+
+ uri = urljoin(self.get_baseuri(dc), self.path)
+ uri = urllib.unquote(uri)
+
+ 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):
+ return self.send_status(ec)
+
+ # 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'):
+ 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">')
+ 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_if_http11(DATA, 207, 'Multi-Status',
+ 'Multiple responses')
+
+ if 'HTTP10_KEEPALIVE' in _PYWEBDAV_BUGS:
+ # work-around for Debian#710672
+
+ 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")
+
+ if self.request_version != 'HTTP/1.1':
+ headers.pop('Keep-Alive', None)
+ headers.pop('Connection', None)
+
+ self.send_response(code, message=msg)
+ if 'Connection' not in headers:
+ self.send_header("Connection", "close")
+ self.send_header("Accept-Ranges", "bytes")
+
+ 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:
+ buffer = StringIO.StringIO()
+ output = gzip.GzipFile(mode='wb', fileobj=buffer)
+ 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()
+ 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)
- try:
- DATA = '%s\n' % pf.createResponse()
- except DAV_Error, (ec,dd):
- return self.send_status(ec)
-
- # 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'):
- 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">')
- 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_if_http11(DATA, 207,'Multi-Status','Multiple responses')
+ self.end_headers()
+ if DATA:
+ if isinstance(DATA, str) or isinstance(DATA, unicode):
+ log.debug("Don't use iterator")
+ self.wfile.write(DATA)
+ else:
+ if self._config.DAV.getboolean('http_response_use_iterator'):
+ # Use iterator to reduce using memory
+ log.debug("Use iterator")
+ 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.wfile.write(DATA.read())
def log_message(self, format, *args):
# pylint: disable=W0622