==========================
- Radicale - CalDAV Server
+ Cadaver - CalDAV Server
==========================
------
==========================
- Radicale - CalDAV Server
+ Cadaver - CalDAV Server
==========================
--------
README
--------
-The Radicale Project is a free and open-source CalDAV calendar server.
+The Cadaver Project is a free and open-source CalDAV calendar server.
-For complete documentation, please visit the `Radicale online documentation
-<http://www.radicale.org/documentation>`_
+For complete documentation, please visit the `Cadaver online documentation
+<http://www.cadaver.org/documentation>`_
==========================
- Radicale - CalDAV Server
+ Cadaver - CalDAV Server
==========================
------
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
+# Copyright © 2011 Keith Packard
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
# This file is just a script, allow [a-z0-9]* variable names
# pylint: disable-msg=C0103
-# ``import radicale`` refers to the ``radicale`` module, not ``radicale.py``
+# ``import cadaver`` refers to the ``cadaver`` module, not ``cadaver.py``
# pylint: disable-msg=W0406
"""
-Radicale Server entry point.
+Cadaver Server entry point.
-Launch the Radicale Server according to configuration and command-line
+Launch the Cadaver Server according to configuration and command-line
arguments.
"""
import sys
import optparse
-import radicale
+import cadaver
# Get command-line options
parser = optparse.OptionParser()
help="show version and exit")
parser.add_option(
"-d", "--daemon", action="store_true",
- default=radicale.config.getboolean("server", "daemon"),
+ default=cadaver.config.getboolean("server", "daemon"),
help="launch as daemon")
parser.add_option(
"-f", "--foreground", action="store_false", dest="daemon",
help="launch in foreground (opposite of --daemon)")
parser.add_option(
"-H", "--host",
- default=radicale.config.get("server", "host"),
+ default=cadaver.config.get("server", "host"),
help="set server hostname")
parser.add_option(
"-p", "--port", type="int",
- default=radicale.config.getint("server", "port"),
+ default=cadaver.config.getint("server", "port"),
help="set server port")
parser.add_option(
"-s", "--ssl", action="store_true",
- default=radicale.config.getboolean("server", "ssl"),
+ default=cadaver.config.getboolean("server", "ssl"),
help="use SSL connection")
parser.add_option(
"-S", "--no-ssl", action="store_false", dest="ssl",
help="do not use SSL connection (opposite of --ssl)")
parser.add_option(
"-k", "--key",
- default=radicale.config.get("server", "key"),
+ default=cadaver.config.get("server", "key"),
help="private key file ")
parser.add_option(
"-c", "--certificate",
- default=radicale.config.get("server", "certificate"),
+ default=cadaver.config.get("server", "certificate"),
help="certificate file ")
options = parser.parse_args()[0]
-# Update Radicale configuration according to options
+# Update Cadaver configuration according to options
for option in parser.option_list:
key = option.dest
if key:
value = getattr(options, key)
- radicale.config.set("server", key, value)
+ cadaver.config.set("server", key, value)
# Print version and exit if the option is given
if options.version:
- print(radicale.VERSION)
+ print(cadaver.VERSION)
sys.exit()
-# Fork if Radicale is launched as daemon
+# Fork if Cadaver is launched as daemon
if options.daemon:
if os.fork():
sys.exit()
sys.stdout = sys.stderr = open(os.devnull, "w")
# Launch calendar server
-server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
+server_class = cadaver.HTTPSServer if options.ssl else cadaver.HTTPServer
server = server_class(
- (options.host, options.port), radicale.CalendarHTTPHandler)
+ (options.host, options.port), cadaver.CalendarHTTPHandler)
server.serve_forever()
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
-Radicale Server module.
+Cadaver Server module.
This module offers 3 useful classes:
managing SSL connections;
- ``CalendarHTTPHandler`` is a CalDAV request handler for HTTP(S) servers.
-To use this module, you should take a look at the file ``radicale.py`` that
+To use this module, you should take a look at the file ``cadaver.py`` that
should have been included in this package.
"""
import BaseHTTPServer as server
# pylint: enable=F0401
-from radicale import acl, config, ical, xmlutils
+from cadaver import acl, config, ical, xmlutils
VERSION = "0.5"
request.send_response(client.UNAUTHORIZED)
request.send_header(
"WWW-Authenticate",
- "Basic realm=\"Radicale Server - Password Required\"")
+ "Basic realm=\"Cadaver Server - Password Required\"")
request.end_headers()
# pylint: enable=W0212
content_type = self.headers.get("Content-Type", None)
if content_type and "charset=" in content_type:
charsets.append(content_type.split("charset=")[1].strip())
- # Then append default Radicale charset
+ # Then append default Cadaver charset
charsets.append(self._encoding)
# Then append various fallbacks
charsets.append("utf-8")
def do_REPORT(self):
"""Manage REPORT request."""
xml_request = self.rfile.read(int(self.headers["Content-Length"]))
+ print ("report %s" % xml_request)
self._answer = xmlutils.report(self.path, xml_request, self._calendar)
self.send_response(client.MULTI_STATUS)
self.send_header("Content-Length", len(self._answer))
self.end_headers()
+ print ("answer %s" % self._answer)
self.wfile.write(self._answer)
# pylint: enable=C0103
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
Users and rights management.
"""
-from radicale import config
+from cadaver import config
def load():
"""Load list of available ACL managers."""
- module = __import__("radicale.acl", fromlist=[config.get("acl", "type")])
+ module = __import__("cadaver.acl", fromlist=[config.get("acl", "type")])
return getattr(module, config.get("acl", "type"))
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
Fake ACL.
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
Htpasswd ACL.
import base64
import hashlib
-from radicale import config
+from cadaver import config
def _plain(hash_value, password):
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
-Radicale configuration module.
+Cadaver configuration module.
Give a configparser-like interface to read and write configuration.
"acl": {
"type": "fake",
"personal": "False",
- "filename": "/etc/radicale/users",
+ "filename": "/etc/cadaver/users",
"encryption": "crypt"},
"storage": {
- "folder": os.path.expanduser("~/.config/radicale/calendars")}}
+ "folder": os.path.expanduser("~/.config/cadaver/calendars")}}
# Create a ConfigParser and configure it
_CONFIG_PARSER = ConfigParser()
for key, value in values.items():
_CONFIG_PARSER.set(section, key, value)
-_CONFIG_PARSER.read("/etc/radicale/config")
-_CONFIG_PARSER.read(os.path.expanduser("~/.config/radicale/config"))
+_CONFIG_PARSER.read("/etc/cadaver/config")
+_CONFIG_PARSER.read(os.path.expanduser("~/.config/cadaver/config"))
# Wrap config module into ConfigParser instance
sys.modules[__name__] = _CONFIG_PARSER
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
-Radicale calendar classes.
+Cadaver calendar classes.
Define the main classes of a calendar as seen from the server.
import os
import codecs
import time
+import calendar
import hashlib
import glob
import tempfile
+import vobject
-from radicale import config
+from cadaver import config
FOLDER = os.path.expanduser(config.get("storage", "folder"))
lines.append("END:VCALENDAR\n")
return "\n".join(lines)
+def parse_time(line):
+ dtstart = line.rsplit(":",1)[1]
+ print ("dtstart %s " % dtstart)
+ try:
+ return time.strptime("%Y%m%dT%H%M%SZ", dtstart)
+ except ValueError:
+ try:
+ return time.strptime("%Y%m%dT%H%M%S", dtstart)
+ except ValueError:
+ return 0
class Item(object):
"""Internal iCal item."""
def __init__(self, text, name=None, path=None):
"""Initialize object from ``text`` and different ``kwargs``."""
- # print "New item %s = %s\n" % (name, text)
+ print "New item %s = %s\n" % (name, text)
+
+ lines = text.splitlines(True)
+ newlines=""
+ for line in lines:
+ if line.find(":") >= 0:
+ newlines = newlines + line
+
+ text = newlines
+
+# try:
+# j = vobject.readOne(text)
+# j.prettyPrint()
+# except Exception:
+# print ("parse error\n")
self.text = text
self._name = name
# An item must have a name, determined in order by:
#
# - the ``name`` parameter
- # - the ``X-RADICALE-NAME`` iCal property (for Events and Todos)
+ # - the ``X-CADAVER-NAME`` iCal property (for Events and Todos)
# - the ``UID`` iCal property (for Events and Todos)
# - the ``TZID`` iCal property (for Timezones)
if not self._name:
for line in self.text.splitlines():
- if line.startswith("X-RADICALE-NAME:"):
- self._name = line.replace("X-RADICALE-NAME:", "").strip()
+ if line.startswith("X-CADAVER-NAME:"):
+ self._name = line.replace("X-CADAVER-NAME:", "").strip()
break
elif line.startswith("TZID:"):
self._name = line.replace("TZID:", "").strip()
break
elif line.startswith("UID:"):
self._name = line.replace("UID:", "").strip()
- # Do not break, a ``X-RADICALE-NAME`` can appear next
+ # Do not break, a ``X-CADAVER-NAME`` can appear next
+
+# self.dtstart = 0
+# for line in self.text.splitlines():
+# if line.startswith("DTSTART"):
+# self.dtstart = parse_time(line)
+# print ("dtstart %s -> %d" % (line, self.dtstart))
+ h = hashlib.sha1(text.encode("utf-8"))
+ self._etag = h.hexdigest()
if not self._name:
- h = hashlib.sha1(text.encode("utf-8"));
- self._name = h.hexdigest();
+ self._name = self._etag
- if "\nX-RADICALE-NAME:" in text:
+ if "\nX-CADAVER-NAME:" in text:
for line in self.text.splitlines():
- if line.startswith("X-RADICALE-NAME:"):
+ if line.startswith("X-CADAVER-NAME:"):
self.text = self.text.replace(
- line, "X-RADICALE-NAME:%s" % self._name)
+ line, "X-CADAVER-NAME:%s" % self._name)
elif "\nUID:" in text:
self.text = self.text.replace(
- "\nUID:", "\nX-RADICALE-NAME:%s\nUID:" % self._name)
+ "\nUID:", "\nX-CADAVER-NAME:%s\nUID:" % self._name)
else:
self.text = self.text.replace(
- "\nSUMMARY:", "\nX-RADICALE-NAME:%s\nSUMMARY:" % self._name)
- self._etag = hash(self.text)
+ "\nSUMMARY:", "\nX-CADAVER-NAME:%s\nSUMMARY:" % self._name)
@property
def etag(self):
Etag is mainly used to know if an item has changed.
"""
- return '"%s"' % self._etag
+ return '%s' % self._etag
@property
def name(self):
def insert_file(self, path):
try:
-# print ("Insert file %s" % path)
+ print ("Insert file %s" % path)
text = open(path).read()
self.insert_text(text, path)
except IOError:
return
def remove_file(self, path):
-# print ("Remove file %s" % path)
+ print ("Remove file %s" % path)
old_items=[]
for old_item in self.my_items:
if old_item.path == path:
self.my_items.remove(old_item)
def scan_file(self, path):
-# print ("Rescan file %s" % path)
+ print ("Rescan file %s" % path)
self.remove_file(path)
self.insert_file(path)
def scan_dir(self):
- files = glob.glob(self.pattern)
- mtime = os.path.getmtime(self.path)
- if mtime == self.mtime:
+ try:
+ mtime = os.path.getmtime(self.path)
+ if mtime == self.mtime:
+ return
+ except OSError:
return
self.mtime = mtime
+ files = glob.glob(self.pattern)
for file in files:
if not file in self.files:
self.insert_file(file)
items = []
- lines = text.splitlines()
+ jtext = text.replace("\n ", "")
+
+ lines = jtext.splitlines()
in_item = False
for line in lines:
def write(self, headers=None, items=None):
#"""Write calendar with given parameters."""
#headers = headers or self.headers or (
- # Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
+ # Header("PRODID:-//Cadaver//NONSGML Cadaver Server//EN"),
# Header("VERSION:2.0"))
#items = items if items is not None else self.items
@property
def etag(self):
"""Etag from calendar."""
- return '"%s"' % hash(self.text)
+ return '%s' % hash(self.text)
@property
def name(self):
self.scan_dir()
headers = []
- headers.append(Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"))
+ headers.append(Header("PRODID:-//Cadaver//NONSGML Cadaver Server//EN"))
headers.append(Header("VERSION:2.0"))
return serialize(headers=headers, items=self.my_items)
"""Find headers items in calendar."""
header_lines = []
- header_lines.append(Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"))
+ header_lines.append(Header("PRODID:-//Cadaver//NONSGML Cadaver Server//EN"))
header_lines.append(Header("VERSION:2.0"))
return header_lines
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2008-2011 Guillaume Ayoub
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
XML and iCal requests manager.
import xml.etree.ElementTree as ET
-from radicale import client, config, ical
+from cadaver import client, config, ical
NAMESPACES = {
def name_from_path(path):
- """Return Radicale item name from ``path``."""
+ """Return Cadaver item name from ``path``."""
path_parts = path.strip("/").split("/")
return path_parts[-1] if len(path_parts) > 2 else None
-# Config file for Radicale - A simple calendar server
+# Config file for Cadaver - A simple calendar server
#
-# Place it into /etc/radicale/config (global)
-# or ~/.config/radicale/config (user)
+# Place it into /etc/cadaver/config (global)
+# or ~/.config/cadaver/config (user)
#
# The current values are the default ones
# Personal calendars only available for logged in users (if needed)
personal = False
# Htpasswd filename (if needed)
-filename = /etc/radicale/users
+filename = /etc/cadaver/users
# Htpasswd encryption method (if needed)
# Value: plain | sha1 | crypt
encryption = crypt
[storage]
# Folder for storing local calendars,
# created if not present
-folder = ~/.config/radicale/calendars
+folder = ~/.config/cadaver/calendars
# vim:ft=cfg
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
-# This file is part of Radicale Server - Calendar Server
+# This file is part of Cadaver Server - Calendar Server
# Copyright © 2009-2011 Guillaume Ayoub
#
# This library is free software: you can redistribute it and/or modify
# GNU General Public License for more details.
#
# 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 Cadaver. If not, see <http://www.gnu.org/licenses/>.
"""
-Radicale CalDAV server
+Cadaver CalDAV server
======================
-The Radicale Project is a CalDAV calendar server. It aims to be a light
+The Cadaver Project is a CalDAV calendar server. It aims to be a light
solution, easy to use, easy to install, easy to configure. As a consequence,
it requires few software dependances and is pre-configured to work
out-of-the-box.
-The Radicale Project runs on most of the UNIX-like platforms (Linux, BSD,
+The Cadaver Project runs on most of the UNIX-like platforms (Linux, BSD,
MacOS X) and Windows. It is known to work with Evolution 2.30+, Lightning 0.9+
and Sunbird 0.9+. It is free and open-source software, released under GPL
version 3.
-For further information, please visit the `Radicale Website
-<http://www.radicale.org/>`_.
+For further information, please visit the `Cadaver Website
+<http://www.cadaver.org/>`_.
"""
from distutils.core import setup
from distutils.command.build_scripts import build_scripts
-import radicale
+import cadaver
# build_scripts is known to have a lot of public methods
"""Build the package."""
def run(self):
"""Run building."""
- # These lines remove the .py extension from the radicale executable
+ # These lines remove the .py extension from the cadaver executable
self.mkpath(self.build_dir)
for script in self.scripts:
root, _ = os.path.splitext(script)
# pylint: enable=R0904
-# When the version is updated, ``radicale.VERSION`` must be modified.
+# When the version is updated, ``cadaver.VERSION`` must be modified.
# A new section in the ``NEWS`` file must be added too.
setup(
- name="Radicale",
- version=radicale.VERSION,
- description="CalDAV Server",
+ name="Cadaver",
+ version=cadaver.VERSION,
+ description="CalDAV and CardDAV Server",
long_description=__doc__,
- author="Guillaume Ayoub",
- author_email="guillaume.ayoub@kozea.fr",
- url="http://www.radicale.org/",
- download_url="http://www.radicale.org/src/radicale/Radicale-%s.tar.gz" % \
- radicale.VERSION,
+ author="Keith Packard",
+ author_email="keithp@keithp.com",
+ url="http://keithp.com/",
+ download_url="http://keithp.com/git/cadaver" % \
+ cadaver.VERSION,
license="GNU GPL v3",
platforms="Any",
- packages=["radicale", "radicale.acl"],
- provides=["radicale"],
- scripts=["radicale.py"],
+ packages=["cadaver", "cadaver.acl"],
+ provides=["cadaver"],
+ scripts=["cadaver.py"],
cmdclass={"build_scripts": BuildScripts},
keywords=["calendar", "CalDAV"],
classifiers=[