diff --git a/config b/config index d04b3da..83daabf 100644 --- a/config +++ b/config @@ -42,4 +42,10 @@ encryption = crypt # created if not present folder = ~/.config/radicale/calendars +[Logging] +# Logging filename +logfile = ~/.config/radicale/radicale.log +# Log facility 10: DEBUG, 20: INFO, 30 WARNING, 40 ERROR, 50 CRITICAL +facility = 50 + # vim:ft=cfg diff --git a/radicale/__init__.py b/radicale/__init__.py index 2fc3f80..aa7e054 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -46,13 +46,14 @@ except ImportError: import BaseHTTPServer as server # pylint: enable=F0401 -from radicale import acl, config, ical, xmlutils +from radicale import acl, config, ical, xmlutils, log VERSION = "git" def _check(request, function): """Check if user has sufficient rights for performing ``request``.""" + log.log(10, "Check if user has sufficient rights for performing ``request``.") # ``_check`` decorator can access ``request`` protected functions # pylint: disable=W0212 authorization = request.headers.get("Authorization", None) @@ -64,8 +65,10 @@ def _check(request, function): user = password = None if request.server.acl.has_right(request._calendar.owner, user, password): + log.log(20, "Sufficient rights for performing ``request``.") function(request) else: + log.log(40, "No sufficient rights for performing ``request``.") request.send_response(client.UNAUTHORIZED) request.send_header( "WWW-Authenticate", @@ -82,6 +85,7 @@ class HTTPServer(server.HTTPServer): # pylint: disable=W0231 def __init__(self, address, handler): """Create server.""" + log.log(10, "Create HTTP server.") server.HTTPServer.__init__(self, address, handler) self.acl = acl.load() # pylint: enable=W0231 @@ -92,6 +96,7 @@ class HTTPSServer(HTTPServer): PROTOCOL = "https" def __init__(self, address, handler): """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 # pylint: disable=F0401 import ssl @@ -110,6 +115,7 @@ class HTTPSServer(HTTPServer): class CalendarHTTPHandler(server.BaseHTTPRequestHandler): """HTTP requests handler for calendars.""" + log.log(10, "HTTP requests handler for calendars.") _encoding = config.get("encoding", "request") # Decorator checking rights before performing request @@ -118,6 +124,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @property def _calendar(self): """The ``ical.Calendar`` object corresponding to the given path.""" + log.log(10, "The ``ical.Calendar`` object corresponding to the given path.") # ``self.path`` must be something like a posix path # ``normpath`` should clean malformed and malicious request paths attributes = posixpath.normpath(self.path.strip("/")).split("/") @@ -127,6 +134,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): def _decode(self, text): """Try to decode text according to various parameters.""" + log.log(10, "Try to decode text according to various parameters.") # List of charsets to try charsets = [] @@ -153,12 +161,14 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): def do_GET(self): """Manage GET request.""" + log.log(10, "Manage GET request.") self.do_HEAD() self.wfile.write(self._answer) @check_rights def do_HEAD(self): """Manage HEAD request.""" + log.log(10, "Manage HEAD request.") item_name = xmlutils.name_from_path(self.path) if item_name: # Get calendar item @@ -188,6 +198,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @check_rights def do_DELETE(self): """Manage DELETE request.""" + log.log(10, "Manage DELETE request.") item = self._calendar.get_item(xmlutils.name_from_path(self.path)) if item and self.headers.get("If-Match", item.etag) == item.etag: # No ETag precondition or precondition verified, delete item @@ -203,6 +214,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): def do_OPTIONS(self): """Manage OPTIONS request.""" + log.log(10, "Manage OPTIONS request.") self.send_response(client.OK) self.send_header( "Allow", "DELETE, HEAD, GET, OPTIONS, PROPFIND, PUT, REPORT") @@ -211,6 +223,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): def do_PROPFIND(self): """Manage PROPFIND request.""" + log.log(10, "Manage PROPFIND request.") xml_request = self.rfile.read(int(self.headers["Content-Length"])) self._answer = xmlutils.propfind( self.path, xml_request, self._calendar, @@ -226,6 +239,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @check_rights def do_PUT(self): """Manage PUT request.""" + log.log(10, "Manage PUT request.") item_name = xmlutils.name_from_path(self.path) item = self._calendar.get_item(item_name) if (not item and not self.headers.get("If-Match")) or \ @@ -249,6 +263,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): @check_rights def do_REPORT(self): """Manage REPORT request.""" + log.log(10, "Manage REPORT request.") xml_request = self.rfile.read(int(self.headers["Content-Length"])) self._answer = xmlutils.report(self.path, xml_request, self._calendar) @@ -257,4 +272,7 @@ class CalendarHTTPHandler(server.BaseHTTPRequestHandler): self.end_headers() self.wfile.write(self._answer) + def log_message(self, format, *args): + log.log(10, format % (args)) + # pylint: enable=C0103 diff --git a/radicale/acl/authLdap.py b/radicale/acl/authLdap.py index d7c08e6..b8142b5 100644 --- a/radicale/acl/authLdap.py +++ b/radicale/acl/authLdap.py @@ -2,7 +2,7 @@ import sys, ldap -from radicale import config +from radicale import config, log def has_right(owner, user, password): if user == None: @@ -12,11 +12,15 @@ def has_right(owner, user, password): if owner != user: return False try: + log.log(10, "Open LDAP server connexion") l=ldap.open(LDAPSERVER, 389) cn="%s%s,%s" % (LDAPPREPEND, user, LDAPAPPEND) + log.log(10, "LDAP bind with dn: %s" %(cn)) l.simple_bind_s(cn, password); + log.log(20, "LDAP bind Ok") return True except: + log.log(40, "LDAP bind error") return False LDAPSERVER = config.get("authLdap", "LDAPServer") diff --git a/radicale/config.py b/radicale/config.py index 1cf9dd6..77f34af 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -56,7 +56,10 @@ INITIAL_CONFIG = { "filename": "/etc/radicale/users", "encryption": "crypt"}, "storage": { - "folder": os.path.expanduser("~/.config/radicale/calendars")}} + "folder": os.path.expanduser("~/.config/radicale/calendars")}, + "logging": { + "logfile": os.path.expanduser("~/.config/radicale/radicale.log"), + "facility": 10}} # Create a ConfigParser and configure it _CONFIG_PARSER = ConfigParser() diff --git a/radicale/log.py b/radicale/log.py new file mode 100644 index 0000000..1861e4e --- /dev/null +++ b/radicale/log.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +import logging, sys +from radicale import config + +class log: + def __init__(self): + self.logger=logging.getLogger("radicale") + self.logger.setLevel(config.get("logging", "facility")) + + handler=logging.FileHandler(config.get("logging", "logfile")) + + formatter = logging.Formatter('%(name)s %(asctime)s %(levelname)s %(message)s') + handler.setFormatter(formatter) + + self.logger.addHandler(handler) + def log(self, level, msg): + self.logger.log(level, msg) + +_LOGGING = log() + +sys.modules[__name__] = _LOGGING \ No newline at end of file diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index b49666c..9289e9d 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -31,7 +31,7 @@ in them for XML requests (all but PUT). import xml.etree.ElementTree as ET -from radicale import client, config, ical +from radicale import client, config, ical, log NAMESPACES = { @@ -47,11 +47,13 @@ def _tag(short_name, local): def _response(code): """Return full W3C names from HTTP status codes.""" + log.log(10, "Return full W3C names from HTTP status codes.") return "HTTP/1.1 %i %s" % (code, client.responses[code]) def name_from_path(path): """Return Radicale item name from ``path``.""" + log.log(10, "Return Radicale item name from ``path``.") return path.split("/")[-1] @@ -62,6 +64,7 @@ def delete(path, calendar): """ # Reading request + log.log(10, "Read and answer DELETE requests.") calendar.remove(name_from_path(path)) # Writing answer @@ -87,8 +90,9 @@ def propfind(path, xml_request, calendar, depth, request): """ # Reading request + log.log(10, "Read and answer PROPFIND requests.") root = ET.fromstring(xml_request) - + prop_element = root.find(_tag("D", "prop")) prop_list = prop_element.getchildren() props = [prop.tag for prop in prop_list] @@ -164,6 +168,7 @@ def propfind(path, xml_request, calendar, depth, request): def put(path, ical_request, calendar): """Read PUT requests.""" + log.log(10, "Read PUT requests.") name = name_from_path(path) if name in (item.name for item in calendar.items): # PUT is modifying an existing item @@ -180,6 +185,7 @@ def report(path, xml_request, calendar): """ # Reading request + log.log(10, "Read and answer REPORT requests.") root = ET.fromstring(xml_request) prop_element = root.find(_tag("D", "prop"))