ContentFilterPlugin.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import time
  2. import re
  3. from Plugin import PluginManager
  4. from Translate import Translate
  5. from Config import config
  6. from ContentFilterStorage import ContentFilterStorage
  7. if "_" not in locals():
  8. _ = Translate("plugins/ContentFilter/languages/")
  9. @PluginManager.registerTo("SiteManager")
  10. class SiteManagerPlugin(object):
  11. def load(self, *args, **kwargs):
  12. global filter_storage
  13. super(SiteManagerPlugin, self).load(*args, **kwargs)
  14. filter_storage = ContentFilterStorage(site_manager=self)
  15. @PluginManager.registerTo("UiWebsocket")
  16. class UiWebsocketPlugin(object):
  17. # Mute
  18. def cbMuteAdd(self, to, auth_address, cert_user_id, reason):
  19. filter_storage.file_content["mutes"][auth_address] = {
  20. "cert_user_id": cert_user_id, "reason": reason, "source": self.site.address, "date_added": time.time()
  21. }
  22. filter_storage.save()
  23. filter_storage.changeDbs(auth_address, "remove")
  24. self.response(to, "ok")
  25. def actionMuteAdd(self, to, auth_address, cert_user_id, reason):
  26. if "ADMIN" in self.getPermissions(to):
  27. self.cbMuteAdd(to, auth_address, cert_user_id, reason)
  28. else:
  29. self.cmd(
  30. "confirm",
  31. [_["Hide all content from <b>%s</b>?"] % cert_user_id, _["Mute"]],
  32. lambda (res): self.cbMuteAdd(to, auth_address, cert_user_id, reason)
  33. )
  34. def cbMuteRemove(self, to, auth_address):
  35. del filter_storage.file_content["mutes"][auth_address]
  36. filter_storage.save()
  37. filter_storage.changeDbs(auth_address, "load")
  38. self.response(to, "ok")
  39. def actionMuteRemove(self, to, auth_address):
  40. if "ADMIN" in self.getPermissions(to):
  41. self.cbMuteRemove(to, auth_address)
  42. else:
  43. self.cmd(
  44. "confirm",
  45. [_["Unmute <b>%s</b>?"] % filter_storage.file_content["mutes"][auth_address]["cert_user_id"], _["Unmute"]],
  46. lambda (res): self.cbMuteRemove(to, auth_address)
  47. )
  48. def actionMuteList(self, to):
  49. if "ADMIN" in self.getPermissions(to):
  50. self.response(to, filter_storage.file_content["mutes"])
  51. else:
  52. return self.response(to, {"error": "Forbidden: Only ADMIN sites can list mutes"})
  53. # Siteblock
  54. def actionSiteblockAdd(self, to, site_address, reason=None):
  55. if "ADMIN" not in self.getPermissions(to):
  56. return self.response(to, {"error": "Forbidden: Only ADMIN sites can add to blocklist"})
  57. filter_storage.file_content["siteblocks"][site_address] = {"date_added": time.time(), "reason": reason}
  58. filter_storage.save()
  59. self.response(to, "ok")
  60. def actionSiteblockRemove(self, to, site_address):
  61. if "ADMIN" not in self.getPermissions(to):
  62. return self.response(to, {"error": "Forbidden: Only ADMIN sites can remove from blocklist"})
  63. del filter_storage.file_content["siteblocks"][site_address]
  64. filter_storage.save()
  65. self.response(to, "ok")
  66. def actionSiteblockList(self, to):
  67. if "ADMIN" in self.getPermissions(to):
  68. self.response(to, filter_storage.file_content["siteblocks"])
  69. else:
  70. return self.response(to, {"error": "Forbidden: Only ADMIN sites can list blocklists"})
  71. # Include
  72. def actionFilterIncludeAdd(self, to, inner_path, description=None, address=None):
  73. if address:
  74. if "ADMIN" not in self.getPermissions(to):
  75. return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
  76. site = self.server.sites[address]
  77. else:
  78. address = self.site.address
  79. site = self.site
  80. if "ADMIN" in self.getPermissions(to):
  81. self.cbFilterIncludeAdd(to, True, address, inner_path, description)
  82. else:
  83. content = site.storage.loadJson(inner_path)
  84. title = _["New shared global content filter: <b>%s</b> (%s sites, %s users)"] % (
  85. inner_path, len(content.get("siteblocks", {})), len(content.get("mutes", {}))
  86. )
  87. self.cmd(
  88. "confirm",
  89. [title, "Add"],
  90. lambda (res): self.cbFilterIncludeAdd(to, res, address, inner_path, description)
  91. )
  92. def cbFilterIncludeAdd(self, to, res, address, inner_path, description):
  93. if not res:
  94. self.response(to, res)
  95. return False
  96. filter_storage.includeAdd(address, inner_path, description)
  97. self.response(to, "ok")
  98. def actionFilterIncludeRemove(self, to, inner_path, address=None):
  99. if address:
  100. if "ADMIN" not in self.getPermissions(to):
  101. return self.response(to, {"error": "Forbidden: Only ADMIN sites can manage different site include"})
  102. else:
  103. address = self.site.address
  104. key = "%s/%s" % (address, inner_path)
  105. if key not in filter_storage.file_content["includes"]:
  106. self.response(to, {"error": "Include not found"})
  107. filter_storage.includeRemove(address, inner_path)
  108. self.response(to, "ok")
  109. def actionFilterIncludeList(self, to, all_sites=False, filters=False):
  110. if all_sites and "ADMIN" not in self.getPermissions(to):
  111. return self.response(to, {"error": "Forbidden: Only ADMIN sites can list all sites includes"})
  112. back = []
  113. includes = filter_storage.file_content.get("includes", {}).values()
  114. for include in includes:
  115. if not all_sites and include["address"] != self.site.address:
  116. continue
  117. if filters:
  118. include = dict(include) # Don't modify original file_content
  119. include_site = filter_storage.site_manager.get(include["address"])
  120. if not include_site:
  121. continue
  122. content = include_site.storage.loadJson(include["inner_path"])
  123. include["mutes"] = content.get("mutes", {})
  124. include["siteblocks"] = content.get("siteblocks", {})
  125. back.append(include)
  126. self.response(to, back)
  127. @PluginManager.registerTo("SiteStorage")
  128. class SiteStoragePlugin(object):
  129. def updateDbFile(self, inner_path, file=None, cur=None):
  130. if file is not False: # File deletion always allowed
  131. # Find for bitcoin addresses in file path
  132. matches = re.findall("/(1[A-Za-z0-9]{26,35})/", inner_path)
  133. # Check if any of the adresses are in the mute list
  134. for auth_address in matches:
  135. if filter_storage.isMuted(auth_address):
  136. self.log.debug("Mute match: %s, ignoring %s" % (auth_address, inner_path))
  137. return False
  138. return super(SiteStoragePlugin, self).updateDbFile(inner_path, file=file, cur=cur)
  139. def onUpdated(self, inner_path, file=None):
  140. file_path = "%s/%s" % (self.site.address, inner_path)
  141. if file_path in filter_storage.file_content["includes"]:
  142. self.log.debug("Filter file updated: %s" % inner_path)
  143. filter_storage.includeUpdateAll()
  144. return super(SiteStoragePlugin, self).onUpdated(inner_path, file=file)
  145. @PluginManager.registerTo("UiRequest")
  146. class UiRequestPlugin(object):
  147. def actionWrapper(self, path, extra_headers=None):
  148. match = re.match("/(?P<address>[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
  149. if not match:
  150. return False
  151. address = match.group("address")
  152. if self.server.site_manager.get(address): # Site already exists
  153. return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
  154. if self.server.site_manager.isDomain(address):
  155. address = self.server.site_manager.resolveDomain(address)
  156. if filter_storage.isSiteblocked(address):
  157. site = self.server.site_manager.get(config.homepage)
  158. if not extra_headers:
  159. extra_headers = {}
  160. self.sendHeader(extra_headers=extra_headers)
  161. return iter([super(UiRequestPlugin, self).renderWrapper(
  162. site, path, "uimedia/plugins/contentfilter/blocklisted.html?address=" + address,
  163. "Blacklisted site", extra_headers, show_loadingscreen=False
  164. )])
  165. else:
  166. return super(UiRequestPlugin, self).actionWrapper(path, extra_headers)
  167. def actionUiMedia(self, path, *args, **kwargs):
  168. if path.startswith("/uimedia/plugins/contentfilter/"):
  169. file_path = path.replace("/uimedia/plugins/contentfilter/", "plugins/ContentFilter/media/")
  170. return self.actionFile(file_path)
  171. else:
  172. return super(UiRequestPlugin, self).actionUiMedia(path)