Synced with origin
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# This file is part of Radicale Server - Calendar Server
|
||||
# This file is part of Radicale - CalDAV and CardDAV server
|
||||
# Copyright © 2014 Jean-Marc Martins
|
||||
# Copyright © 2012-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2018 Unrud <unrud@outlook.com>
|
||||
@@ -17,21 +17,30 @@
|
||||
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Iterable, Iterator, Optional, Tuple
|
||||
|
||||
import vobject
|
||||
|
||||
from radicale import item as radicale_item
|
||||
import radicale.item as radicale_item
|
||||
from radicale import pathutils
|
||||
from radicale.log import logger
|
||||
from radicale.storage import multifilesystem
|
||||
from radicale.storage.multifilesystem.base import CollectionBase
|
||||
from radicale.storage.multifilesystem.cache import CollectionPartCache
|
||||
from radicale.storage.multifilesystem.lock import CollectionPartLock
|
||||
|
||||
|
||||
class CollectionGetMixin:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
class CollectionPartGet(CollectionPartCache, CollectionPartLock,
|
||||
CollectionBase):
|
||||
|
||||
_item_cache_cleaned: bool
|
||||
|
||||
def __init__(self, storage_: "multifilesystem.Storage", path: str,
|
||||
filesystem_path: Optional[str] = None) -> None:
|
||||
super().__init__(storage_, path, filesystem_path)
|
||||
self._item_cache_cleaned = False
|
||||
|
||||
def _list(self):
|
||||
def _list(self) -> Iterator[str]:
|
||||
for entry in os.scandir(self._filesystem_path):
|
||||
if not entry.is_file():
|
||||
continue
|
||||
@@ -42,13 +51,14 @@ class CollectionGetMixin:
|
||||
continue
|
||||
yield href
|
||||
|
||||
def _get(self, href, verify_href=True):
|
||||
def _get(self, href: str, verify_href: bool = True
|
||||
) -> Optional[radicale_item.Item]:
|
||||
if verify_href:
|
||||
try:
|
||||
if not pathutils.is_safe_filesystem_path_component(href):
|
||||
raise pathutils.UnsafePathError(href)
|
||||
path = pathutils.path_to_filesystem(
|
||||
self._filesystem_path, href)
|
||||
path = pathutils.path_to_filesystem(self._filesystem_path,
|
||||
href)
|
||||
except ValueError as e:
|
||||
logger.debug(
|
||||
"Can't translate name %r safely to filesystem in %r: %s",
|
||||
@@ -63,36 +73,33 @@ class CollectionGetMixin:
|
||||
return None
|
||||
except PermissionError:
|
||||
# Windows raises ``PermissionError`` when ``path`` is a directory
|
||||
if (os.name == "nt" and
|
||||
if (sys.platform == "win32" and
|
||||
os.path.isdir(path) and os.access(path, os.R_OK)):
|
||||
return None
|
||||
raise
|
||||
# The hash of the component in the file system. This is used to check,
|
||||
# if the entry in the cache is still valid.
|
||||
input_hash = self._item_cache_hash(raw_text)
|
||||
cache_hash, uid, etag, text, name, tag, start, end = \
|
||||
self._load_item_cache(href, input_hash)
|
||||
if input_hash != cache_hash:
|
||||
cache_hash = self._item_cache_hash(raw_text)
|
||||
cache_content = self._load_item_cache(href, cache_hash)
|
||||
if cache_content is None:
|
||||
with self._acquire_cache_lock("item"):
|
||||
# Lock the item cache to prevent multpile processes from
|
||||
# generating the same data in parallel.
|
||||
# This improves the performance for multiple requests.
|
||||
if self._storage._lock.locked == "r":
|
||||
# Check if another process created the file in the meantime
|
||||
cache_hash, uid, etag, text, name, tag, start, end = \
|
||||
self._load_item_cache(href, input_hash)
|
||||
if input_hash != cache_hash:
|
||||
cache_content = self._load_item_cache(href, cache_hash)
|
||||
if cache_content is None:
|
||||
try:
|
||||
vobject_items = tuple(vobject.readComponents(
|
||||
raw_text.decode(self._encoding)))
|
||||
vobject_items = radicale_item.read_components(
|
||||
raw_text.decode(self._encoding))
|
||||
radicale_item.check_and_sanitize_items(
|
||||
vobject_items, tag=self.get_meta("tag"))
|
||||
vobject_items, tag=self.tag)
|
||||
vobject_item, = vobject_items
|
||||
temp_item = radicale_item.Item(
|
||||
collection=self, vobject_item=vobject_item)
|
||||
cache_hash, uid, etag, text, name, tag, start, end = \
|
||||
self._store_item_cache(
|
||||
href, temp_item, input_hash)
|
||||
cache_content = self._store_item_cache(
|
||||
href, temp_item, cache_hash)
|
||||
except Exception as e:
|
||||
raise RuntimeError("Failed to load item %r in %r: %s" %
|
||||
(href, self.path, e)) from e
|
||||
@@ -107,11 +114,14 @@ class CollectionGetMixin:
|
||||
# Don't keep reference to ``vobject_item``, because it requires a lot
|
||||
# of memory.
|
||||
return radicale_item.Item(
|
||||
collection=self, href=href, last_modified=last_modified, etag=etag,
|
||||
text=text, uid=uid, name=name, component_name=tag,
|
||||
time_range=(start, end))
|
||||
collection=self, href=href, last_modified=last_modified,
|
||||
etag=cache_content.etag, text=cache_content.text,
|
||||
uid=cache_content.uid, name=cache_content.name,
|
||||
component_name=cache_content.tag,
|
||||
time_range=(cache_content.start, cache_content.end))
|
||||
|
||||
def get_multi(self, hrefs):
|
||||
def get_multi(self, hrefs: Iterable[str]
|
||||
) -> Iterator[Tuple[str, Optional[radicale_item.Item]]]:
|
||||
# It's faster to check for file name collissions here, because
|
||||
# we only need to call os.listdir once.
|
||||
files = None
|
||||
@@ -123,13 +133,16 @@ class CollectionGetMixin:
|
||||
path = os.path.join(self._filesystem_path, href)
|
||||
if (not pathutils.is_safe_filesystem_path_component(href) or
|
||||
href not in files and os.path.lexists(path)):
|
||||
logger.debug(
|
||||
"Can't translate name safely to filesystem: %r", href)
|
||||
logger.debug("Can't translate name safely to filesystem: %r",
|
||||
href)
|
||||
yield (href, None)
|
||||
else:
|
||||
yield (href, self._get(href, verify_href=False))
|
||||
|
||||
def get_all(self):
|
||||
# We don't need to check for collissions, because the the file names
|
||||
# are from os.listdir.
|
||||
return (self._get(href, verify_href=False) for href in self._list())
|
||||
def get_all(self) -> Iterator[radicale_item.Item]:
|
||||
for href in self._list():
|
||||
# We don't need to check for collissions, because the file names
|
||||
# are from os.listdir.
|
||||
item = self._get(href, verify_href=False)
|
||||
if item is not None:
|
||||
yield item
|
||||
|
||||
Reference in New Issue
Block a user