diff --git a/radicale.py b/radicale.py index e7d490c..03f6a14 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 # @@ -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. """ diff --git a/radicale/__init__.py b/radicale/__init__.py index 360c79b..99fcfe0 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 # @@ -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") @@ -90,6 +95,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 @@ -203,11 +209,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() @@ -216,7 +229,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/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 bb2f26b..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 # @@ -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 509b5f9..5f2f8fe 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 # @@ -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..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 # @@ -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 a4061dd..40d7f1e 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 # @@ -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. @@ -95,14 +95,15 @@ def propfind(path, xml_request, calendar, depth, request): # Writing answer multistatus = ET.Element(_tag("D", "multistatus")) - if depth == "0": - items = [calendar] - elif depth == "1": - items = [calendar] + calendar.events + calendar.todos + 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 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) @@ -111,7 +112,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 +123,44 @@ 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) - 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 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) + elif tag == _tag("C", "supported-calendar-component-set"): + comp = ET.Element(_tag("C", "comp")) + comp.set("name", "VTODO") # pylint: disable=W0511 + 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")) @@ -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")) @@ -227,19 +233,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")) 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