verify.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. # This file is part of Radicale Server - Calendar Server
  2. # Copyright © 2014 Jean-Marc Martins
  3. # Copyright © 2012-2017 Guillaume Ayoub
  4. # Copyright © 2017-2018 Unrud <unrud@outlook.com>
  5. #
  6. # This library is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  18. import contextlib
  19. from radicale import pathutils, storage
  20. from radicale.log import logger
  21. class CollectionVerifyMixin:
  22. @classmethod
  23. def verify(cls):
  24. item_errors = collection_errors = 0
  25. @contextlib.contextmanager
  26. def exception_cm(sane_path, href=None):
  27. nonlocal item_errors, collection_errors
  28. try:
  29. yield
  30. except Exception as e:
  31. if href:
  32. item_errors += 1
  33. name = "item %r in %r" % (href, sane_path)
  34. else:
  35. collection_errors += 1
  36. name = "collection %r" % sane_path
  37. logger.error("Invalid %s: %s", name, e, exc_info=True)
  38. remaining_sane_paths = [""]
  39. while remaining_sane_paths:
  40. sane_path = remaining_sane_paths.pop(0)
  41. path = pathutils.unstrip_path(sane_path, True)
  42. logger.debug("Verifying collection %r", sane_path)
  43. with exception_cm(sane_path):
  44. saved_item_errors = item_errors
  45. collection = None
  46. uids = set()
  47. has_child_collections = False
  48. for item in cls.discover(path, "1", exception_cm):
  49. if not collection:
  50. collection = item
  51. collection.get_meta()
  52. continue
  53. if isinstance(item, storage.BaseCollection):
  54. has_child_collections = True
  55. remaining_sane_paths.append(item.path)
  56. elif item.uid in uids:
  57. logger.error("Invalid item %r in %r: UID conflict %r",
  58. item.href, sane_path, item.uid)
  59. else:
  60. uids.add(item.uid)
  61. logger.debug("Verified item %r in %r",
  62. item.href, sane_path)
  63. if item_errors == saved_item_errors:
  64. collection.sync()
  65. if has_child_collections and collection.get_meta("tag"):
  66. logger.error("Invalid collection %r: %r must not have "
  67. "child collections", sane_path,
  68. collection.get_meta("tag"))
  69. return item_errors == 0 and collection_errors == 0