From f479b4ba674e464407dd63fa1955e0d21534ab11 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Mon, 12 Apr 2010 14:43:40 +0200 Subject: [PATCH] Check ETag before modifying or adding items. --- radicale/__init__.py | 34 +++++++++++++++++++++------------- radicale/ical.py | 6 ++++++ radicale/xmlutils.py | 8 ++++---- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index e16708f..89bc468 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -153,14 +153,18 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @check_rights def do_DELETE(self): """Manage DELETE request.""" - # TODO: Check etag before deleting - etag = self.headers.get("If-Match", None) - answer = xmlutils.delete(self.path, self._calendar) + item = self._calendar.get_item(xmlutils.name_from_path(self.path)) + if item and self.headers.get("If-Match", item.etag) == item.etag: + # No ETag precondition or precondition verified, delete item + answer = xmlutils.delete(self.path, self._calendar) - self.send_response(client.NO_CONTENT) - self.send_header("Content-Length", len(answer)) - self.end_headers() - self.wfile.write(answer) + self.send_response(client.NO_CONTENT) + self.send_header("Content-Length", len(answer)) + self.end_headers() + self.wfile.write(answer) + else: + # No item or ETag precondition not verified, do not delete item + self.send_response(client.PRECONDITION_FAILED) def do_OPTIONS(self): """Manage OPTIONS request.""" @@ -183,13 +187,17 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @check_rights def do_PUT(self): """Manage PUT request.""" - # TODO: Check etag before putting - etag = self.headers.get("If-Match", None) - ical_request = self._decode( - self.rfile.read(int(self.headers["Content-Length"]))) - xmlutils.put(self.path, ical_request, self._calendar) + item = self._calendar.get_item(xmlutils.name_from_path(self.path)) + if not item or self.headers.get("If-Match", item.etag) == item.etag: + # No item, no ETag precondition or precondition verified, put item + ical_request = self._decode( + self.rfile.read(int(self.headers["Content-Length"]))) + xmlutils.put(self.path, ical_request, self._calendar) - self.send_response(client.CREATED) + self.send_response(client.CREATED) + else: + # ETag precondition not verified, do not put item + self.send_response(client.PRECONDITION_FAILED) @check_rights def do_REPORT(self): diff --git a/radicale/ical.py b/radicale/ical.py index 6410119..d9a899e 100644 --- a/radicale/ical.py +++ b/radicale/ical.py @@ -174,6 +174,12 @@ class Calendar(object): return items + def get_item(self, name): + """Get calendar item called ``name``.""" + for item in self.items: + if item.name == name: + return item + def append(self, name, text): """Append items from ``text`` to calendar. diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 3d7bf3b..a4c32f3 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -50,7 +50,7 @@ def _response(code): return "HTTP/1.1 %i %s" % (code, client.responses[code]) -def _name_from_path(path): +def name_from_path(path): """Return Radicale item name from ``path``.""" return path.split("/")[-1] @@ -62,7 +62,7 @@ def delete(path, calendar): """ # Reading request - calendar.remove(_name_from_path(path)) + calendar.remove(name_from_path(path)) # Writing answer multistatus = ET.Element(_tag("D", "multistatus")) @@ -142,7 +142,7 @@ def propfind(path, xml_request, calendar): def put(path, ical_request, calendar): """Read PUT requests.""" - name = _name_from_path(path) + name = name_from_path(path) if name in (item.name for item in calendar.items): # PUT is modifying an existing item calendar.replace(name, ical_request) @@ -176,7 +176,7 @@ def report(path, xml_request, calendar): for hreference in hreferences: # Check if the reference is an item or a calendar - name = hreference.split("/")[-1] + name = name_from_path(hreference) if name: # Reference is an item path = "/".join(hreference.split("/")[:-1]) + "/"