Huge code and copyright cleanup.

git-svn-id: http://svn.32rwr.info/radicale/trunk@9 74e4794c-479d-4a33-9dda-c6c359d70f12
This commit is contained in:
(no author) 2009-07-27 15:04:54 +00:00
parent e7a5ef8c5d
commit 81f7201399
11 changed files with 196 additions and 185 deletions

View File

@ -2,7 +2,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -23,6 +25,12 @@
# TODO: Magage command-line options # TODO: Magage command-line options
# TODO: Forget twisted? # TODO: Forget twisted?
"""
Radicale Server entry point.
Launch the Radicale Serve according to the configuration.
"""
import sys import sys
from twisted.web import server from twisted.web import server
from twisted.internet import reactor from twisted.internet import reactor
@ -31,18 +39,14 @@ from twisted.python import log
import radicale import radicale
class ServerContextFactory(object): class ServerContextFactory(object):
""" """SSL context factory."""
SSL context factory def get_context(self):
""" """Get SSL context for the HTTP server."""
def getContext(self):
"""
Get SSL context for the HTTP server
"""
from OpenSSL import SSL from OpenSSL import SSL
ctx = SSL.Context(SSL.SSLv23_METHOD) context = SSL.Context(SSL.SSLv23_METHOD)
ctx.use_certificate_file(radicale.config.get("server", "certificate")) context.use_certificate_file(radicale.config.get("server", "certificate"))
ctx.use_privatekey_file(radicale.config.get("server", "privatekey")) context.use_privatekey_file(radicale.config.get("server", "privatekey"))
return ctx return context
log.startLogging(sys.stdout) log.startLogging(sys.stdout)
#log.startLogging(open(radicale.config.get("server", "log"), "w")) #log.startLogging(open(radicale.config.get("server", "log"), "w"))

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,16 +19,15 @@
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
# TODO: Manage errors (see xmlutils) # TODO: Manage errors (see xmlutils)
# TODO: Forget twisted?
from twisted.web.resource import Resource from twisted.web.resource import Resource
from twisted.web import http from twisted.web import http
import posixpath import posixpath
import config import config
import support import support
import acl import acl
import xmlutils import xmlutils
import calendar import calendar
@ -34,112 +35,88 @@ _users = acl.users()
_calendars = support.calendars() _calendars = support.calendars()
class CalendarResource(Resource): class CalendarResource(Resource):
""" """Twisted resource for requests at calendar depth (/user/calendar)."""
Twisted resource for requests at calendar depth (/user/calendar) # Tell twisted this is a leaf for requests
"""
isLeaf = True isLeaf = True
def __init__(self, user, cal): def __init__(self, user, cal):
""" """Initialize by creating a calendar object.
Initialize resource creating a calendar object corresponding
to the stocked calendar named user/cal The calendar object corresponds to the stocked calendar named
``user``/``cal``.
""" """
Resource.__init__(self) Resource.__init__(self)
self.calendar = calendar.Calendar(user, cal) self.calendar = calendar.Calendar(user, cal)
def render_DELETE(self, request): def render_DELETE(self, request):
""" """Manage DELETE ``request``."""
Manage DELETE requests
"""
obj = request.getHeader("if-match") obj = request.getHeader("if-match")
answer = xmlutils.delete(obj, self.calendar, str(request.URLPath())) answer = xmlutils.delete(obj, self.calendar, str(request.URLPath()))
request.setResponseCode(http.NO_CONTENT) request.setResponseCode(http.NO_CONTENT)
return answer return answer
def render_OPTIONS(self, request): def render_OPTIONS(self, request):
""" """Manage OPTIONS ``request``."""
Manage OPTIONS requests
"""
request.setHeader("Allow", "DELETE, OPTIONS, PROPFIND, PUT, REPORT") request.setHeader("Allow", "DELETE, OPTIONS, PROPFIND, PUT, REPORT")
request.setHeader("DAV", "1, calendar-access") request.setHeader("DAV", "1, calendar-access")
request.setResponseCode(http.OK) request.setResponseCode(http.OK)
return "" return ""
def render_PROPFIND(self, request): def render_PROPFIND(self, request):
""" """Manage PROPFIND ``request``."""
Manage PROPFIND requests xml_request = request.content.read()
""" answer = xmlutils.propfind(xml_request, self.calendar, str(request.URLPath()))
xmlRequest = request.content.read()
answer = xmlutils.propfind(xmlRequest, self.calendar, str(request.URLPath()))
request.setResponseCode(http.MULTI_STATUS) request.setResponseCode(http.MULTI_STATUS)
return answer return answer
def render_PUT(self, request): def render_PUT(self, request):
""" """Manage PUT ``request``."""
Manage PUT requests
"""
# TODO: Improve charset detection # TODO: Improve charset detection
contentType = request.getHeader("content-type") contentType = request.getHeader("content-type")
if contentType and "charset=" in contentType: if contentType and "charset=" in contentType:
charset = contentType.split("charset=")[1].strip() charset = contentType.split("charset=")[1].strip()
else: else:
charset = config.get("encoding", "request") charset = config.get("encoding", "request")
icalRequest = unicode(request.content.read(), charset) ical_request = request.content.read().decode(charset)
obj = request.getHeader("if-match") obj = request.getHeader("if-match")
xmlutils.put(icalRequest, self.calendar, str(request.URLPath()), obj) xmlutils.put(ical_request, self.calendar, str(request.URLPath()), obj)
request.setResponseCode(http.CREATED) request.setResponseCode(http.CREATED)
return "" return ""
def render_REPORT(self, request): def render_REPORT(self, request):
""" """Manage REPORT ``request``."""
Manage REPORT requests xml_request = request.content.read()
""" answer = xmlutils.report(xml_request, self.calendar, str(request.URLPath()))
xmlRequest = request.content.read()
answer = xmlutils.report(xmlRequest, self.calendar, str(request.URLPath()))
request.setResponseCode(http.MULTI_STATUS) request.setResponseCode(http.MULTI_STATUS)
return answer return answer
class UserResource(Resource): class UserResource(Resource):
""" """Twisted resource for requests at user depth (/user)."""
Twisted resource for requests at user depth (/user)
"""
def __init__(self, user): def __init__(self, user):
""" """Initialize by connecting requests to ``user`` calendars resources."""
Initialize resource by connecting children requests to
the user calendars resources
"""
Resource.__init__(self) Resource.__init__(self)
for cal in _calendars: for cal in _calendars:
if cal.startswith("%s%s"%(user, posixpath.sep)): if cal.startswith("%s%s"%(user, posixpath.sep)):
calName = cal.split(posixpath.sep)[1] cal_name = cal.split(posixpath.sep)[1]
self.putChild(calName, CalendarResource(user, cal)) self.putChild(cal_name, CalendarResource(user, cal))
def getChild(self, cal, request): def getChild(self, cal, request):
""" """Get calendar resource if ``cal`` exists."""
Get calendar resource if user exists
"""
if cal in _calendars: if cal in _calendars:
return Resource.getChild(self, cal, request) return Resource.getChild(self, cal, request)
else: else:
return self return self
class HttpResource(Resource): class HttpResource(Resource):
""" """Twisted resource for requests at root depth (/)."""
Twisted resource for requests at root depth (/)
"""
def __init__(self): def __init__(self):
""" """Initialize by connecting requests to the users resources."""
Initialize resource by connecting children requests to
the users resources
"""
Resource.__init__(self) Resource.__init__(self)
for user in _users: for user in _users:
self.putChild(user, UserResource(user)) self.putChild(user, UserResource(user))
def getChild(self, user, request): def getChild(self, user, request):
""" """Get user resource if ``user`` exists."""
Get user resource if user exists
"""
if user in _users: if user in _users:
return Resource.getChild(self, user, request) return Resource.getChild(self, user, request)
else: else:

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,6 +18,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Users and rights management.
This module loads a list of users with access rights, according to the acl
configuration.
"""
from .. import config from .. import config
_acl = __import__(config.get("acl", "type"), locals(), globals()) _acl = __import__(config.get("acl", "type"), locals(), globals())

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,10 +18,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Fake ACL.
Just load the default user set in configuration, with no rights management.
"""
from .. import config from .. import config
def users(): def users():
""" """Get the list of all users."""
Get the List of all Users return [config.get("acl", "user")]
"""
return [config.get("acl", "defaultUser")]

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,10 +18,17 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Htpasswd ACL.
Load the list of users according to the htpasswd configuration.
"""
# TODO: Manage rights
from .. import config from .. import config
def users(): def users():
""" """Get the list of all users."""
Get the List of all Users return [line.split(":")[0] for line
""" in open(config.get("acl", "filename")).readlines()]
return [line.split(":")[0] for line in open(config.get("acl", "filename")).readlines()]

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,73 +18,68 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
# TODO: Manage inheritance for classes """
Radicale calendar classes.
from time import time Define the main classes of a calendar as seen from the server.
"""
import support import support
hash_tag = lambda vcalendar: str(hash(vcalendar))
class Calendar(object): class Calendar(object):
""" """Internal calendar class."""
Internal Calendar Class
"""
def __init__(self, user, cal): def __init__(self, user, cal):
# TODO: Use properties from the calendar """Initialize the calendar with ``cal`` and ``user`` parameters."""
# TODO: Use properties from the calendar configuration
self.encoding = "utf-8" self.encoding = "utf-8"
self.owner = "lize" self.owner = "lize"
self.user = user self.user = user
self.cal = cal self.cal = cal
self.version = "2.0" self.version = "2.0"
self.ctag = str(hash(self.vcalendar())) self.ctag = hash_tag(self.vcalendar())
def append(self, vcalendar): def append(self, vcalendar):
""" """Append vcalendar to the calendar."""
Append vcalendar self.ctag = hash_tag(self.vcalendar())
"""
self.ctag = str(hash(self.vcalendar()))
support.append(self.cal, vcalendar) support.append(self.cal, vcalendar)
def remove(self, uid): def remove(self, uid):
""" """Remove object named ``uid`` from the calendar."""
Remove Object Named uid self.ctag = hash_tag(self.vcalendar())
"""
self.ctag = str(hash(self.vcalendar()))
support.remove(self.cal, uid) support.remove(self.cal, uid)
def replace(self, uid, vcalendar): def replace(self, uid, vcalendar):
""" """Replace objet named ``uid`` by ``vcalendar`` in the calendar."""
Replace Objet Named uid by vcalendar self.ctag = hash_tag(self.vcalendar())
"""
self.ctag = str(hash(self.vcalendar()))
support.remove(self.cal, uid) support.remove(self.cal, uid)
support.append(self.cal, vcalendar) support.append(self.cal, vcalendar)
def vcalendar(self): def vcalendar(self):
"""Return unicode calendar from the calendar."""
return unicode(support.read(self.cal), self.encoding) return unicode(support.read(self.cal), self.encoding)
class Event(object): class Event(object):
""" """Internal event class."""
Internal Event Class
"""
# TODO: Fix the behaviour if no UID is given
def __init__(self, vcalendar): def __init__(self, vcalendar):
"""Initialize event from ``vcalendar``."""
self.text = vcalendar self.text = vcalendar
def etag(self): def etag(self):
return str(hash(self.text)) """Return etag from event."""
return hash_tag(self.text)
class Header(object): class Header(object):
""" """Internal header class."""
Internal Headers Class
"""
def __init__(self, vcalendar): def __init__(self, vcalendar):
"""Initialize header from ``vcalendar``."""
self.text = vcalendar self.text = vcalendar
class Timezone(object): class Timezone(object):
""" """Internal timezone class."""
Internal Timezone Class
"""
def __init__(self, vcalendar): def __init__(self, vcalendar):
"""Initialize timezone from ``vcalendar``."""
lines = vcalendar.splitlines() lines = vcalendar.splitlines()
for line in lines: for line in lines:
if line.startswith("TZID:"): if line.startswith("TZID:"):
@ -92,12 +89,11 @@ class Timezone(object):
self.text = vcalendar self.text = vcalendar
class Todo(object): class Todo(object):
""" """Internal todo class."""
Internal Todo Class
"""
# TODO: Fix the behaviour if no UID is given
def __init__(self, vcalendar): def __init__(self, vcalendar):
"""Initialize todo from ``vcalendar``."""
self.text = vcalendar self.text = vcalendar
def etag(self): def etag(self):
return str(hash(self.text)) """Return etag from todo."""
return hash_tag(self.text)

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,9 +18,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Radicale configuration module.
Give a configparser-like interface to read and write configuration.
"""
from ConfigParser import RawConfigParser as ConfigParser from ConfigParser import RawConfigParser as ConfigParser
# Default functions
_config = ConfigParser() _config = ConfigParser()
get = _config.get get = _config.get
set = _config.set set = _config.set
@ -28,7 +35,6 @@ getfloat = _config.getfloat
options = _config.options options = _config.options
items = _config.items items = _config.items
# Default config
_initial = { _initial = {
"server": { "server": {
"type": "http", "type": "http",
@ -53,20 +59,19 @@ _initial = {
"acl": { "acl": {
"type": "fake", "type": "fake",
"filename": "/etc/radicale/users", "filename": "/etc/radicale/users",
"defaultUser": "radicale", "user": "radicale",
}, },
"support": { "support": {
"type": "plain", "type": "plain",
"folder": "~/.config/radicale", "folder": "~/.config/radicale",
"defaultCalendar": "radicale/calendar", "calendar": "radicale/calendar",
}, },
} }
# Set the default config
for section, values in _initial.iteritems(): for section, values in _initial.iteritems():
_config.add_section(section) _config.add_section(section)
for key, value in values.iteritems(): for key, value in values.iteritems():
_config.set(section, key, value) _config.set(section, key, value)
# Set the user config # TODO: Use abstract filename for other platforms
_config.read("/etc/radicale/config") _config.read("/etc/radicale/config")

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,16 +18,19 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
iCal parsing functions.
"""
# TODO: Manage filters (see xmlutils) # TODO: Manage filters (see xmlutils)
import calendar import calendar
def writeCalendar(headers=[calendar.Header("PRODID:-//The Radicale Team//NONSGML Radicale Server//EN"), def write_calendar(headers=[
calendar.Header("VERSION:2.0")], calendar.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
calendar.Header("VERSION:2.0")],
timezones=[], todos=[], events=[]): timezones=[], todos=[], events=[]):
""" """Create calendar from ``headers``, ``timezones``, ``todos``, ``events``."""
Create calendar from headers, timezones, todos, events
"""
# TODO: Manage encoding and EOL # TODO: Manage encoding and EOL
cal = u"\n".join(( cal = u"\n".join((
u"BEGIN:VCALENDAR", u"BEGIN:VCALENDAR",
@ -37,9 +42,7 @@ def writeCalendar(headers=[calendar.Header("PRODID:-//The Radicale Team//NONSGML
return u"\n".join([line for line in cal.splitlines() if line]) return u"\n".join([line for line in cal.splitlines() if line])
def headers(vcalendar): def headers(vcalendar):
""" """Find Headers items in ``vcalendar``."""
Find Headers Items in vcalendar
"""
headers = [] headers = []
lines = vcalendar.splitlines() lines = vcalendar.splitlines()
@ -53,6 +56,10 @@ def headers(vcalendar):
return headers return headers
def _parse(vcalendar, tag, obj): def _parse(vcalendar, tag, obj):
"""Find ``tag`` items in ``vcalendar``.
Return a list of items of type ``obj``.
"""
items = [] items = []
lines = vcalendar.splitlines() lines = vcalendar.splitlines()

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,6 +18,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Calendar storage support configuration.
"""
from .. import config from .. import config
_support = __import__(config.get("support", "type"), locals(), globals()) _support = __import__(config.get("support", "type"), locals(), globals())

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -16,6 +18,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Plain text storage.
"""
import os import os
import posixpath import posixpath
@ -25,9 +31,7 @@ from .. import config
_folder = os.path.expanduser(config.get("support", "folder")) _folder = os.path.expanduser(config.get("support", "folder"))
def calendars(): def calendars():
""" """List available calendars paths."""
List Available Calendars Paths
"""
calendars = [] calendars = []
for folder in os.listdir(_folder): for folder in os.listdir(_folder):
@ -37,40 +41,34 @@ def calendars():
return calendars return calendars
def mkcalendar(name): def mkcalendar(name):
""" """Write a new calendar called ``name``."""
Write new calendar
"""
user, cal = name.split(posixpath.sep) user, cal = name.split(posixpath.sep)
if not os.path.exists(os.path.join(_folder, user)): if not os.path.exists(os.path.join(_folder, user)):
os.makedirs(os.path.join(_folder, user)) os.makedirs(os.path.join(_folder, user))
fd = open(os.path.join(_folder, user, cal), "w") fd = open(os.path.join(_folder, user, cal), "w")
fd.write(ical.writeCalendar().encode(config.get("encoding", "stock"))) fd.write(ical.write_calendar().encode(config.get("encoding", "stock")))
def read(cal): def read(cal):
""" """Read calendar ``cal``."""
Read cal
"""
path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep))
return open(path).read() return open(path).read()
def append(cal, vcalendar): def append(cal, vcalendar):
""" """Append ``vcalendar`` to ``cal``."""
Append vcalendar to cal old_calendar = unicode(read(cal), config.get("encoding", "stock"))
""" old_tzs = [tz.tzid for tz in ical.timezones(old_calendar)]
oldCalendar = unicode(read(cal), config.get("encoding", "stock"))
oldTzs = [tz.tzid for tz in ical.timezones(oldCalendar)]
path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep))
oldObjects = [] old_objects = []
oldObjects.extend([event.etag() for event in ical.events(oldCalendar)]) old_objects.extend([event.etag() for event in ical.events(old_calendar)])
oldObjects.extend([todo.etag() for todo in ical.todos(oldCalendar)]) old_objects.extend([todo.etag() for todo in ical.todos(old_calendar)])
objects = [] objects = []
objects.extend(ical.events(vcalendar)) objects.extend(ical.events(vcalendar))
objects.extend(ical.todos(vcalendar)) objects.extend(ical.todos(vcalendar))
for tz in ical.timezones(vcalendar): for tz in ical.timezones(vcalendar):
if tz.tzid not in oldTzs: if tz.tzid not in old_tzs:
# TODO: Manage position, encoding and EOL # TODO: Manage position, encoding and EOL
fd = open(path) fd = open(path)
lines = [line for line in fd.readlines() if line] lines = [line for line in fd.readlines() if line]
@ -84,7 +82,7 @@ def append(cal, vcalendar):
fd.close() fd.close()
for obj in objects: for obj in objects:
if obj.etag() not in oldObjects: if obj.etag() not in old_objects:
# TODO: Manage position, encoding and EOL # TODO: Manage position, encoding and EOL
fd = open(path) fd = open(path)
lines = [line for line in fd.readlines() if line] lines = [line for line in fd.readlines() if line]
@ -98,9 +96,7 @@ def append(cal, vcalendar):
fd.close() fd.close()
def remove(cal, etag): def remove(cal, etag):
""" """Remove object named ``etag`` from ``cal``."""
Remove object named uid from cal
"""
path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep)) path = os.path.join(_folder, cal.replace(posixpath.sep, os.path.sep))
cal = unicode(read(cal), config.get("encoding", "stock")) cal = unicode(read(cal), config.get("encoding", "stock"))
@ -111,11 +107,10 @@ def remove(cal, etag):
events = [event for event in ical.events(cal) if event.etag() != etag] events = [event for event in ical.events(cal) if event.etag() != etag]
fd = open(path, "w") fd = open(path, "w")
fd.write(ical.writeCalendar(headers, timezones, todos, events).encode(config.get("encoding", "stock"))) fd.write(ical.write_calendar(headers, timezones, todos, events).encode(config.get("encoding", "stock")))
fd.close() fd.close()
if config.get("support", "defaultCalendar"): if config.get("support", "calendar"):
user, cal = config.get("support", "defaultCalendar").split(posixpath.sep) user, cal = config.get("support", "calendar").split(posixpath.sep)
if not os.path.exists(os.path.join(_folder, user, cal)): if not os.path.exists(os.path.join(_folder, user, cal)):
mkcalendar(config.get("support", "defaultCalendar")) mkcalendar(config.get("support", "calendar"))

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8; indent-tabs-mode: nil; -*- # -*- coding: utf-8; indent-tabs-mode: nil; -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 The Radicale Team # Copyright © 2008-2009 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,7 +19,7 @@
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
""" """
XML and iCal requests manager XML and iCal requests manager.
Note that all these functions need to receive unicode objects for full Note that all these functions need to receive unicode objects for full
iCal requests (PUT) and string objects with charset correctly defined iCal requests (PUT) and string objects with charset correctly defined
@ -32,22 +34,21 @@ import xml.etree.ElementTree as ET
import config import config
import ical import ical
# TODO: This is a well-known and accepted hack for ET # 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
# readers. Is there another clean solution to force namespaces?
for key,value in config.items("namespace"): for key,value in config.items("namespace"):
ET._namespace_map[value] = key ET._namespace_map[value] = key
def _tag(shortName, local): def _tag(short_name, local):
""" """Get XML Clark notation {uri(``short_name``)}``local``."""
Get XML Clark notation {uri(shortName)}local return "{%s}%s"%(config.get("namespace", short_name), local)
"""
return "{%s}%s"%(config.get("namespace", shortName), local)
def delete(obj, calendar, url): def delete(obj, calendar, url):
""" """Read and answer DELETE requests.
Read and answer DELETE requests
"""
# Read rfc4918-9.6 for info
Read rfc4918-9.6 for info.
"""
# Reading request # Reading request
calendar.remove(obj) calendar.remove(obj)
@ -66,14 +67,13 @@ def delete(obj, calendar, url):
return ET.tostring(multistatus, config.get("encoding", "request")) return ET.tostring(multistatus, config.get("encoding", "request"))
def propfind(xmlRequest, calendar, url): def propfind(xml_request, calendar, url):
""" """Read and answer PROPFIND requests.
Read and answer PROPFIND requests
"""
# Read rfc4918-9.1 for info
Read rfc4918-9.1 for info.
"""
# Reading request # Reading request
root = ET.fromstring(xmlRequest) root = ET.fromstring(xml_request)
propElement = root.find(_tag("D", "prop")) propElement = root.find(_tag("D", "prop"))
propList = propElement.getchildren() propList = propElement.getchildren()
@ -117,9 +117,7 @@ def propfind(xmlRequest, calendar, url):
return ET.tostring(multistatus, config.get("encoding", "request")) return ET.tostring(multistatus, config.get("encoding", "request"))
def put(icalRequest, calendar, url, obj): def put(icalRequest, calendar, url, obj):
""" """Read PUT requests."""
Read PUT requests
"""
if obj: if obj:
# PUT is modifying obj # PUT is modifying obj
calendar.replace(obj, icalRequest) calendar.replace(obj, icalRequest)
@ -127,14 +125,13 @@ def put(icalRequest, calendar, url, obj):
# PUT is adding a new object # PUT is adding a new object
calendar.append(icalRequest) calendar.append(icalRequest)
def report(xmlRequest, calendar, url): def report(xml_request, calendar, url):
""" """Read and answer REPORT requests.
Read and answer REPORT requests
"""
# Read rfc3253-3.6 for info
Read rfc3253-3.6 for info.
"""
# Reading request # Reading request
root = ET.fromstring(xmlRequest) root = ET.fromstring(xml_request)
propElement = root.find(_tag("D", "prop")) propElement = root.find(_tag("D", "prop"))
propList = propElement.getchildren() propList = propElement.getchildren()
@ -209,7 +206,7 @@ def report(xmlRequest, calendar, url):
if _tag("C", "calendar-data") in properties: if _tag("C", "calendar-data") in properties:
cdata = ET.Element(_tag("C", "calendar-data")) cdata = ET.Element(_tag("C", "calendar-data"))
# TODO: Maybe assume that events and todos are not the same # TODO: Maybe assume that events and todos are not the same
cdata.text = ical.writeCalendar(headers, timezones, [obj]) cdata.text = ical.write_calendar(headers, timezones, [obj])
prop.append(cdata) prop.append(cdata)
status = ET.Element(_tag("D", "status")) status = ET.Element(_tag("D", "status"))