Improve WebDAV compatibility

Add getlastmodified and getcontentlength properties, and fix nested
collections support. Fix #321.
This commit is contained in:
Guillaume Ayoub 2016-04-18 09:11:00 +09:00
parent 99544fcf80
commit 65659fc909
2 changed files with 23 additions and 12 deletions

View File

@ -114,14 +114,19 @@ def path_to_filesystem(root, *paths):
class Item: class Item:
def __init__(self, item, href, etag): def __init__(self, item, href, etag, last_modified=None):
self.item = item self.item = item
self.href = href self.href = href
self.etag = etag self.etag = etag
self.last_modified = last_modified
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.item, attr) return getattr(self.item, attr)
@property
def content_length(self):
return len(self.serialize().encode(config.get("encoding", "request")))
class Collection: class Collection:
"""Collection stored in several files per calendar.""" """Collection stored in several files per calendar."""
@ -170,8 +175,7 @@ class Collection:
return return
# Try to guess if the path leads to a collection or an item # Try to guess if the path leads to a collection or an item
if os.path.exists(path_to_filesystem( if os.path.isfile(path_to_filesystem(FOLDER, sane_path)):
FOLDER, *attributes[:-1]) + ".props"):
attributes.pop() attributes.pop()
path = "/".join(attributes) path = "/".join(attributes)
@ -185,7 +189,6 @@ class Collection:
if items: if items:
for item in items: for item in items:
yield collection.get(item[0]) yield collection.get(item[0])
else:
_, directories, _ = next(os.walk(collection._filesystem_path)) _, directories, _ = next(os.walk(collection._filesystem_path))
for sub_path in directories: for sub_path in directories:
full_path = os.path.join(collection._filesystem_path, sub_path) full_path = os.path.join(collection._filesystem_path, sub_path)
@ -248,7 +251,11 @@ class Collection:
if os.path.isfile(path): if os.path.isfile(path):
with open(path, encoding=STORAGE_ENCODING) as fd: with open(path, encoding=STORAGE_ENCODING) as fd:
text = fd.read() text = fd.read()
return Item(vobject.readOne(text), href, get_etag(text)) last_modified = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT",
time.gmtime(os.path.getmtime(path)))
return Item(
vobject.readOne(text), href, get_etag(text), last_modified)
else: else:
log.LOGGER.debug( log.LOGGER.debug(
"Can't tranlate name safely to filesystem, " "Can't tranlate name safely to filesystem, "
@ -365,10 +372,10 @@ class Collection:
@property @property
def last_modified(self): def last_modified(self):
"""Get the HTTP-datetime of when the collection was modified.""" """Get the HTTP-datetime of when the collection was modified."""
last = max([ last = max([os.path.getmtime(self._filesystem_path)] + [
os.path.getmtime(os.path.join(self._filesystem_path, filename)) os.path.getmtime(os.path.join(self._filesystem_path, filename))
for filename in os.listdir(self._filesystem_path)] or [0]) for filename in os.listdir(self._filesystem_path)] or [0])
return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(last)) return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last))
def serialize(self): def serialize(self):
items = [] items = []

View File

@ -276,6 +276,8 @@ def _propfind_response(path, item, props, user, write=False):
tag = ET.Element(_tag("D", "href")) tag = ET.Element(_tag("D", "href"))
tag.text = _href(path) tag.text = _href(path)
element.append(tag) element.append(tag)
elif tag == _tag("D", "getlastmodified"):
element.text = item.last_modified
elif tag in (_tag("D", "principal-collection-set"), elif tag in (_tag("D", "principal-collection-set"),
_tag("C", "calendar-user-address-set"), _tag("C", "calendar-user-address-set"),
_tag("CR", "addressbook-home-set"), _tag("CR", "addressbook-home-set"),
@ -377,6 +379,8 @@ def _propfind_response(path, item, props, user, write=False):
elif tag == _tag("D", "resourcetype"): elif tag == _tag("D", "resourcetype"):
# resourcetype must be returned empty for non-collection elements # resourcetype must be returned empty for non-collection elements
pass pass
elif tag == _tag("D", "getcontentlength"):
element.text = str(item.content_length)
else: else:
is404 = True is404 = True