Selaa lähdekoodia

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

Conflicts:
	radicale/__init__.py
Corentin Le Bail 15 vuotta sitten
vanhempi
sitoutus
f8137315c0
7 muutettua tiedostoa jossa 95 lisäystä ja 48 poistoa
  1. 8 1
      NEWS
  2. 1 6
      TODO
  3. 5 4
      config
  4. 53 27
      radicale.py
  5. 24 6
      radicale/__init__.py
  6. 1 2
      radicale/config.py
  7. 3 2
      setup.py

+ 8 - 1
NEWS

@@ -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

+ 1 - 6
TODO

@@ -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
 

+ 5 - 4
config

@@ -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

+ 53 - 27
radicale.py

@@ -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)
-server.serve_forever()
+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()

+ 24 - 6
radicale/__init__.py

@@ -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,15 +124,17 @@ 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)
-        self.server_bind()
-        self.server_activate()
+
+        if bind_and_activate:
+            self.server_bind()
+            self.server_activate()
 
 
 class CalendarHTTPHandler(server.BaseHTTPRequestHandler):

+ 1 - 2
radicale/config.py

@@ -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",

+ 3 - 2
setup.py

@@ -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"])