UiWebsocketPlugin.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import re
  2. import time
  3. import cgi
  4. import gevent
  5. from Plugin import PluginManager
  6. from Config import config
  7. from util import helper
  8. @PluginManager.registerTo("UiWebsocket")
  9. class UiWebsocketPlugin(object):
  10. def __init__(self, *args, **kwargs):
  11. self.time_peer_numbers_updated = 0
  12. super(UiWebsocketPlugin, self).__init__(*args, **kwargs)
  13. def actionFileWrite(self, to, inner_path, *args, **kwargs):
  14. super(UiWebsocketPlugin, self).actionFileWrite(to, inner_path, *args, **kwargs)
  15. # Add file to content.db and set it as pinned
  16. content_db = self.site.content_manager.contents.db
  17. content_db.my_optional_files[self.site.address + "/" + inner_path] = time.time()
  18. if len(content_db.my_optional_files) > 50: # Keep only last 50
  19. oldest_key = min(
  20. content_db.my_optional_files.iterkeys(),
  21. key=(lambda key: content_db.my_optional_files[key])
  22. )
  23. del content_db.my_optional_files[oldest_key]
  24. def updatePeerNumbers(self):
  25. content_db = self.site.content_manager.contents.db
  26. content_db.updatePeerNumbers()
  27. self.site.updateWebsocket(peernumber_updated=True)
  28. # Optional file functions
  29. def actionOptionalFileList(self, to, address=None, orderby="time_downloaded DESC", limit=10):
  30. if not address:
  31. address = self.site.address
  32. # Update peer numbers if necessary
  33. content_db = self.site.content_manager.contents.db
  34. if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
  35. # Start in new thread to avoid blocking
  36. self.time_peer_numbers_updated = time.time()
  37. gevent.spawn(self.updatePeerNumbers)
  38. if address != self.site.address and "ADMIN" not in self.site.settings["permissions"]:
  39. return self.response(to, "optionalSiteInfo not allowed on this site")
  40. if not all([re.match("^[a-z_*/+-]+( DESC| ASC|)$", part.strip()) for part in orderby.split(",")]):
  41. return self.response(to, "Invalid order_by")
  42. if type(limit) != int:
  43. return self.response(to, "Invalid limit")
  44. back = []
  45. content_db = self.site.content_manager.contents.db
  46. site_id = content_db.site_ids[address]
  47. query = "SELECT * FROM file_optional WHERE site_id = %s AND is_downloaded = 1 ORDER BY %s LIMIT %s" % (site_id, orderby, limit)
  48. for row in content_db.execute(query):
  49. back.append(dict(row))
  50. self.response(to, back)
  51. def actionOptionalFileInfo(self, to, inner_path):
  52. content_db = self.site.content_manager.contents.db
  53. site_id = content_db.site_ids[self.site.address]
  54. # Update peer numbers if necessary
  55. if time.time() - content_db.time_peer_numbers_updated > 60 * 1 and time.time() - self.time_peer_numbers_updated > 60 * 5:
  56. # Start in new thread to avoid blocking
  57. self.time_peer_numbers_updated = time.time()
  58. gevent.spawn(self.updatePeerNumbers)
  59. query = "SELECT * FROM file_optional WHERE site_id = :site_id AND inner_path = :inner_path LIMIT 1"
  60. res = content_db.execute(query, {"site_id": site_id, "inner_path": inner_path})
  61. row = next(res, None)
  62. if row:
  63. self.response(to, dict(row))
  64. else:
  65. self.response(to, None)
  66. def setPin(self, inner_path, is_pinned, address=None):
  67. if not address:
  68. address = self.site.address
  69. if not self.hasSitePermission(address):
  70. return {"error": "Forbidden"}
  71. site = self.server.sites[address]
  72. content_db = site.content_manager.contents.db
  73. site_id = content_db.site_ids[site.address]
  74. content_db.execute("UPDATE file_optional SET is_pinned = %s WHERE ?" % is_pinned, {"site_id": site_id, "inner_path": inner_path})
  75. return "ok"
  76. def actionOptionalFilePin(self, to, inner_path, address=None):
  77. back = self.setPin(inner_path, 1, address)
  78. if back == "ok":
  79. self.cmd("notification", ["done", "Pinned %s files" % len(inner_path) if type(inner_path) is list else 1, 5000])
  80. self.response(to, back)
  81. def actionOptionalFileUnpin(self, to, inner_path, address=None):
  82. back = self.setPin(inner_path, 0, address)
  83. if back == "ok":
  84. self.cmd("notification", ["done", "Removed pin from %s files" % len(inner_path) if type(inner_path) is list else 1, 5000])
  85. self.response(to, back)
  86. def actionOptionalFileDelete(self, to, inner_path, address=None):
  87. if not address:
  88. address = self.site.address
  89. if not self.hasSitePermission(address):
  90. return self.response(to, {"error": "Forbidden"})
  91. site = self.server.sites[address]
  92. content_db = site.content_manager.contents.db
  93. site_id = content_db.site_ids[site.address]
  94. res = content_db.execute("SELECT * FROM file_optional WHERE ? LIMIT 1", {"site_id": site_id, "inner_path": inner_path})
  95. row = next(res, None)
  96. if not row:
  97. return self.response(to, {"error": "Not found in content.db"})
  98. removed = site.content_manager.optionalRemove(inner_path, row["hash_id"], row["size"])
  99. # if not removed:
  100. # return self.response(to, {"error": "Not found in hash_id: %s" % row["hash_id"]})
  101. content_db.execute("UPDATE file_optional SET is_downloaded = 0, is_pinned = 0, peer = peer - 1 WHERE ?", {"site_id": site_id, "inner_path": inner_path})
  102. try:
  103. site.storage.delete(inner_path)
  104. except Exception, err:
  105. return self.response(to, {"error": "File delete error: %s" % err})
  106. self.response(to, "ok")
  107. # Limit functions
  108. def actionOptionalLimitStats(self, to):
  109. if "ADMIN" not in self.site.settings["permissions"]:
  110. return self.response(to, "Forbidden")
  111. back = {}
  112. back["limit"] = config.optional_limit
  113. back["used"] = self.site.content_manager.contents.db.execute(
  114. "SELECT SUM(size) FROM file_optional WHERE is_downloaded = 1 AND is_pinned = 0"
  115. ).fetchone()[0]
  116. back["free"] = helper.getFreeSpace()
  117. self.response(to, back)
  118. def actionOptionalLimitSet(self, to, limit):
  119. if "ADMIN" not in self.site.settings["permissions"]:
  120. return self.response(to, {"error": "Forbidden"})
  121. config.optional_limit = re.sub("\.0+$", "", limit) # Remove unnecessary digits from end
  122. config.saveValue("optional_limit", limit)
  123. self.response(to, "ok")
  124. # Distribute help functions
  125. def actionOptionalHelpList(self, to, address=None):
  126. if not address:
  127. address = self.site.address
  128. if not self.hasSitePermission(address):
  129. return self.response(to, {"error": "Forbidden"})
  130. site = self.server.sites[address]
  131. self.response(to, site.settings.get("optional_help", {}))
  132. def actionOptionalHelp(self, to, directory, title, address=None):
  133. if not address:
  134. address = self.site.address
  135. if not self.hasSitePermission(address):
  136. return self.response(to, {"error": "Forbidden"})
  137. site = self.server.sites[address]
  138. content_db = site.content_manager.contents.db
  139. site_id = content_db.site_ids[address]
  140. if "optional_help" not in site.settings:
  141. site.settings["optional_help"] = {}
  142. stats = content_db.execute(
  143. "SELECT COUNT(*) AS num, SUM(size) AS size FROM file_optional WHERE site_id = :site_id AND inner_path LIKE :inner_path",
  144. {"site_id": site_id, "inner_path": directory + "%"}
  145. ).fetchone()
  146. stats = dict(stats)
  147. if not stats["size"]:
  148. stats["size"] = 0
  149. if not stats["num"]:
  150. stats["num"] = 0
  151. self.cmd("notification", [
  152. "done",
  153. "You started to help distribute <b>%s</b>.<br><small>Directory: %s</small>" %
  154. (cgi.escape(title), cgi.escape(directory)),
  155. 10000
  156. ])
  157. site.settings["optional_help"][directory] = title
  158. self.response(to, dict(stats))
  159. def actionOptionalHelpRemove(self, to, directory, address=None):
  160. if not address:
  161. address = self.site.address
  162. if not self.hasSitePermission(address):
  163. return self.response(to, {"error": "Forbidden"})
  164. site = self.server.sites[address]
  165. try:
  166. del site.settings["optional_help"][directory]
  167. self.response(to, "ok")
  168. except Exception:
  169. self.response(to, {"error": "Not found"})
  170. def cbOptionalHelpAll(self, to, site, value):
  171. site.settings["autodownloadoptional"] = value
  172. self.response(to, value)
  173. def actionOptionalHelpAll(self, to, value, address=None):
  174. if not address:
  175. address = self.site.address
  176. if not self.hasSitePermission(address):
  177. return self.response(to, {"error": "Forbidden"})
  178. site = self.server.sites[address]
  179. if value:
  180. if "ADMIN" in self.site.settings["permissions"]:
  181. self.cbOptionalHelpAll(to, site, True)
  182. else:
  183. site_title = site.content_manager.contents["content.json"].get("title", address)
  184. self.cmd(
  185. "confirm",
  186. [
  187. "Help distribute all new optional files on site <b>%s</b>" % cgi.escape(site_title),
  188. "Yes, I want to help!"
  189. ],
  190. lambda (res): self.cbOptionalHelpAll(to, site, True)
  191. )
  192. else:
  193. site.settings["autodownloadoptional"] = False
  194. self.response(to, False)