verify.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  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 StorageVerifyMixin:
  22. def verify(self):
  23. item_errors = collection_errors = 0
  24. @contextlib.contextmanager
  25. def exception_cm(sane_path, href=None):
  26. nonlocal item_errors, collection_errors
  27. try:
  28. yield
  29. except Exception as e:
  30. if href:
  31. item_errors += 1
  32. name = "item %r in %r" % (href, sane_path)
  33. else:
  34. collection_errors += 1
  35. name = "collection %r" % sane_path
  36. logger.error("Invalid %s: %s", name, e, exc_info=True)
  37. remaining_sane_paths = [""]
  38. while remaining_sane_paths:
  39. sane_path = remaining_sane_paths.pop(0)
  40. path = pathutils.unstrip_path(sane_path, True)
  41. logger.debug("Verifying collection %r", sane_path)
  42. with exception_cm(sane_path):
  43. saved_item_errors = item_errors
  44. collection = None
  45. uids = set()
  46. has_child_collections = False
  47. for item in self.discover(path, "1", exception_cm):
  48. if not collection:
  49. collection = item
  50. collection.get_meta()
  51. continue
  52. if isinstance(item, storage.BaseCollection):
  53. has_child_collections = True
  54. remaining_sane_paths.append(item.path)
  55. elif item.uid in uids:
  56. logger.error("Invalid item %r in %r: UID conflict %r",
  57. item.href, sane_path, item.uid)
  58. else:
  59. uids.add(item.uid)
  60. logger.debug("Verified item %r in %r",
  61. item.href, sane_path)
  62. if item_errors == saved_item_errors:
  63. collection.sync()
  64. if has_child_collections and collection.get_meta("tag"):
  65. logger.error("Invalid collection %r: %r must not have "
  66. "child collections", sane_path,
  67. collection.get_meta("tag"))
  68. return item_errors == 0 and collection_errors == 0