From 039e868e5f36d02e032d919d2fb58d7ddf332a6e Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Fri, 7 Jan 2011 15:25:05 +0100 Subject: [PATCH 1/7] iPhone support, thank you Andrew --- radicale/__init__.py | 2 +- radicale/xmlutils.py | 80 +++++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index 64b5833..b842b6a 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -216,7 +216,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): xml_request = self.rfile.read(int(self.headers["Content-Length"])) self._answer = xmlutils.propfind( self.path, xml_request, self._calendar, - self.headers.get("depth", "infinity"), self) + self.headers.get("depth", "infinity")) self.send_response(client.MULTI_STATUS) self.send_header("DAV", "1, calendar-access") diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 35071a3..11cb175 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -79,7 +79,7 @@ def delete(path, calendar): return ET.tostring(multistatus, config.get("encoding", "request")) -def propfind(path, xml_request, calendar, depth, request): +def propfind(path, xml_request, calendar, depth): """Read and answer PROPFIND requests. Read rfc4918-9.1 for info. @@ -97,10 +97,8 @@ def propfind(path, xml_request, calendar, depth, request): if depth == "0": items = [calendar] - elif depth == "1": - items = [calendar] + calendar.events + calendar.todos else: - # depth is infinity or not specified + # depth is 1, infinity or not specified # we limit ourselves to depth == 1 items = [calendar] + calendar.events + calendar.todos @@ -111,7 +109,7 @@ def propfind(path, xml_request, calendar, depth, request): multistatus.append(response) href = ET.Element(_tag("D", "href")) - href.text = path if is_calendar else "%s/%s" % (path, item.name) + href.text = path if is_calendar else path + item.name response.append(href) propstat = ET.Element(_tag("D", "propstat")) @@ -122,42 +120,45 @@ def propfind(path, xml_request, calendar, depth, request): for tag in props: element = ET.Element(tag) - if tag == _tag("D", "resourcetype"): - if is_calendar: - tag = ET.Element(_tag("C", "calendar")) - element.append(tag) - tag = ET.Element(_tag("D", "collection")) - element.append(tag) - else: - tag = ET.Element(_tag("C", "comp")) - tag.set("name", element.tag) - element.append(tag) + if tag == _tag("D", "resourcetype") and is_calendar: + tag = ET.Element(_tag("C", "calendar")) + element.append(tag) + tag = ET.Element(_tag("D", "collection")) + element.append(tag) elif tag == _tag("D", "owner"): element.text = calendar.owner elif tag == _tag("D", "getcontenttype"): element.text = "text/calendar" + elif tag == _tag("CS", "getctag") and is_calendar: + element.text = item.etag elif tag == _tag("D", "getetag"): element.text = item.etag - elif tag == _tag("D", "displayname"): + elif tag == _tag("D", "displayname") and is_calendar: element.text = calendar.name - elif tag == _tag("D", "supported-report-set"): - supported_report = ET.Element(_tag("D", "supported-report")) - report_set = ET.Element(_tag("D", "report")) - report_set.append(ET.Element(_tag("C", "calendar-multiget"))) - supported_report.append(report_set) - element.append(supported_report) elif tag == _tag("D", "principal-URL"): # TODO: use a real principal URL, read rfc3744-4.2 for info - element.text = "%s://%s%s" % ( - request.server.PROTOCOL, request.headers["Host"], - request.path) + tag = ET.Element(_tag("D", "href")) + tag.text = path + element.append(tag) + elif tag == _tag("D", "principal-collection-set"): + tag = ET.Element(_tag("D", "href")) + tag.text = path + element.append(tag) elif tag == _tag("C", "calendar-home-set"): tag = ET.Element(_tag("D", "href")) - tag.text = "%s://%s%s" % ( - request.server.PROTOCOL, request.headers["Host"], - request.path) + tag.text = path element.append(tag) - + elif tag == _tag("C", "supported-calendar-component-set"): + comp = ET.Element(_tag("C", "comp")) + comp.set("name", "VTODO") + element.append(comp) + comp = ET.Element(_tag("C", "comp")) + comp.set("name", "VEVENT") + element.append(comp) + elif tag == _tag("D", "current-user-privilege-set"): + privilege = ET.Element(_tag("D", "privilege")) + privilege.append(ET.Element(_tag("D", "all"))) + element.append(privilege) prop.append(element) status = ET.Element(_tag("D", "status")) @@ -227,19 +228,14 @@ def report(path, xml_request, calendar): prop = ET.Element(_tag("D", "prop")) propstat.append(prop) - if _tag("D", "getetag") in props: - element = ET.Element(_tag("D", "getetag")) - element.text = item.etag - prop.append(element) - - if _tag("C", "calendar-data") in props: - element = ET.Element(_tag("C", "calendar-data")) - if isinstance(item, ical.Event): - element.text = ical.serialize( - calendar.headers, calendar.timezones + [item]) - elif isinstance(item, ical.Todo): - element.text = ical.serialize( - calendar.headers, calendar.timezones + [item]) + for tag in props: + element = ET.Element(tag) + if tag == _tag("D", "getetag"): + element.text = item.etag + elif tag == _tag("C", "calendar-data"): + if isinstance(item, (ical.Event, ical.Todo)): + element.text = ical.serialize( + calendar.headers, calendar.timezones + [item]) prop.append(element) status = ET.Element(_tag("D", "status")) From d11d4cc8c19a9cd41b950ef86fb87862fa1f9cbe Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sun, 9 Jan 2011 17:41:42 +0100 Subject: [PATCH 2/7] Pylint, remove fixed todos --- radicale/__init__.py | 1 + radicale/acl/htpasswd.py | 2 +- radicale/config.py | 2 -- radicale/ical.py | 1 - radicale/xmlutils.py | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index b842b6a..ebcfe16 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -90,6 +90,7 @@ class HTTPServer(server.HTTPServer): class HTTPSServer(HTTPServer): """HTTPS server.""" PROTOCOL = "https" + def __init__(self, address, handler): """Create server by wrapping HTTP socket in an SSL socket.""" # Fails with Python 2.5, import if needed diff --git a/radicale/acl/htpasswd.py b/radicale/acl/htpasswd.py index bb2f26b..f641351 100644 --- a/radicale/acl/htpasswd.py +++ b/radicale/acl/htpasswd.py @@ -49,7 +49,7 @@ def _sha1(hash_value, password): """Check if ``hash_value`` and ``password`` match using sha1 method.""" hash_value = hash_value.replace("{SHA}", "").encode("ascii") password = password.encode(config.get("encoding", "stock")) - sha1 = hashlib.sha1() + sha1 = hashlib.sha1() # pylint: disable=E1101 sha1.update(password) return sha1.digest() == base64.b64decode(hash_value) diff --git a/radicale/config.py b/radicale/config.py index 1cf9dd6..8c37280 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -25,8 +25,6 @@ Give a configparser-like interface to read and write configuration. """ -# TODO: Use abstract filenames for other platforms - import os import sys # Manage Python2/3 different modules diff --git a/radicale/ical.py b/radicale/ical.py index cff038f..b156741 100644 --- a/radicale/ical.py +++ b/radicale/ical.py @@ -135,7 +135,6 @@ class Calendar(object): def __init__(self, path): """Initialize the calendar with ``cal`` and ``user`` parameters.""" - # TODO: Use properties from the calendar configuration self.encoding = "utf-8" self.owner = path.split("/")[0] self.path = os.path.join(FOLDER, path.replace("/", os.path.sep)) diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 11cb175..d300e0e 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -150,7 +150,7 @@ def propfind(path, xml_request, calendar, depth): element.append(tag) elif tag == _tag("C", "supported-calendar-component-set"): comp = ET.Element(_tag("C", "comp")) - comp.set("name", "VTODO") + comp.set("name", "VTODO") # pylint: disable=W0511 element.append(comp) comp = ET.Element(_tag("C", "comp")) comp.set("name", "VEVENT") From 04938243a8f855448e64eb38d958f2343bf0f91d Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sun, 9 Jan 2011 17:46:22 +0100 Subject: [PATCH 3/7] Update copyright year --- radicale.py | 2 +- radicale/__init__.py | 2 +- radicale/acl/__init__.py | 2 +- radicale/acl/fake.py | 2 +- radicale/acl/htpasswd.py | 2 +- radicale/config.py | 2 +- radicale/ical.py | 2 +- radicale/xmlutils.py | 2 +- setup.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/radicale.py b/radicale.py index e7d490c..704bcd6 100755 --- a/radicale.py +++ b/radicale.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/__init__.py b/radicale/__init__.py index ebcfe16..c23a98c 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/acl/__init__.py b/radicale/acl/__init__.py index 349b1dd..d33e878 100644 --- a/radicale/acl/__init__.py +++ b/radicale/acl/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/acl/fake.py b/radicale/acl/fake.py index 145f3d1..9ddd224 100644 --- a/radicale/acl/fake.py +++ b/radicale/acl/fake.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/acl/htpasswd.py b/radicale/acl/htpasswd.py index f641351..250ec90 100644 --- a/radicale/acl/htpasswd.py +++ b/radicale/acl/htpasswd.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/config.py b/radicale/config.py index 8c37280..63929ae 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/ical.py b/radicale/ical.py index b156741..f8d51e4 100644 --- a/radicale/ical.py +++ b/radicale/ical.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index d300e0e..ed986f1 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2008-2010 Guillaume Ayoub +# Copyright © 2008-2011 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel # Copyright © 2008 Pascal Halter # diff --git a/setup.py b/setup.py index 70553b1..2d9b09c 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # # This file is part of Radicale Server - Calendar Server -# Copyright © 2009-2010 Guillaume Ayoub +# Copyright © 2009-2011 Guillaume Ayoub # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 9874129bbba7a7a3c7500ed91073e7241d1305ea Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Thu, 27 Jan 2011 00:04:30 +0100 Subject: [PATCH 4/7] Add simple support for calendar-user-address-set (references #181) --- radicale/xmlutils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index ed986f1..af7e665 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -140,11 +140,10 @@ def propfind(path, xml_request, calendar, depth): tag = ET.Element(_tag("D", "href")) tag.text = path element.append(tag) - elif tag == _tag("D", "principal-collection-set"): - tag = ET.Element(_tag("D", "href")) - tag.text = path - element.append(tag) - elif tag == _tag("C", "calendar-home-set"): + elif tag in ( + _tag("D", "principal-collection-set"), + _tag("C", "calendar-user-address-set"), + _tag("C", "calendar-home-set")): tag = ET.Element(_tag("D", "href")) tag.text = path element.append(tag) From c38c79a738dadcf4692ab948c7c9f3f60e4987cc Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Fri, 28 Jan 2011 01:52:28 +0100 Subject: [PATCH 5/7] Fix typo --- radicale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale.py b/radicale.py index 704bcd6..03f6a14 100755 --- a/radicale.py +++ b/radicale.py @@ -28,7 +28,7 @@ """ Radicale Server entry point. -Launch the Radicale Serve according to configuration and command-line +Launch the Radicale Server according to configuration and command-line arguments. """ From 9b535ba4b2707c324735d9c6bc0894ca2a41e159 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Tue, 1 Feb 2011 17:01:30 +0100 Subject: [PATCH 6/7] Add support for MKCALENDAR requests --- radicale/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index c23a98c..ad94318 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -204,11 +204,18 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): # No item or ETag precondition not verified, do not delete item self.send_response(client.PRECONDITION_FAILED) + @check_rights + def do_MKCALENDAR(self): + """Manage MKCALENDAR request.""" + self.send_response(client.CREATED) + self.end_headers() + def do_OPTIONS(self): """Manage OPTIONS request.""" self.send_response(client.OK) self.send_header( - "Allow", "DELETE, HEAD, GET, OPTIONS, PROPFIND, PUT, REPORT") + "Allow", "DELETE, HEAD, GET, MKCALENDAR, " + "OPTIONS, PROPFIND, PUT, REPORT") self.send_header("DAV", "1, calendar-access") self.end_headers() From 2e1b51e3c17f622617af9b98d772dee7212d5484 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sat, 12 Feb 2011 12:05:02 +0100 Subject: [PATCH 7/7] Softly ignore /user/ PROPFIND and REPORT requests (references #181) --- radicale/__init__.py | 5 +++++ radicale/xmlutils.py | 26 ++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index ad94318..905257b 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -55,6 +55,11 @@ def _check(request, function): """Check if user has sufficient rights for performing ``request``.""" # ``_check`` decorator can access ``request`` protected functions # pylint: disable=W0212 + + # If we have no calendar, don't check rights + if not request._calendar: + return function(request) + authorization = request.headers.get("Authorization", None) if authorization: challenge = authorization.lstrip("Basic").strip().encode("ascii") diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index af7e665..f811f7f 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -95,12 +95,15 @@ def propfind(path, xml_request, calendar, depth): # Writing answer multistatus = ET.Element(_tag("D", "multistatus")) - if depth == "0": - items = [calendar] + if calendar: + if depth == "0": + items = [calendar] + else: + # depth is 1, infinity or not specified + # we limit ourselves to depth == 1 + items = [calendar] + calendar.events + calendar.todos else: - # depth is 1, infinity or not specified - # we limit ourselves to depth == 1 - items = [calendar] + calendar.events + calendar.todos + items = [] for item in items: is_calendar = isinstance(item, ical.Calendar) @@ -191,12 +194,15 @@ def report(path, xml_request, calendar): prop_list = prop_element.getchildren() props = [prop.tag for prop in prop_list] - if root.tag == _tag("C", "calendar-multiget"): - # Read rfc4791-7.9 for info - hreferences = set((href_element.text for href_element - in root.findall(_tag("D", "href")))) + if calendar: + if root.tag == _tag("C", "calendar-multiget"): + # Read rfc4791-7.9 for info + hreferences = set((href_element.text for href_element + in root.findall(_tag("D", "href")))) + else: + hreferences = (path,) else: - hreferences = (path,) + hreferences = () # Writing answer multistatus = ET.Element(_tag("D", "multistatus"))