diff --git a/radicale.py b/radicale.py index 96d52e0..e9afa62 100755 --- a/radicale.py +++ b/radicale.py @@ -30,13 +30,10 @@ Radicale Server entry point. Launch the Radicale Serve according to the configuration. """ -import sys -import BaseHTTPServer - import radicale if radicale.config.get("server", "type") == "http": - server = BaseHTTPServer.HTTPServer( + server = radicale.server.HTTPServer( ("", radicale.config.getint("server", "port")), radicale.CalendarHandler) server.serve_forever() diff --git a/radicale/__init__.py b/radicale/__init__.py index 53daef7..d8618b5 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -21,25 +21,21 @@ # TODO: Manage errors (see xmlutils) import posixpath -import httplib -import BaseHTTPServer +try: + from http import client, server +except ImportError: + import httplib as client + import BaseHTTPServer as server -import config -import support -import acl -import xmlutils -import calendar +from radicale import config, support, xmlutils -_users = acl.users() -_calendars = support.calendars() - -class CalendarHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class CalendarHandler(server.BaseHTTPRequestHandler): """HTTP requests handler for calendars.""" def _parse_path(self): path = self.path.strip("/").split("/") if len(path) >= 2: cal = "%s/%s" % (path[0], path[1]) - self.calendar = calendar.Calendar(_users[0], cal) + self.calendar = calendar.Calendar("radicale", cal) def do_DELETE(self): """Manage DELETE ``request``.""" @@ -47,14 +43,14 @@ class CalendarHandler(BaseHTTPServer.BaseHTTPRequestHandler): obj = self.headers.get("if-match", None) answer = xmlutils.delete(obj, self.calendar, self.path) - self.send_response(httplib.NO_CONTENT) + self.send_response(client.NO_CONTENT) self.send_header("Content-Length", len(answer)) self.end_headers() self.wfile.write(answer) def do_OPTIONS(self): """Manage OPTIONS ``request``.""" - self.send_response(httplib.OK) + self.send_response(client.OK) self.send_header("Allow", "DELETE, OPTIONS, PROPFIND, PUT, REPORT") self.send_header("DAV", "1, calendar-access") self.end_headers() @@ -65,7 +61,7 @@ class CalendarHandler(BaseHTTPServer.BaseHTTPRequestHandler): xml_request = self.rfile.read(int(self.headers["Content-Length"])) answer = xmlutils.propfind(xml_request, self.calendar, self.path) - self.send_response(httplib.MULTI_STATUS) + self.send_response(client.MULTI_STATUS) self.send_header("DAV", "1, calendar-access") self.send_header("Content-Length", len(answer)) self.end_headers() @@ -84,7 +80,7 @@ class CalendarHandler(BaseHTTPServer.BaseHTTPRequestHandler): obj = self.headers.get("if-match", None) xmlutils.put(ical_request, self.calendar, self.path, obj) - self.send_response(httplib.CREATED) + self.send_response(client.CREATED) def do_REPORT(self): """Manage REPORT ``request``.""" @@ -92,7 +88,7 @@ class CalendarHandler(BaseHTTPServer.BaseHTTPRequestHandler): xml_request = self.rfile.read(int(self.headers["Content-Length"])) answer = xmlutils.report(xml_request, self.calendar, self.path) - self.send_response(httplib.MULTI_STATUS) + self.send_response(client.MULTI_STATUS) self.send_header("Content-Length", len(answer)) self.end_headers() self.wfile.write(answer) diff --git a/radicale/acl/__init__.py b/radicale/acl/__init__.py index 7d5463d..26d69f6 100644 --- a/radicale/acl/__init__.py +++ b/radicale/acl/__init__.py @@ -25,7 +25,7 @@ This module loads a list of users with access rights, according to the acl configuration. """ -from .. import config +from radicale import config _acl = __import__(config.get("acl", "type"), locals(), globals()) diff --git a/radicale/acl/fake.py b/radicale/acl/fake.py index 5db1282..f2d5278 100644 --- a/radicale/acl/fake.py +++ b/radicale/acl/fake.py @@ -24,7 +24,7 @@ Fake ACL. Just load the default user set in configuration, with no rights management. """ -from .. import config +from radicale import config def users(): """Get the list of all users.""" diff --git a/radicale/acl/htpasswd.py b/radicale/acl/htpasswd.py index d723994..60b1ff6 100644 --- a/radicale/acl/htpasswd.py +++ b/radicale/acl/htpasswd.py @@ -26,7 +26,7 @@ Load the list of users according to the htpasswd configuration. # TODO: Manage rights -from .. import config +from radicale import config def users(): """Get the list of all users.""" diff --git a/radicale/calendar.py b/radicale/calendar.py index 2a3fe8e..06abba2 100644 --- a/radicale/calendar.py +++ b/radicale/calendar.py @@ -24,7 +24,7 @@ Radicale calendar classes. Define the main classes of a calendar as seen from the server. """ -import support +from radicale import support hash_tag = lambda vcalendar: str(hash(vcalendar)) @@ -33,8 +33,9 @@ class Calendar(object): def __init__(self, user, cal): """Initialize the calendar with ``cal`` and ``user`` parameters.""" # TODO: Use properties from the calendar configuration + self.support = support.load() self.encoding = "utf-8" - self.owner = "lize" + self.owner = "radicale" self.user = user self.cal = cal self.version = "2.0" @@ -43,22 +44,22 @@ class Calendar(object): def append(self, vcalendar): """Append vcalendar to the calendar.""" self.ctag = hash_tag(self.vcalendar()) - support.append(self.cal, vcalendar) + self.support.append(self.cal, vcalendar) def remove(self, uid): """Remove object named ``uid`` from the calendar.""" self.ctag = hash_tag(self.vcalendar()) - support.remove(self.cal, uid) + self.support.remove(self.cal, uid) def replace(self, uid, vcalendar): """Replace objet named ``uid`` by ``vcalendar`` in the calendar.""" self.ctag = hash_tag(self.vcalendar()) - support.remove(self.cal, uid) - support.append(self.cal, vcalendar) + self.support.remove(self.cal, uid) + self.support.append(self.cal, vcalendar) def vcalendar(self): """Return unicode calendar from the calendar.""" - return unicode(support.read(self.cal), self.encoding) + return self.support.read(self.cal) def etag(self): """Return etag from calendar.""" diff --git a/radicale/config.py b/radicale/config.py index 08a2da7..53e89f1 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -24,7 +24,10 @@ Radicale configuration module. Give a configparser-like interface to read and write configuration. """ -from ConfigParser import RawConfigParser as ConfigParser +try: + from configparser import RawConfigParser as ConfigParser +except ImportError: + from ConfigParser import RawConfigParser as ConfigParser _config = ConfigParser() get = _config.get @@ -68,9 +71,9 @@ _initial = { }, } -for section, values in _initial.iteritems(): +for section, values in _initial.items(): _config.add_section(section) - for key, value in values.iteritems(): + for key, value in values.items(): _config.set(section, key, value) # TODO: Use abstract filename for other platforms diff --git a/radicale/ical.py b/radicale/ical.py index 9227eeb..ac02637 100644 --- a/radicale/ical.py +++ b/radicale/ical.py @@ -24,7 +24,7 @@ iCal parsing functions. # TODO: Manage filters (see xmlutils) -import calendar +from radicale import calendar def write_calendar(headers=[ calendar.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"), @@ -32,14 +32,14 @@ def write_calendar(headers=[ timezones=[], todos=[], events=[]): """Create calendar from ``headers``, ``timezones``, ``todos``, ``events``.""" # TODO: Manage encoding and EOL - cal = u"\n".join(( - u"BEGIN:VCALENDAR", - u"\n".join([header.text for header in headers]), - u"\n".join([timezone.text for timezone in timezones]), - u"\n".join([todo.text for todo in todos]), - u"\n".join([event.text for event in events]), - u"END:VCALENDAR")) - return u"\n".join([line for line in cal.splitlines() if line]) + cal = "\n".join(( + "BEGIN:VCALENDAR", + "\n".join([header.text for header in headers]), + "\n".join([timezone.text for timezone in timezones]), + "\n".join([todo.text for todo in todos]), + "\n".join([event.text for event in events]), + "END:VCALENDAR")) + return "\n".join([line for line in cal.splitlines() if line]) def headers(vcalendar): """Find Headers items in ``vcalendar``.""" diff --git a/radicale/support/__init__.py b/radicale/support/__init__.py index 169496e..9bd3ea9 100644 --- a/radicale/support/__init__.py +++ b/radicale/support/__init__.py @@ -22,12 +22,9 @@ Calendar storage support configuration. """ -from .. import config +from radicale import config -_support = __import__(config.get("support", "type"), locals(), globals()) - -append = _support.append -calendars =_support.calendars -mkcalendar =_support.mkcalendar -read = _support.read -remove = _support.remove +def load(): + module = __import__("radicale.support", globals(), locals(), + [config.get("support", "type")]) + return getattr(module, config.get("support", "type")) diff --git a/radicale/support/plain.py b/radicale/support/plain.py index bf59957..f22db49 100644 --- a/radicale/support/plain.py +++ b/radicale/support/plain.py @@ -24,12 +24,15 @@ Plain text storage. import os import posixpath +import codecs -from .. import ical -from .. import config +from radicale import config, ical _folder = os.path.expanduser(config.get("support", "folder")) +def _open(path, mode="r"): + return codecs.open(path, mode, config.get("encoding", "stock")) + def calendars(): """List available calendars paths.""" calendars = [] @@ -45,17 +48,17 @@ def mkcalendar(name): user, cal = name.split(posixpath.sep) if not os.path.exists(os.path.join(_folder, user)): os.makedirs(os.path.join(_folder, user)) - fd = open(os.path.join(_folder, user, cal), "w") - fd.write(ical.write_calendar().encode(config.get("encoding", "stock"))) + fd = _open(os.path.join(_folder, user, cal), "w") + fd.write(ical.write_calendar()) def read(cal): """Read calendar ``cal``.""" path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) - return open(path).read() + return _open(path).read() def append(cal, vcalendar): """Append ``vcalendar`` to ``cal``.""" - old_calendar = unicode(read(cal), config.get("encoding", "stock")) + old_calendar = read(cal) old_tzs = [tz.tzid for tz in ical.timezones(old_calendar)] path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) @@ -69,29 +72,29 @@ def append(cal, vcalendar): for tz in ical.timezones(vcalendar): if tz.tzid not in old_tzs: - # TODO: Manage position, encoding and EOL - fd = open(path) + # TODO: Manage position and EOL + fd = _open(path) lines = [line for line in fd.readlines() if line] fd.close() for i,line in enumerate(tz.text.splitlines()): - lines.insert(2+i, line.encode(config.get("encoding", "stock"))+"\n") + lines.insert(2 + i, line + "\n") - fd = open(path, "w") + fd = _open(path, "w") fd.writelines(lines) fd.close() for obj in objects: if obj.etag() not in old_objects: - # TODO: Manage position, encoding and EOL - fd = open(path) + # TODO: Manage position and EOL + fd = _open(path) lines = [line for line in fd.readlines() if line] fd.close() for line in obj.text.splitlines(): - lines.insert(-1, line.encode(config.get("encoding", "stock"))+"\n") + lines.insert(-1, line + "\n") - fd = open(path, "w") + fd = _open(path, "w") fd.writelines(lines) fd.close() @@ -99,15 +102,15 @@ def remove(cal, etag): """Remove object named ``etag`` from ``cal``.""" path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) - cal = unicode(read(cal), config.get("encoding", "stock")) + cal = read(cal) headers = ical.headers(cal) timezones = ical.timezones(cal) todos = [todo for todo in ical.todos(cal) if todo.etag() != etag] events = [event for event in ical.events(cal) if event.etag() != etag] - fd = open(path, "w") - fd.write(ical.write_calendar(headers, timezones, todos, events).encode(config.get("encoding", "stock"))) + fd = _open(path, "w") + fd.write(ical.write_calendar(headers, timezones, todos, events)) fd.close() if config.get("support", "calendar"): diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index 6c912a5..5212443 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -31,8 +31,7 @@ in them for XML requests (all but PUT). import xml.etree.ElementTree as ET -import config -import ical +from radicale import config, ical # TODO: This is a well-known and accepted hack for ET to avoid ET from renaming # namespaces, which is accepted in XML norm but often not in XML