heechee /

from lxml.etree import fromstring, tostring, Element, SubElement
from werkzeug import Request, Response
from werkzeug.exceptions import HTTPException, MethodNotAllowed, BadRequest

from svndiff import make_cheap_diff
from repo import Repository, File, Directory

DAV_NS = "{DAV:}"
SVN_NS = "{svn:}"
SVN_DAV_NS = "{}"

class Application(object):
    verbs = ["OPTIONS", "PROPFIND", "REPORT"]
    def __init__(self, repo):
        self.repo = repo
    def __call__(self, request):
            if hasattr(self, request.method):
                return getattr(self, request.method)(request)
                raise MethodNotAllowed(self.verbs)
        except HTTPException, e:
            return e
    def OPTIONS(self, request):
        Look, Ma, we support ALL THESE methods. Really.
        return Response(headers = {
            "Allow": ",".join(self.verbs),
    def PROPFIND(self, request):
        Responds to SVN's PROPFIND requests, which usually detail finding out
        various things about the repo.
        # Do they want the properties of the overall repo?
        data =
        tree = fromstring(data)
        props = [x.tag.split("}")[-1] for x in tree.find("{DAV:}prop").getchildren()]
        if request.path == "/":
            response = self._propstat_response(request, {
                DAV_NS+"version-controlled-configuration": self._href("/!svn/vcc/default"),
                DAV_NS+"resourcetype": Element(DAV_NS+"collection"),
                SVN_DAV_NS+"baseline-relative-path": None,
                SVN_DAV_NS+"repository-uuid": self.repo.uuid,
        elif request.path == "/!svn/vcc/default":
            answers = {}
            if "checked-in" in props:
                answers[DAV_NS+"checked-in"] = self._href("/!svn/bln/1/")
            if "baseline-collection" in props:
                answers[DAV_NS+"baseline-collection"] = self._href("/!svn/bc/1/")
            if "version-name" in props:
                answers[DAV_NS+"version-name"] = "1"
            response = self._propstat_response(request, answers)
        elif request.path == "/!svn/bln/1":
            response = self._propstat_response(request, {
                DAV_NS+"baseline-collection": self._href("/!svn/bc/1/"),
                DAV_NS+"version-name": '1',
        elif request.path == "/!svn/bc/1":
            response = self._propstat_response(request, {
                DAV_NS+"version-controlled-configuration": self._href("/!svn/vcc/default"),
                DAV_NS+"resourcetype": Element(DAV_NS+"collection"),
                SVN_NS+"baseline-relative-path": None,
                SVN_NS+"repository-uuid": self.repo.uuid,
        # We don't recognise it! How unlikely.
            raise BadRequest()
        return Response(response, status = 207)
    def REPORT(self, request):
        "i.e. SVN-wants-a-massive-diff time."
        up_rep = Element(SVN_NS+"update-report")
        up_rep.attrib['send-all'] = "true"
        SubElement(up_rep, SVN_NS+"target-revision", rev="1")
        # The queue is a list of (parent, item) tuples.
        queue = [(up_rep, self.repo.get_rev(1))]
        while queue:
            parent, item = queue.pop()
            if isinstance(item, Directory):
                # Make the directory's entry
                dir = SubElement(parent, SVN_NS+"open-directory", rev="1")
                dir_ch_in = SubElement(dir, DAV_NS+"checked-in")
                # Add its children
                for child in item.get_children():
                    queue.append((dir, child))
            elif isinstance(item, File):
                # Make the file object
                file = SubElement(parent, SVN_NS+"add-file",
                dir_ch_in = SubElement(file, DAV_NS+"checked-in")
                rev = SubElement(file, SVN_NS+"set-prop", name="svn:entry:committed-rev")
                rev.text = "1"
                txdelta = SubElement(file, SVN_NS+"txdelta")
                txdelta.text = make_cheap_diff(item.contents).encode("base64")
        return Response(tostring(up_rep))
    def _href(self, target):
        "Single-call making of DAV:href tags."
        elem = Element(DAV_NS+"href")
        elem.text = target
        return elem
    def _propstat_response(self, request, props, status="HTTP/1.1 200 OK"):
        "Creates a multi-status response."
        multi = Element(DAV_NS + "multistatus")
        response = SubElement(multi, DAV_NS + "response")
        # Path of this request
        href = SubElement(response, DAV_NS + "href")
        href.text = request.path
        # It's gonna be a propstat
        propstat = SubElement(response, DAV_NS + "propstat")
        prop = SubElement(propstat, DAV_NS + "prop")
        # Add in the props we're told about
        for tag, value in props.items():
            elem = SubElement(prop, tag)
            # Is it a string value?
            if isinstance(value, basestring):
                elem.text = value
            # Is it None? (do nothing)
            elif value is None:
            # Then it's hopefully an element.
        # Finally, add in the status
        SubElement(propstat, DAV_NS + "status").text = status
        return tostring(multi)

if __name__ == "__main__":
    from werkzeug import run_simple
    run_simple('localhost', 8080, Application(Repository()))