瀏覽代碼

Clean the storage module

Guillaume Ayoub 10 年之前
父節點
當前提交
2408097ab9
共有 1 個文件被更改,包括 26 次插入63 次删除
  1. 26 63
      radicale/storage.py

+ 26 - 63
radicale/storage.py

@@ -18,15 +18,13 @@
 """
 Storage backends.
 
-This module loads the storage backend, according to the storage
-configuration.
+This module loads the storage backend, according to the storage configuration.
 
 Default storage uses one folder per collection and one file per collection
 entry.
 
 """
 
-import hashlib
 import json
 import os
 import posixpath
@@ -34,6 +32,8 @@ import shutil
 import sys
 import time
 from contextlib import contextmanager
+from hashlib import md5
+from random import randint
 from uuid import uuid4
 
 import vobject
@@ -54,6 +54,8 @@ def _load():
 
 FOLDER = os.path.expanduser(config.get("storage", "filesystem_folder"))
 FILESYSTEM_ENCODING = sys.getfilesystemencoding()
+STORAGE_ENCODING = config.get("encoding", "stock")
+
 
 def serialize(tag, headers=(), items=()):
     """Return a text corresponding to given collection ``tag``.
@@ -92,47 +94,15 @@ def sanitize_path(path):
     return new_path + trailing_slash
 
 
-def clean_name(name):
-    """Clean an item name by removing slashes and leading/ending brackets."""
-    # Remove leading and ending brackets that may have been put by Outlook
-    name = name.strip("{}")
-    # Remove slashes, mostly unwanted when saving on filesystems
-    name = name.replace("/", "_")
-    return name
-
-
-def is_safe_path_component(path):
-    """Check if path is a single component of a POSIX path.
-
-    Check that the path is safe to join too.
-
-    """
-    if not path:
-        return False
-    if posixpath.split(path)[0]:
-        return False
-    if path in (".", ".."):
-        return False
-    return True
-
-
 def is_safe_filesystem_path_component(path):
     """Check if path is a single component of a filesystem path.
 
     Check that the path is safe to join too.
 
     """
-    if not path:
-        return False
-    drive, _ = os.path.splitdrive(path)
-    if drive:
-        return False
-    head, _ = os.path.split(path)
-    if head:
-        return False
-    if path in (os.curdir, os.pardir):
-        return False
-    return True
+    return (
+        path and not os.path.splitdrive(path)[0] and
+        not os.path.split(path)[0] and path not in (os.curdir, os.pardir))
 
 
 def path_to_filesystem(path):
@@ -154,14 +124,6 @@ def path_to_filesystem(path):
     return safe_path
 
 
-@contextmanager
-def _open(path, mode="r"):
-    """Open a file at ``path`` with encoding set in the configuration."""
-    abs_path = os.path.join(FOLDER, path.replace("/", os.sep))
-    with open(abs_path, mode, encoding=config.get("encoding", "stock")) as fd:
-        yield fd
-
-
 class Item(object):
     """Internal iCal item."""
     def __init__(self, text, name=None):
@@ -189,7 +151,9 @@ class Item(object):
                         break
 
         if self._name:
-            self._name = clean_name(self._name)
+            # Leading and ending brackets that may have been put by Outlook.
+            # Slashes are mostly unwanted when saving collections on disk.
+            self._name = self._name.strip("{}").replace("/", "_")
         else:
             self._name = uuid4().hex
 
@@ -210,9 +174,9 @@ class Item(object):
         Etag is mainly used to know if an item has changed.
 
         """
-        md5 = hashlib.md5()
-        md5.update(self.text.encode("utf-8"))
-        return '"%s"' % md5.hexdigest()
+        etag = md5()
+        etag.update(self.text.encode("utf-8"))
+        return '"%s"' % etag.hexdigest()
 
     @property
     def name(self):
@@ -407,7 +371,7 @@ class Collection:
                     "skipping component: %s", name)
                 continue
             filename = os.path.join(self._filesystem_path, name)
-            with _open(filename, "w") as fd:
+            with open(filename, "w", encoding=STORAGE_ENCODING) as fd:
                 fd.write(component.text)
 
     @property
@@ -447,7 +411,7 @@ class Collection:
         for filename in filenames:
             path = os.path.join(self._filesystem_path, filename)
             try:
-                with _open(path) as fd:
+                with open(path, encoding=STORAGE_ENCODING) as fd:
                     items.update(self._parse(fd.read(), components))
             except (OSError, IOError) as e:
                 log.LOGGER.warning(
@@ -460,15 +424,14 @@ class Collection:
     def children(cls, path):
         filesystem_path = path_to_filesystem(path)
         _, directories, files = next(os.walk(filesystem_path))
-        for filename in directories + files:
-            # make sure that the local filename can be translated
-            # into an internal path
-            if not is_safe_path_component(filename):
-                log.LOGGER.debug("Skipping unsupported filename: %s", filename)
+        for path in directories + files:
+            # Check that the local path can be translated into an internal path
+            if not path or posixpath.split(path)[0] or path in (".", ".."):
+                log.LOGGER.debug("Skipping unsupported filename: %s", path)
                 continue
-            rel_filename = posixpath.join(path, filename)
-            if cls.is_node(rel_filename) or cls.is_leaf(rel_filename):
-                yield cls(rel_filename)
+            relative_path = posixpath.join(path, path)
+            if cls.is_node(relative_path) or cls.is_leaf(relative_path):
+                yield cls(relative_path)
 
     @classmethod
     def is_node(cls, path):
@@ -567,9 +530,9 @@ class Collection:
     @property
     def etag(self):
         """Etag from collection."""
-        md5 = hashlib.md5()
-        md5.update(self.text.encode("utf-8"))
-        return '"%s"' % md5.hexdigest()
+        etag = md5()
+        etag.update(self.text.encode("utf-8"))
+        return '"%s"' % etag.hexdigest()
 
     @property
     def name(self):