Ejemplo n.º 1
0
 def delete(self, href=None):
     if href is None:
         # Delete the collection
         parent_dir = os.path.dirname(self._filesystem_path)
         try:
             os.rmdir(self._filesystem_path)
         except OSError:
             with TemporaryDirectory(
                     prefix=".Radicale.tmp-", dir=parent_dir) as tmp:
                 os.rename(self._filesystem_path, os.path.join(
                     tmp, os.path.basename(self._filesystem_path)))
                 self._sync_directory(parent_dir)
         else:
             self._sync_directory(parent_dir)
     else:
         # Delete an item
         if not pathutils.is_safe_filesystem_path_component(href):
             raise pathutils.UnsafePathError(href)
         path = pathutils.path_to_filesystem(self._filesystem_path, href)
         if not os.path.isfile(path):
             raise storage.ComponentNotFoundError(href)
         os.remove(path)
         self._sync_directory(os.path.dirname(path))
         # Track the change
         self._update_history_etag(href, None)
         self._clean_history()
Ejemplo n.º 2
0
 def move(self, item, to_collection, to_href):
     if not pathutils.is_safe_filesystem_path_component(to_href):
         raise pathutils.UnsafePathError(to_href)
     os.replace(
         pathutils.path_to_filesystem(item.collection._filesystem_path,
                                      item.href),
         pathutils.path_to_filesystem(to_collection._filesystem_path,
                                      to_href))
     self._sync_directory(to_collection._filesystem_path)
     if item.collection._filesystem_path != to_collection._filesystem_path:
         self._sync_directory(item.collection._filesystem_path)
     # Move the item cache entry
     cache_folder = os.path.join(item.collection._filesystem_path,
                                 ".Radicale.cache", "item")
     to_cache_folder = os.path.join(to_collection._filesystem_path,
                                    ".Radicale.cache", "item")
     self._makedirs_synced(to_cache_folder)
     try:
         os.replace(os.path.join(cache_folder, item.href),
                    os.path.join(to_cache_folder, to_href))
     except FileNotFoundError:
         pass
     else:
         self._makedirs_synced(to_cache_folder)
         if cache_folder != to_cache_folder:
             self._makedirs_synced(cache_folder)
     # Track the change
     to_collection._update_history_etag(to_href, item)
     item.collection._update_history_etag(item.href, None)
     to_collection._clean_history()
     if item.collection._filesystem_path != to_collection._filesystem_path:
         item.collection._clean_history()
Ejemplo n.º 3
0
    def _upload_all_nonatomic(self, items, suffix=""):
        """Upload a new set of items.

        This takes a list of vobject items and
        uploads them nonatomic and without existence checks.

        """
        cache_folder = os.path.join(self._filesystem_path, ".Radicale.cache",
                                    "item")
        self._storage._makedirs_synced(cache_folder)
        hrefs = set()
        for item in items:
            uid = item.uid
            try:
                cache_content = self._item_cache_content(item)
            except Exception as e:
                raise ValueError(
                    "Failed to store item %r in temporary collection %r: %s" %
                    (uid, self.path, e)) from e
            href_candidate_funtions = []
            if os.name in ("nt", "posix"):
                href_candidate_funtions.append(lambda: uid if uid.lower(
                ).endswith(suffix.lower()) else uid + suffix)
            href_candidate_funtions.extend(
                (lambda: radicale_item.get_etag(uid).strip('"') + suffix,
                 lambda: radicale_item.find_available_uid(
                     hrefs.__contains__, suffix)))
            href = f = None
            while href_candidate_funtions:
                href = href_candidate_funtions.pop(0)()
                if href in hrefs:
                    continue
                if not pathutils.is_safe_filesystem_path_component(href):
                    if not href_candidate_funtions:
                        raise pathutils.UnsafePathError(href)
                    continue
                try:
                    f = open(pathutils.path_to_filesystem(
                        self._filesystem_path, href),
                             "w",
                             newline="",
                             encoding=self._encoding)
                    break
                except OSError as e:
                    if href_candidate_funtions and (
                            os.name == "posix" and e.errno == 22
                            or os.name == "nt" and e.errno == 123):
                        continue
                    raise
            with f:
                f.write(item.serialize())
                f.flush()
                self._storage._fsync(f)
            hrefs.add(href)
            with open(os.path.join(cache_folder, href), "wb") as f:
                pickle.dump(cache_content, f)
                f.flush()
                self._storage._fsync(f)
        self._storage._sync_directory(cache_folder)
        self._storage._sync_directory(self._filesystem_path)
Ejemplo n.º 4
0
 def upload(self, href, item):
     if not pathutils.is_safe_filesystem_path_component(href):
         raise pathutils.UnsafePathError(href)
     try:
         self._store_item_cache(href, item)
     except Exception as e:
         raise ValueError("Failed to store item %r in collection %r: %s" %
                          (href, self.path, e)) from e
     path = pathutils.path_to_filesystem(self._filesystem_path, href)
     with self._atomic_write(path, newline="") as fd:
         fd.write(item.serialize())
     # Clean the cache after the actual item is stored, or the cache entry
     # will be removed again.
     self._clean_item_cache()
     # Track the change
     self._update_history_etag(href, item)
     self._clean_history()
     return self._get(href, verify_href=False)
Ejemplo n.º 5
0
 def replace_fn(source, target):
     nonlocal href
     while href_candidates:
         href = href_candidates.pop(0)()
         if href in hrefs:
             continue
         if not pathutils.is_safe_filesystem_path_component(href):
             if not href_candidates:
                 raise pathutils.UnsafePathError(href)
             continue
         try:
             return os.replace(source, pathutils.path_to_filesystem(
                 self._filesystem_path, href))
         except OSError as e:
             if href_candidates and (
                     os.name == "posix" and e.errno == 22 or
                     os.name == "nt" and e.errno == 123):
                 continue
             raise
Ejemplo n.º 6
0
 def _get(self, href, verify_href=True):
     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)
         except ValueError as e:
             logger.debug(
                 "Can't translate name %r safely to filesystem in %r: %s",
                 href,
                 self.path,
                 e,
                 exc_info=True)
             return None
     else:
         path = os.path.join(self._filesystem_path, href)
     try:
         with open(path, "rb") as f:
             raw_text = f.read()
     except (FileNotFoundError, IsADirectoryError):
         return None
     except PermissionError:
         # Windows raises ``PermissionError`` when ``path`` is a directory
         if (os.name == "nt" 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:
         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._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:
                 try:
                     vobject_items = tuple(
                         vobject.readComponents(
                             raw_text.decode(self._encoding)))
                     radicale_item.check_and_sanitize_items(
                         vobject_items, tag=self.get_meta("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)
                 except Exception as e:
                     raise RuntimeError("Failed to load item %r in %r: %s" %
                                        (href, self.path, e)) from e
                 # Clean cache entries once after the data in the file
                 # system was edited externally.
                 if not self._item_cache_cleaned:
                     self._item_cache_cleaned = True
                     self._clean_item_cache()
     last_modified = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                   time.gmtime(os.path.getmtime(path)))
     # 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))