Improve error handling

* Check the configuration file for errors (check option names and basic type checking).
  * Perform basic type checking on command line arguments.
  * Only print stack traces in debug mode.
  * Include much more information in error messages (e.g. include the path of invalid files).
  * Send Bad Request to clients for invalid XML requests or iCalendar data.
  * Change the log level of some messages.
This commit is contained in:
Unrud
2017-05-31 11:08:32 +02:00
parent 824835bcd4
commit c9664137a5
8 changed files with 362 additions and 184 deletions

View File

@ -91,7 +91,12 @@ def load(configuration, logger):
if storage_type == "multifilesystem":
collection_class = Collection
else:
collection_class = import_module(storage_type).Collection
try:
collection_class = import_module(storage_type).Collection
except ImportError as e:
raise RuntimeError("Storage module %r not found" %
storage_type) from e
logger.info("Storage type is %r", storage_type)
class CollectionCopy(collection_class):
"""Collection copy, avoids overriding the original class attributes."""
@ -184,25 +189,25 @@ def path_to_filesystem(root, *paths):
class UnsafePathError(ValueError):
def __init__(self, path):
message = "Can't translate name safely to filesystem: %s" % path
message = "Can't translate name safely to filesystem: %r" % path
super().__init__(message)
class CollidingPathError(ValueError):
def __init__(self, path):
message = "File name collision: %s" % path
message = "File name collision: %r" % path
super().__init__(message)
class ComponentExistsError(ValueError):
def __init__(self, path):
message = "Component already exists: %s" % path
message = "Component already exists: %r" % path
super().__init__(message)
class ComponentNotFoundError(ValueError):
def __init__(self, path):
message = "Component doesn't exist: %s" % path
message = "Component doesn't exist: %r" % path
super().__init__(message)
@ -510,7 +515,8 @@ class Collection(BaseCollection):
for href in os.listdir(filesystem_path):
if not is_safe_filesystem_path_component(href):
if not href.startswith(".Radicale"):
cls.logger.debug("Skipping collection: %s", href)
cls.logger.debug("Skipping collection %r in %r", href,
path)
continue
child_filesystem_path = path_to_filesystem(filesystem_path, href)
if os.path.isdir(child_filesystem_path):
@ -621,7 +627,8 @@ class Collection(BaseCollection):
for href in os.listdir(self._filesystem_path):
if not is_safe_filesystem_path_component(href):
if not href.startswith(".Radicale"):
self.logger.debug("Skipping component: %s", href)
self.logger.debug(
"Skipping item %r in %r", href, self.path)
continue
path = os.path.join(self._filesystem_path, href)
if os.path.isfile(path):
@ -631,8 +638,8 @@ class Collection(BaseCollection):
if not href:
return None
if not is_safe_filesystem_path_component(href):
self.logger.debug(
"Can't translate name safely to filesystem: %s", href)
self.logger.debug("Can't translate name %r safely to filesystem "
"in %r", href, self.path)
return None
path = path_to_filesystem(self._filesystem_path, href)
if not os.path.isfile(path):
@ -644,9 +651,9 @@ class Collection(BaseCollection):
time.gmtime(os.path.getmtime(path)))
try:
item = vobject.readOne(text)
except Exception:
self.logger.error("Failed to parse component: %s", href)
raise
except Exception as e:
raise RuntimeError("Failed to parse item %r in %r" %
(href, self.path)) from e
return Item(self, item, href, last_modified)
def upload(self, href, vobject_item):
@ -685,7 +692,11 @@ class Collection(BaseCollection):
def get_meta(self, key=None):
if os.path.exists(self._props_path):
with open(self._props_path, encoding=self.encoding) as f:
meta = json.load(f)
try:
meta = json.load(f)
except ValueError as e:
raise RuntimeError("Failed to load properties of collect"
"ion %r: %s" % (self.path, e)) from e
return meta.get(key) if key else meta
def set_meta(self, props):
@ -715,8 +726,8 @@ class Collection(BaseCollection):
items.append(self.get(href).item)
time_end = datetime.datetime.now()
self.logger.info(
"Collection read %d items in %s sec from %s", len(items),
(time_end - time_begin).total_seconds(), self._filesystem_path)
"Read %d items in %.3f seconds from %r", len(items),
(time_end - time_begin).total_seconds(), self.path)
if self.get_meta("tag") == "VCALENDAR":
collection = vobject.iCalendar()
for item in items: