ContentFilterStorage.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import os
  2. import json
  3. import logging
  4. import collections
  5. import time
  6. import hashlib
  7. from Debug import Debug
  8. from Plugin import PluginManager
  9. from Config import config
  10. from util import helper
  11. class ContentFilterStorage(object):
  12. def __init__(self, site_manager):
  13. self.log = logging.getLogger("ContentFilterStorage")
  14. self.file_path = "%s/filters.json" % config.data_dir
  15. self.site_manager = site_manager
  16. self.file_content = self.load()
  17. # Set default values for filters.json
  18. if not self.file_content:
  19. self.file_content = {}
  20. # Site blacklist renamed to site blocks
  21. if "site_blacklist" in self.file_content:
  22. self.file_content["siteblocks"] = self.file_content["site_blacklist"]
  23. del self.file_content["site_blacklist"]
  24. for key in ["mutes", "siteblocks", "includes"]:
  25. if key not in self.file_content:
  26. self.file_content[key] = {}
  27. self.include_filters = collections.defaultdict(set) # Merged list of mutes and blacklists from all include
  28. self.includeUpdateAll(update_site_dbs=False)
  29. def load(self):
  30. # Rename previously used mutes.json -> filters.json
  31. if os.path.isfile("%s/mutes.json" % config.data_dir):
  32. self.log.info("Renaming mutes.json to filters.json...")
  33. os.rename("%s/mutes.json" % config.data_dir, self.file_path)
  34. if os.path.isfile(self.file_path):
  35. try:
  36. return json.load(open(self.file_path))
  37. except Exception as err:
  38. self.log.error("Error loading filters.json: %s" % err)
  39. return None
  40. else:
  41. return None
  42. def includeUpdateAll(self, update_site_dbs=True):
  43. s = time.time()
  44. new_include_filters = collections.defaultdict(set)
  45. # Load all include files data into a merged set
  46. for include_path in self.file_content["includes"]:
  47. address, inner_path = include_path.split("/", 1)
  48. try:
  49. content = self.site_manager.get(address).storage.loadJson(inner_path)
  50. except Exception as err:
  51. self.log.warning(
  52. "Error loading include %s: %s" %
  53. (include_path, Debug.formatException(err))
  54. )
  55. continue
  56. for key, val in content.items():
  57. if type(val) is not dict:
  58. continue
  59. new_include_filters[key].update(val.keys())
  60. mutes_added = new_include_filters["mutes"].difference(self.include_filters["mutes"])
  61. mutes_removed = self.include_filters["mutes"].difference(new_include_filters["mutes"])
  62. self.include_filters = new_include_filters
  63. if update_site_dbs:
  64. for auth_address in mutes_added:
  65. self.changeDbs(auth_address, "remove")
  66. for auth_address in mutes_removed:
  67. if not self.isMuted(auth_address):
  68. self.changeDbs(auth_address, "load")
  69. num_mutes = len(self.include_filters["mutes"])
  70. num_siteblocks = len(self.include_filters["siteblocks"])
  71. self.log.debug(
  72. "Loaded %s mutes, %s blocked sites from %s includes in %.3fs" %
  73. (num_mutes, num_siteblocks, len(self.file_content["includes"]), time.time() - s)
  74. )
  75. def includeAdd(self, address, inner_path, description=None):
  76. self.file_content["includes"]["%s/%s" % (address, inner_path)] = {
  77. "date_added": time.time(),
  78. "address": address,
  79. "description": description,
  80. "inner_path": inner_path
  81. }
  82. self.includeUpdateAll()
  83. self.save()
  84. def includeRemove(self, address, inner_path):
  85. del self.file_content["includes"]["%s/%s" % (address, inner_path)]
  86. self.includeUpdateAll()
  87. self.save()
  88. def save(self):
  89. s = time.time()
  90. helper.atomicWrite(self.file_path, json.dumps(self.file_content, indent=2, sort_keys=True).encode("utf8"))
  91. self.log.debug("Saved in %.3fs" % (time.time() - s))
  92. def isMuted(self, auth_address):
  93. if auth_address in self.file_content["mutes"] or auth_address in self.include_filters["mutes"]:
  94. return True
  95. else:
  96. return False
  97. def getSiteAddressHashed(self, address):
  98. return "0x" + hashlib.sha256(address.encode("ascii")).hexdigest()
  99. def isSiteblocked(self, address):
  100. if address in self.file_content["siteblocks"] or address in self.include_filters["siteblocks"]:
  101. return True
  102. return False
  103. def getSiteblockDetails(self, address):
  104. details = self.file_content["siteblocks"].get(address)
  105. if not details:
  106. address_sha256 = self.getSiteAddressHashed(address)
  107. details = self.file_content["siteblocks"].get(address_sha256)
  108. if not details:
  109. includes = self.file_content.get("includes", {}).values()
  110. for include in includes:
  111. include_site = self.site_manager.get(include["address"])
  112. if not include_site:
  113. continue
  114. content = include_site.storage.loadJson(include["inner_path"])
  115. details = content.get("siteblocks", {}).get(address)
  116. if details:
  117. details["include"] = include
  118. break
  119. return details
  120. # Search and remove or readd files of an user
  121. def changeDbs(self, auth_address, action):
  122. self.log.debug("Mute action %s on user %s" % (action, auth_address))
  123. res = list(self.site_manager.list().values())[0].content_manager.contents.db.execute(
  124. "SELECT * FROM content LEFT JOIN site USING (site_id) WHERE inner_path LIKE :inner_path",
  125. {"inner_path": "%%/%s/%%" % auth_address}
  126. )
  127. for row in res:
  128. site = self.site_manager.sites.get(row["address"])
  129. if not site:
  130. continue
  131. dir_inner_path = helper.getDirname(row["inner_path"])
  132. for file_name in site.storage.walk(dir_inner_path):
  133. if action == "remove":
  134. site.storage.onUpdated(dir_inner_path + file_name, False)
  135. else:
  136. site.storage.onUpdated(dir_inner_path + file_name)
  137. site.onFileDone(dir_inner_path + file_name)