Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
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
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.
-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