1
0
Fork 0
mirror of https://github.com/Kozea/Radicale.git synced 2025-06-26 16:45:52 +00:00
Radicale/radicale/storage/multifilesystem/discover.py

118 lines
4.4 KiB
Python
Raw Permalink Normal View History

2021-12-08 21:45:42 +01:00
# This file is part of Radicale - CalDAV and CardDAV server
2018-09-04 03:33:50 +02:00
# Copyright © 2014 Jean-Marc Martins
# Copyright © 2012-2017 Guillaume Ayoub
2019-06-17 04:13:24 +02:00
# Copyright © 2017-2018 Unrud <unrud@outlook.com>
2018-09-04 03:33:50 +02:00
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
2024-09-22 18:57:48 +02:00
import base64
2018-09-04 03:33:50 +02:00
import os
2019-06-15 09:01:55 +02:00
import posixpath
from typing import Callable, ContextManager, Iterator, Optional, Set, cast
2018-09-04 03:33:50 +02:00
2021-07-26 20:56:47 +02:00
from radicale import pathutils, types
2018-09-04 03:33:50 +02:00
from radicale.log import logger
2021-07-26 20:56:47 +02:00
from radicale.storage import multifilesystem
from radicale.storage.multifilesystem.base import StorageBase
2018-09-04 03:33:50 +02:00
2021-07-26 20:56:47 +02:00
@types.contextmanager
def _null_child_context_manager(path: str,
href: Optional[str]) -> Iterator[None]:
yield
2021-07-26 20:56:47 +02:00
class StoragePartDiscover(StorageBase):
def discover(
self, path: str, depth: str = "0",
child_context_manager: Optional[
Callable[[str, Optional[str]], ContextManager[None]]] = None,
user_groups: Set[str] = set([])
2021-07-26 20:56:47 +02:00
) -> Iterator[types.CollectionOrItem]:
# assert isinstance(self, multifilesystem.Storage)
if child_context_manager is None:
child_context_manager = _null_child_context_manager
2018-09-04 03:33:50 +02:00
# Path should already be sanitized
sane_path = pathutils.strip_path(path)
attributes = sane_path.split("/") if sane_path else []
folder = self._get_collection_root_folder()
2018-09-04 03:33:50 +02:00
# Create the root collection
self._makedirs_synced(folder)
2018-09-04 03:33:50 +02:00
try:
filesystem_path = pathutils.path_to_filesystem(folder, sane_path)
except ValueError as e:
# Path is unsafe
logger.debug("Unsafe path %r requested from storage: %s",
sane_path, e, exc_info=True)
return
# Check if the path exists and if it leads to a collection or an item
2021-07-26 20:56:47 +02:00
href: Optional[str]
2018-09-04 03:33:50 +02:00
if not os.path.isdir(filesystem_path):
if attributes and os.path.isfile(filesystem_path):
href = attributes.pop()
else:
return
else:
href = None
sane_path = "/".join(attributes)
collection = self._collection_class(
2021-07-26 20:56:47 +02:00
cast(multifilesystem.Storage, self),
pathutils.unstrip_path(sane_path, True))
2018-09-04 03:33:50 +02:00
if href:
2021-07-26 20:56:47 +02:00
item = collection._get(href)
if item is not None:
yield item
2018-09-04 03:33:50 +02:00
return
yield collection
if depth == "0":
return
for href in collection._list():
with child_context_manager(sane_path, href):
2021-07-26 20:56:47 +02:00
item = collection._get(href)
if item is not None:
yield item
2018-09-04 03:33:50 +02:00
for entry in os.scandir(filesystem_path):
if not entry.is_dir():
continue
href = entry.name
if not pathutils.is_safe_filesystem_path_component(href):
if not href.startswith(".Radicale"):
logger.debug("Skipping collection %r in %r",
href, sane_path)
continue
sane_child_path = posixpath.join(sane_path, href)
child_path = pathutils.unstrip_path(sane_child_path, True)
2021-07-26 20:56:47 +02:00
with child_context_manager(sane_child_path, None):
yield self._collection_class(
cast(multifilesystem.Storage, self), child_path)
for group in user_groups:
href = base64.b64encode(group.encode('utf-8')).decode('ascii')
logger.debug(f"searching for group calendar {group} {href}")
sane_child_path = f"GROUPS/{href}"
if not os.path.isdir(pathutils.path_to_filesystem(folder, sane_child_path)):
continue
child_path = f"/GROUPS/{href}/"
with child_context_manager(sane_child_path, None):
yield self._collection_class(
cast(multifilesystem.Storage, self), child_path)