room.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from twisted.internet import defer
  16. from synapse.api.errors import StoreError
  17. from ._base import SQLBaseStore
  18. from synapse.util.caches.descriptors import cachedInlineCallbacks
  19. from .engines import PostgresEngine, Sqlite3Engine
  20. import collections
  21. import logging
  22. import ujson as json
  23. logger = logging.getLogger(__name__)
  24. OpsLevel = collections.namedtuple(
  25. "OpsLevel",
  26. ("ban_level", "kick_level", "redact_level",)
  27. )
  28. class RoomStore(SQLBaseStore):
  29. @defer.inlineCallbacks
  30. def store_room(self, room_id, room_creator_user_id, is_public):
  31. """Stores a room.
  32. Args:
  33. room_id (str): The desired room ID, can be None.
  34. room_creator_user_id (str): The user ID of the room creator.
  35. is_public (bool): True to indicate that this room should appear in
  36. public room lists.
  37. Raises:
  38. StoreError if the room could not be stored.
  39. """
  40. try:
  41. yield self._simple_insert(
  42. "rooms",
  43. {
  44. "room_id": room_id,
  45. "creator": room_creator_user_id,
  46. "is_public": is_public,
  47. },
  48. desc="store_room",
  49. )
  50. except Exception as e:
  51. logger.error("store_room with room_id=%s failed: %s", room_id, e)
  52. raise StoreError(500, "Problem creating room.")
  53. def get_room(self, room_id):
  54. """Retrieve a room.
  55. Args:
  56. room_id (str): The ID of the room to retrieve.
  57. Returns:
  58. A namedtuple containing the room information, or an empty list.
  59. """
  60. return self._simple_select_one(
  61. table="rooms",
  62. keyvalues={"room_id": room_id},
  63. retcols=("room_id", "is_public", "creator"),
  64. desc="get_room",
  65. allow_none=True,
  66. )
  67. def set_room_is_public(self, room_id, is_public):
  68. return self._simple_update_one(
  69. table="rooms",
  70. keyvalues={"room_id": room_id},
  71. updatevalues={"is_public": is_public},
  72. desc="set_room_is_public",
  73. )
  74. def get_public_room_ids(self):
  75. return self._simple_select_onecol(
  76. table="rooms",
  77. keyvalues={
  78. "is_public": True,
  79. },
  80. retcol="room_id",
  81. desc="get_public_room_ids",
  82. )
  83. def get_room_count(self):
  84. """Retrieve a list of all rooms
  85. """
  86. def f(txn):
  87. sql = "SELECT count(*) FROM rooms"
  88. txn.execute(sql)
  89. row = txn.fetchone()
  90. return row[0] or 0
  91. return self.runInteraction(
  92. "get_rooms", f
  93. )
  94. def _store_room_topic_txn(self, txn, event):
  95. if hasattr(event, "content") and "topic" in event.content:
  96. self._simple_insert_txn(
  97. txn,
  98. "topics",
  99. {
  100. "event_id": event.event_id,
  101. "room_id": event.room_id,
  102. "topic": event.content["topic"],
  103. },
  104. )
  105. self._store_event_search_txn(
  106. txn, event, "content.topic", event.content["topic"]
  107. )
  108. def _store_room_name_txn(self, txn, event):
  109. if hasattr(event, "content") and "name" in event.content:
  110. self._simple_insert_txn(
  111. txn,
  112. "room_names",
  113. {
  114. "event_id": event.event_id,
  115. "room_id": event.room_id,
  116. "name": event.content["name"],
  117. }
  118. )
  119. self._store_event_search_txn(
  120. txn, event, "content.name", event.content["name"]
  121. )
  122. def _store_room_message_txn(self, txn, event):
  123. if hasattr(event, "content") and "body" in event.content:
  124. self._store_event_search_txn(
  125. txn, event, "content.body", event.content["body"]
  126. )
  127. def _store_history_visibility_txn(self, txn, event):
  128. self._store_content_index_txn(txn, event, "history_visibility")
  129. def _store_guest_access_txn(self, txn, event):
  130. self._store_content_index_txn(txn, event, "guest_access")
  131. def _store_content_index_txn(self, txn, event, key):
  132. if hasattr(event, "content") and key in event.content:
  133. sql = (
  134. "INSERT INTO %(key)s"
  135. " (event_id, room_id, %(key)s)"
  136. " VALUES (?, ?, ?)" % {"key": key}
  137. )
  138. txn.execute(sql, (
  139. event.event_id,
  140. event.room_id,
  141. event.content[key]
  142. ))
  143. def _store_event_search_txn(self, txn, event, key, value):
  144. if isinstance(self.database_engine, PostgresEngine):
  145. sql = (
  146. "INSERT INTO event_search"
  147. " (event_id, room_id, key, vector, stream_ordering, origin_server_ts)"
  148. " VALUES (?,?,?,to_tsvector('english', ?),?,?)"
  149. )
  150. txn.execute(
  151. sql,
  152. (
  153. event.event_id, event.room_id, key, value,
  154. event.internal_metadata.stream_ordering,
  155. event.origin_server_ts,
  156. )
  157. )
  158. elif isinstance(self.database_engine, Sqlite3Engine):
  159. sql = (
  160. "INSERT INTO event_search (event_id, room_id, key, value)"
  161. " VALUES (?,?,?,?)"
  162. )
  163. txn.execute(sql, (event.event_id, event.room_id, key, value,))
  164. else:
  165. # This should be unreachable.
  166. raise Exception("Unrecognized database engine")
  167. @cachedInlineCallbacks()
  168. def get_room_name_and_aliases(self, room_id):
  169. def get_room_name(txn):
  170. sql = (
  171. "SELECT name FROM room_names"
  172. " INNER JOIN current_state_events USING (room_id, event_id)"
  173. " WHERE room_id = ?"
  174. " LIMIT 1"
  175. )
  176. txn.execute(sql, (room_id,))
  177. rows = txn.fetchall()
  178. if rows:
  179. return rows[0][0]
  180. else:
  181. return None
  182. return [row[0] for row in txn.fetchall()]
  183. def get_room_aliases(txn):
  184. sql = (
  185. "SELECT content FROM current_state_events"
  186. " INNER JOIN events USING (room_id, event_id)"
  187. " WHERE room_id = ?"
  188. )
  189. txn.execute(sql, (room_id,))
  190. return [row[0] for row in txn.fetchall()]
  191. name = yield self.runInteraction("get_room_name", get_room_name)
  192. alias_contents = yield self.runInteraction("get_room_aliases", get_room_aliases)
  193. aliases = []
  194. for c in alias_contents:
  195. try:
  196. content = json.loads(c)
  197. except:
  198. continue
  199. aliases.extend(content.get('aliases', []))
  200. defer.returnValue((name, aliases))
  201. def add_event_report(self, room_id, event_id, user_id, reason, content,
  202. received_ts):
  203. next_id = self._event_reports_id_gen.get_next()
  204. return self._simple_insert(
  205. table="event_reports",
  206. values={
  207. "id": next_id,
  208. "received_ts": received_ts,
  209. "room_id": room_id,
  210. "event_id": event_id,
  211. "user_id": user_id,
  212. "reason": reason,
  213. "content": json.dumps(content),
  214. },
  215. desc="add_event_report"
  216. )