Merge branch 'master' of git://gitorious.org/radicale/radicale

Conflicts:
	radicale/__init__.py
This commit is contained in:
Corentin Le Bail 2011-04-10 09:35:01 +02:00
commit f8137315c0
7 changed files with 95 additions and 48 deletions

9
NEWS
View File

@ -6,10 +6,17 @@
NEWS
------
0.5 - *Not released yet*
0.6 - *Not released yet*
========================
* IPv6 support
0.5 - Historical Artifacts
==========================
* Calendar depth
* iPhone support
* MacOS and Windows support
* HEAD requests management
* htpasswd user from calendar path

7
TODO
View File

@ -6,17 +6,12 @@
TODO
------
0.5
===
* iCal and iPhone support
0.6
===
* [IN PROGRESS] Group calendars
* [IN PROGRESS] LDAP and databases auth support
* [IN PROGRESS] Smart, verbose and configurable logs
* CalDAV rights
* Read-only access for foreign users

9
config
View File

@ -6,10 +6,11 @@
# The current values are the default ones
[server]
# CalDAV server hostname, empty for all hostnames
host =
# CalDAV server port
port = 5232
# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# IPv6 adresses are configured to only allow IPv6 connections
hosts = 0.0.0.0:5232
# Daemon flag
daemon = False
# SSL flag, enable HTTPS protocol

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Radicale Server - Calendar Server
@ -26,10 +26,9 @@
# pylint: disable-msg=W0406
"""
Radicale Server entry point.
Radicale CalDAV Server.
Launch the Radicale Server according to configuration and command-line
arguments.
Launch the server according to configuration and command-line options.
"""
@ -38,15 +37,13 @@ arguments.
import os
import sys
import optparse
import signal
import threading
import radicale
# Get command-line options
parser = optparse.OptionParser()
parser.add_option(
"-v", "--version", action="store_true",
default=False,
help="show version and exit")
parser = optparse.OptionParser(version=radicale.VERSION)
parser.add_option(
"-d", "--daemon", action="store_true",
default=radicale.config.getboolean("server", "daemon"),
@ -55,13 +52,9 @@ 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"),
help="set server hostname")
parser.add_option(
"-p", "--port", type="int",
default=radicale.config.getint("server", "port"),
help="set server port")
"-H", "--hosts",
default=radicale.config.get("server", "hosts"),
help="set server hostnames and ports")
parser.add_option(
"-s", "--ssl", action="store_true",
default=radicale.config.getboolean("server", "ssl"),
@ -72,11 +65,11 @@ parser.add_option(
parser.add_option(
"-k", "--key",
default=radicale.config.get("server", "key"),
help="private key file ")
help="set private key file")
parser.add_option(
"-c", "--certificate",
default=radicale.config.get("server", "certificate"),
help="certificate file ")
help="set certificate file")
options = parser.parse_args()[0]
# Update Radicale configuration according to options
@ -86,19 +79,52 @@ for option in parser.option_list:
value = getattr(options, key)
radicale.config.set("server", key, value)
# Print version and exit if the option is given
if options.version:
print(radicale.VERSION)
sys.exit()
# Fork if Radicale is launched as daemon
if options.daemon:
if os.fork():
sys.exit()
sys.stdout = sys.stderr = open(os.devnull, "w")
# Launch calendar server
# Create calendar servers
servers = []
server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
server = server_class(
(options.host, options.port), radicale.CalendarHTTPHandler)
shutdown_program = threading.Event()
for host in options.hosts.split(','):
address, port = host.strip().rsplit(':', 1)
address, port = address.strip('[] '), int(port)
servers.append(server_class((address, port), radicale.CalendarHTTPHandler))
# SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for shutdown
signal.signal(signal.SIGTERM, lambda *_: shutdown_program.set())
signal.signal(signal.SIGINT, lambda *_: shutdown_program.set())
def serve_forever(server):
"""Serve a server forever, cleanly shutdown when things go wrong."""
try:
server.serve_forever()
finally:
shutdown_program.set()
# Start the servers in a different loop to avoid possible race-conditions, when
# a server exists but another server is added to the list at the same time
for server in servers:
threading.Thread(target=serve_forever, args=(server,)).start()
# Main loop: wait until all servers are exited
try:
# We must do the busy-waiting here, as all ``.join()`` calls completly
# block the thread, such that signals are not received
while True:
# The number is irrelevant, it only needs to be greater than 0.05 due
# to python implementing its own busy-waiting logic
shutdown_program.wait(5.0)
if shutdown_program.is_set():
break
finally:
# Ignore signals, so that they cannot interfere
signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGTERM, signal.SIG_IGN)
for server in servers:
server.shutdown()

View File

@ -88,10 +88,26 @@ class HTTPServer(server.HTTPServer):
# Maybe a Pylint bug, ``__init__`` calls ``server.HTTPServer.__init__``
# pylint: disable=W0231
def __init__(self, address, handler):
def __init__(self, address, handler, bind_and_activate=True):
"""Create server."""
log.log(10, "Create HTTP server.")
server.HTTPServer.__init__(self, address, handler)
ipv6 = ":" in address[0]
if ipv6:
self.address_family = socket.AF_INET6
# Do not bind and activate, as we might change socketopts
server.HTTPServer.__init__(self, address, handler, False)
if ipv6:
# Only allow IPv6 connections to the IPv6 socket
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
if bind_and_activate:
self.server_bind()
self.server_activate()
self.acl = acl.load()
# pylint: enable=W0231
@ -100,7 +116,7 @@ class HTTPSServer(HTTPServer):
"""HTTPS server."""
PROTOCOL = "https"
def __init__(self, address, handler):
def __init__(self, address, handler, bind_and_activate=True):
"""Create server by wrapping HTTP socket in an SSL socket."""
log.log(10, "Create server by wrapping HTTP socket in an SSL socket.")
# Fails with Python 2.5, import if needed
@ -108,13 +124,15 @@ class HTTPSServer(HTTPServer):
import ssl
# pylint: enable=F0401
HTTPServer.__init__(self, address, handler)
HTTPServer.__init__(self, address, handler, False)
self.socket = ssl.wrap_socket(
socket.socket(self.address_family, self.socket_type),
self.socket,
server_side=True,
certfile=config.get("server", "certificate"),
keyfile=config.get("server", "key"),
ssl_version=ssl.PROTOCOL_SSLv23)
if bind_and_activate:
self.server_bind()
self.server_activate()

View File

@ -39,8 +39,7 @@ except ImportError:
# Default configuration
INITIAL_CONFIG = {
"server": {
"host": "",
"port": "5232",
"hosts": "0.0.0.0:5232",
"daemon": "False",
"ssl": "False",
"certificate": "/etc/apache2/ssl/server.crt",

View File

@ -27,8 +27,8 @@ 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,
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
MacOS X) and Windows. It is known to work with Evolution, Lightning, iPhone
and Android clients. It is free and open-source software, released under GPL
version 3.
For further information, please visit the `Radicale Website
@ -91,4 +91,5 @@ setup(
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.0",
"Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.2",
"Topic :: Office/Business :: Groupware"])