diff --git a/.project b/.project
new file mode 100644
index 00000000..94930993
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ Radicale
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 00000000..09e36056
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,10 @@
+
+
+
+
+
+/Radicale
+
+python 2.7
+Default
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000..864a84bd
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+#Sat Aug 04 10:58:22 CEST 2012
+eclipse.preferences.version=1
+encoding//radicale/__init__.py=utf-8
+encoding//radicale/__main__.py=utf-8
diff --git a/NEWS.rst b/NEWS.rst
index 93c9be0b..7bbc28e7 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -3,6 +3,18 @@
======
+0.7.1 - Waterfalls
+==================
+
+* Many address books fixes
+* New IMAP ACL (by Daniel Aleksandersen)
+* PAM ACL fixed (by Daniel Aleksandersen)
+* Courier ACL fixed (by Benjamin Frank)
+* Always set display name to collections (by Oskari Timperi)
+* Various DELETE responses fixed
+
+
+
0.7 - Eternal Sunshine
======================
diff --git a/radicale/__init__.py b/radicale/__init__.py
index 7a24859b..d88af77c 100644
--- a/radicale/__init__.py
+++ b/radicale/__init__.py
@@ -172,7 +172,7 @@ class Application(object):
def __call__(self, environ, start_response):
"""Manage a request."""
log.LOGGER.info("%s request at %s received" % (
- environ["REQUEST_METHOD"], environ["PATH_INFO"]))
+ environ["REQUEST_METHOD"], environ["PATH_INFO"]))
headers = pprint.pformat(self.headers_log(environ))
log.LOGGER.debug("Request headers:\n%s" % headers)
@@ -216,20 +216,29 @@ class Application(object):
if access.is_authenticated(user, password):
- collections = []
- for collection in items:
- log.LOGGER.debug("Testing %s" % (collection.name))
- if not isinstance(collection, ical.Collection):
- log.LOGGER.info("not a collection: " + collection.name)
+ last_collection_allowed = None
+ allowed_items = []
+ for item in items:
+ log.LOGGER.debug("Testing %s" % (item.name))
+ if not isinstance(item, ical.Collection):
+ # item is not a colleciton, it's the child of the last
+ # collection we've met in the loop. Only add this item if
+ # this last collection was allowed. log.LOGGER.info("not a collection: " + collection.name)
# collections.append(collection)
- elif access.may_read(user, collection) or access.may_write(user, collection):
- log.LOGGER.info("Has access to " + collection.name)
- collections.append(collection)
+ if last_collection_allowed:
+ allowed_items.append(item)
+ else:
+ if access.may_read(user, item) or access.may_write(user, item):
+ log.LOGGER.info(user + "has access to " + item.name)
+ last_collection_allowed = True
+ allowed_items.append(item)
+ else:
+ last_collection_allowed = False
- if collections:
+ if allowed_items:
# Collections found
status, headers, answer = function(
- environ, collections, content, user)
+ environ, allowed_items, content, user)
else:
# Good user and no collections found, redirect user to home
location = "/%s/" % str(quote(user))
@@ -241,7 +250,7 @@ class Application(object):
else:
# Send answer anyway since else we're getting into a redirect loop
status, headers, answer = function(
- environ, collections, content, user)
+ environ, allowed_items, content, user)
else:
@@ -499,7 +508,7 @@ class Application(object):
# Evolution bug workaround
etag = environ.get("HTTP_IF_MATCH", "").replace("\\", "")
if (not item and not etag) or (
- item and ((etag or item.etag) == item.etag)):
+ item and ((etag or item.etag) == item.etag)):
# PUT allowed in 3 cases
# Case 1: No item and no ETag precondition: Add new item
# Case 2: Item and ETag precondition verified: Modify item
diff --git a/radicale/__main__.py b/radicale/__main__.py
index b2a0da6f..175f6133 100644
--- a/radicale/__main__.py
+++ b/radicale/__main__.py
@@ -91,6 +91,8 @@ def run():
# Fork if Radicale is launched as daemon
if options.daemon:
+ if options.pid and os.path.exists(options.pid):
+ raise OSError("PID file exists: %s" % options.pid)
pid = os.fork()
if pid:
try:
diff --git a/radicale/acl/courier.py b/radicale/acl/courier.py
index 21241c1f..c5bd7b97 100644
--- a/radicale/acl/courier.py
+++ b/radicale/acl/courier.py
@@ -33,7 +33,7 @@ def is_authenticated(user, password):
"""Check if ``user``/``password`` couple is valid."""
line = "%s\nlogin\n%s\n%s" % (sys.argv[0], user, password)
- line = "%i\n%s" % (len(line), line)
+ line = "AUTH %i\n%s" % (len(line), line)
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(COURIER_SOCKET)
@@ -48,7 +48,13 @@ def is_authenticated(user, password):
log.LOGGER.debug("Got Courier socket response: %r" % data)
- if repr(data) == "FAIL":
- return False
+ # Address, HOME, GID, and either UID or USERNAME are mandatory in resposne
+ # see http://www.courier-mta.org/authlib/README_authlib.html#authpipeproto
+ for line in data.split():
+ if 'GID' in line:
+ return True
- return True
+ # default is reject
+ # this alleviates the problem of a possibly empty reply from authlib
+ # see http://www.courier-mta.org/authlib/README_authlib.html#authpipeproto
+ return False
diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py
index d72aa492..6d3a92ac 100644
--- a/radicale/xmlutils.py
+++ b/radicale/xmlutils.py
@@ -270,8 +270,8 @@ def _propfind_response(path, item, props, user):
element.append(privilege)
elif tag == _tag("D", "supported-report-set"):
for report_name in (
- "principal-property-search", "sync-collection"
- "expand-property", "principal-search-property-set"):
+ "principal-property-search", "sync-collection"
+ "expand-property", "principal-search-property-set"):
supported = ET.Element(_tag("D", "supported-report"))
report_tag = ET.Element(_tag("D", "report"))
report_tag.text = report_name
@@ -422,9 +422,8 @@ def report(path, xml_request, collection):
props = [prop.tag for prop in prop_element]
if collection:
- if root.tag in (
- _tag("C", "calendar-multiget"),
- _tag("CR", "addressbook-multiget")):
+ if root.tag in (_tag("C", "calendar-multiget"),
+ _tag("CR", "addressbook-multiget")):
# Read rfc4791-7.9 for info
hreferences = set(
href_element.text for href_element
@@ -472,8 +471,8 @@ def report(path, xml_request, collection):
element = ET.Element(tag)
if tag == _tag("D", "getetag"):
element.text = item.etag
- elif tag in (
- _tag("C", "calendar-data"), _tag("CR", "address-data")):
+ elif tag in (_tag("C", "calendar-data"),
+ _tag("CR", "address-data")):
if isinstance(item, ical.Component):
element.text = ical.serialize(
collection_tag, collection_headers,