room.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. # Copyright 2018 New Vector Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """ This module contains REST servlets to do with rooms: /rooms/<paths> """
  17. import logging
  18. import re
  19. from typing import List, Optional
  20. from urllib import parse as urlparse
  21. from canonicaljson import json
  22. from synapse.api.constants import EventTypes, Membership
  23. from synapse.api.errors import (
  24. AuthError,
  25. Codes,
  26. HttpResponseException,
  27. InvalidClientCredentialsError,
  28. SynapseError,
  29. )
  30. from synapse.api.filtering import Filter
  31. from synapse.events.utils import format_event_for_client_v2
  32. from synapse.http.servlet import (
  33. RestServlet,
  34. assert_params_in_dict,
  35. parse_integer,
  36. parse_json_object_from_request,
  37. parse_string,
  38. )
  39. from synapse.logging.opentracing import set_tag
  40. from synapse.rest.client.transactions import HttpTransactionCache
  41. from synapse.rest.client.v2_alpha._base import client_patterns
  42. from synapse.storage.state import StateFilter
  43. from synapse.streams.config import PaginationConfig
  44. from synapse.types import RoomAlias, RoomID, StreamToken, ThirdPartyInstanceID, UserID
  45. MYPY = False
  46. if MYPY:
  47. import synapse.server
  48. logger = logging.getLogger(__name__)
  49. class TransactionRestServlet(RestServlet):
  50. def __init__(self, hs):
  51. super(TransactionRestServlet, self).__init__()
  52. self.txns = HttpTransactionCache(hs)
  53. class RoomCreateRestServlet(TransactionRestServlet):
  54. # No PATTERN; we have custom dispatch rules here
  55. def __init__(self, hs):
  56. super(RoomCreateRestServlet, self).__init__(hs)
  57. self._room_creation_handler = hs.get_room_creation_handler()
  58. self.auth = hs.get_auth()
  59. def register(self, http_server):
  60. PATTERNS = "/createRoom"
  61. register_txn_path(self, PATTERNS, http_server)
  62. # define CORS for all of /rooms in RoomCreateRestServlet for simplicity
  63. http_server.register_paths(
  64. "OPTIONS",
  65. client_patterns("/rooms(?:/.*)?$", v1=True),
  66. self.on_OPTIONS,
  67. self.__class__.__name__,
  68. )
  69. # define CORS for /createRoom[/txnid]
  70. http_server.register_paths(
  71. "OPTIONS",
  72. client_patterns("/createRoom(?:/.*)?$", v1=True),
  73. self.on_OPTIONS,
  74. self.__class__.__name__,
  75. )
  76. def on_PUT(self, request, txn_id):
  77. set_tag("txn_id", txn_id)
  78. return self.txns.fetch_or_execute_request(request, self.on_POST, request)
  79. async def on_POST(self, request):
  80. requester = await self.auth.get_user_by_req(request)
  81. info, _ = await self._room_creation_handler.create_room(
  82. requester, self.get_room_config(request)
  83. )
  84. return 200, info
  85. def get_room_config(self, request):
  86. user_supplied_config = parse_json_object_from_request(request)
  87. return user_supplied_config
  88. def on_OPTIONS(self, request):
  89. return 200, {}
  90. # TODO: Needs unit testing for generic events
  91. class RoomStateEventRestServlet(TransactionRestServlet):
  92. def __init__(self, hs):
  93. super(RoomStateEventRestServlet, self).__init__(hs)
  94. self.handlers = hs.get_handlers()
  95. self.event_creation_handler = hs.get_event_creation_handler()
  96. self.room_member_handler = hs.get_room_member_handler()
  97. self.message_handler = hs.get_message_handler()
  98. self.auth = hs.get_auth()
  99. def register(self, http_server):
  100. # /room/$roomid/state/$eventtype
  101. no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"
  102. # /room/$roomid/state/$eventtype/$statekey
  103. state_key = (
  104. "/rooms/(?P<room_id>[^/]*)/state/"
  105. "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$"
  106. )
  107. http_server.register_paths(
  108. "GET",
  109. client_patterns(state_key, v1=True),
  110. self.on_GET,
  111. self.__class__.__name__,
  112. )
  113. http_server.register_paths(
  114. "PUT",
  115. client_patterns(state_key, v1=True),
  116. self.on_PUT,
  117. self.__class__.__name__,
  118. )
  119. http_server.register_paths(
  120. "GET",
  121. client_patterns(no_state_key, v1=True),
  122. self.on_GET_no_state_key,
  123. self.__class__.__name__,
  124. )
  125. http_server.register_paths(
  126. "PUT",
  127. client_patterns(no_state_key, v1=True),
  128. self.on_PUT_no_state_key,
  129. self.__class__.__name__,
  130. )
  131. def on_GET_no_state_key(self, request, room_id, event_type):
  132. return self.on_GET(request, room_id, event_type, "")
  133. def on_PUT_no_state_key(self, request, room_id, event_type):
  134. return self.on_PUT(request, room_id, event_type, "")
  135. async def on_GET(self, request, room_id, event_type, state_key):
  136. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  137. format = parse_string(
  138. request, "format", default="content", allowed_values=["content", "event"]
  139. )
  140. msg_handler = self.message_handler
  141. data = await msg_handler.get_room_data(
  142. user_id=requester.user.to_string(),
  143. room_id=room_id,
  144. event_type=event_type,
  145. state_key=state_key,
  146. is_guest=requester.is_guest,
  147. )
  148. if not data:
  149. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  150. if format == "event":
  151. event = format_event_for_client_v2(data.get_dict())
  152. return 200, event
  153. elif format == "content":
  154. return 200, data.get_dict()["content"]
  155. async def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
  156. requester = await self.auth.get_user_by_req(request)
  157. if txn_id:
  158. set_tag("txn_id", txn_id)
  159. content = parse_json_object_from_request(request)
  160. event_dict = {
  161. "type": event_type,
  162. "content": content,
  163. "room_id": room_id,
  164. "sender": requester.user.to_string(),
  165. }
  166. if state_key is not None:
  167. event_dict["state_key"] = state_key
  168. if event_type == EventTypes.Member:
  169. membership = content.get("membership", None)
  170. event_id, _ = await self.room_member_handler.update_membership(
  171. requester,
  172. target=UserID.from_string(state_key),
  173. room_id=room_id,
  174. action=membership,
  175. content=content,
  176. )
  177. else:
  178. (
  179. event,
  180. _,
  181. ) = await self.event_creation_handler.create_and_send_nonmember_event(
  182. requester, event_dict, txn_id=txn_id
  183. )
  184. event_id = event.event_id
  185. ret = {} # type: dict
  186. if event_id:
  187. set_tag("event_id", event_id)
  188. ret = {"event_id": event_id}
  189. return 200, ret
  190. # TODO: Needs unit testing for generic events + feedback
  191. class RoomSendEventRestServlet(TransactionRestServlet):
  192. def __init__(self, hs):
  193. super(RoomSendEventRestServlet, self).__init__(hs)
  194. self.event_creation_handler = hs.get_event_creation_handler()
  195. self.auth = hs.get_auth()
  196. def register(self, http_server):
  197. # /rooms/$roomid/send/$event_type[/$txn_id]
  198. PATTERNS = "/rooms/(?P<room_id>[^/]*)/send/(?P<event_type>[^/]*)"
  199. register_txn_path(self, PATTERNS, http_server, with_get=True)
  200. async def on_POST(self, request, room_id, event_type, txn_id=None):
  201. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  202. content = parse_json_object_from_request(request)
  203. event_dict = {
  204. "type": event_type,
  205. "content": content,
  206. "room_id": room_id,
  207. "sender": requester.user.to_string(),
  208. }
  209. if b"ts" in request.args and requester.app_service:
  210. event_dict["origin_server_ts"] = parse_integer(request, "ts", 0)
  211. event, _ = await self.event_creation_handler.create_and_send_nonmember_event(
  212. requester, event_dict, txn_id=txn_id
  213. )
  214. set_tag("event_id", event.event_id)
  215. return 200, {"event_id": event.event_id}
  216. def on_GET(self, request, room_id, event_type, txn_id):
  217. return 200, "Not implemented"
  218. def on_PUT(self, request, room_id, event_type, txn_id):
  219. set_tag("txn_id", txn_id)
  220. return self.txns.fetch_or_execute_request(
  221. request, self.on_POST, request, room_id, event_type, txn_id
  222. )
  223. # TODO: Needs unit testing for room ID + alias joins
  224. class JoinRoomAliasServlet(TransactionRestServlet):
  225. def __init__(self, hs):
  226. super(JoinRoomAliasServlet, self).__init__(hs)
  227. self.room_member_handler = hs.get_room_member_handler()
  228. self.auth = hs.get_auth()
  229. def register(self, http_server):
  230. # /join/$room_identifier[/$txn_id]
  231. PATTERNS = "/join/(?P<room_identifier>[^/]*)"
  232. register_txn_path(self, PATTERNS, http_server)
  233. async def on_POST(self, request, room_identifier, txn_id=None):
  234. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  235. try:
  236. content = parse_json_object_from_request(request)
  237. except Exception:
  238. # Turns out we used to ignore the body entirely, and some clients
  239. # cheekily send invalid bodies.
  240. content = {}
  241. if RoomID.is_valid(room_identifier):
  242. room_id = room_identifier
  243. try:
  244. remote_room_hosts = [
  245. x.decode("ascii") for x in request.args[b"server_name"]
  246. ] # type: Optional[List[str]]
  247. except Exception:
  248. remote_room_hosts = None
  249. elif RoomAlias.is_valid(room_identifier):
  250. handler = self.room_member_handler
  251. room_alias = RoomAlias.from_string(room_identifier)
  252. room_id, remote_room_hosts = await handler.lookup_room_alias(room_alias)
  253. room_id = room_id.to_string()
  254. else:
  255. raise SynapseError(
  256. 400, "%s was not legal room ID or room alias" % (room_identifier,)
  257. )
  258. await self.room_member_handler.update_membership(
  259. requester=requester,
  260. target=requester.user,
  261. room_id=room_id,
  262. action="join",
  263. txn_id=txn_id,
  264. remote_room_hosts=remote_room_hosts,
  265. content=content,
  266. third_party_signed=content.get("third_party_signed", None),
  267. )
  268. return 200, {"room_id": room_id}
  269. def on_PUT(self, request, room_identifier, txn_id):
  270. set_tag("txn_id", txn_id)
  271. return self.txns.fetch_or_execute_request(
  272. request, self.on_POST, request, room_identifier, txn_id
  273. )
  274. # TODO: Needs unit testing
  275. class PublicRoomListRestServlet(TransactionRestServlet):
  276. PATTERNS = client_patterns("/publicRooms$", v1=True)
  277. def __init__(self, hs):
  278. super(PublicRoomListRestServlet, self).__init__(hs)
  279. self.hs = hs
  280. self.auth = hs.get_auth()
  281. async def on_GET(self, request):
  282. server = parse_string(request, "server", default=None)
  283. try:
  284. await self.auth.get_user_by_req(request, allow_guest=True)
  285. except InvalidClientCredentialsError as e:
  286. # Option to allow servers to require auth when accessing
  287. # /publicRooms via CS API. This is especially helpful in private
  288. # federations.
  289. if not self.hs.config.allow_public_rooms_without_auth:
  290. raise
  291. # We allow people to not be authed if they're just looking at our
  292. # room list, but require auth when we proxy the request.
  293. # In both cases we call the auth function, as that has the side
  294. # effect of logging who issued this request if an access token was
  295. # provided.
  296. if server:
  297. raise e
  298. else:
  299. pass
  300. limit = parse_integer(request, "limit", 0)
  301. since_token = parse_string(request, "since", None)
  302. if limit == 0:
  303. # zero is a special value which corresponds to no limit.
  304. limit = None
  305. handler = self.hs.get_room_list_handler()
  306. if server and server != self.hs.config.server_name:
  307. try:
  308. data = await handler.get_remote_public_room_list(
  309. server, limit=limit, since_token=since_token
  310. )
  311. except HttpResponseException as e:
  312. raise e.to_synapse_error()
  313. else:
  314. data = await handler.get_local_public_room_list(
  315. limit=limit, since_token=since_token
  316. )
  317. return 200, data
  318. async def on_POST(self, request):
  319. await self.auth.get_user_by_req(request, allow_guest=True)
  320. server = parse_string(request, "server", default=None)
  321. content = parse_json_object_from_request(request)
  322. limit = int(content.get("limit", 100)) # type: Optional[int]
  323. since_token = content.get("since", None)
  324. search_filter = content.get("filter", None)
  325. include_all_networks = content.get("include_all_networks", False)
  326. third_party_instance_id = content.get("third_party_instance_id", None)
  327. if include_all_networks:
  328. network_tuple = None
  329. if third_party_instance_id is not None:
  330. raise SynapseError(
  331. 400, "Can't use include_all_networks with an explicit network"
  332. )
  333. elif third_party_instance_id is None:
  334. network_tuple = ThirdPartyInstanceID(None, None)
  335. else:
  336. network_tuple = ThirdPartyInstanceID.from_string(third_party_instance_id)
  337. if limit == 0:
  338. # zero is a special value which corresponds to no limit.
  339. limit = None
  340. handler = self.hs.get_room_list_handler()
  341. if server and server != self.hs.config.server_name:
  342. try:
  343. data = await handler.get_remote_public_room_list(
  344. server,
  345. limit=limit,
  346. since_token=since_token,
  347. search_filter=search_filter,
  348. include_all_networks=include_all_networks,
  349. third_party_instance_id=third_party_instance_id,
  350. )
  351. except HttpResponseException as e:
  352. raise e.to_synapse_error()
  353. else:
  354. data = await handler.get_local_public_room_list(
  355. limit=limit,
  356. since_token=since_token,
  357. search_filter=search_filter,
  358. network_tuple=network_tuple,
  359. )
  360. return 200, data
  361. # TODO: Needs unit testing
  362. class RoomMemberListRestServlet(RestServlet):
  363. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)
  364. def __init__(self, hs):
  365. super(RoomMemberListRestServlet, self).__init__()
  366. self.message_handler = hs.get_message_handler()
  367. self.auth = hs.get_auth()
  368. async def on_GET(self, request, room_id):
  369. # TODO support Pagination stream API (limit/tokens)
  370. requester = await self.auth.get_user_by_req(request)
  371. handler = self.message_handler
  372. # request the state as of a given event, as identified by a stream token,
  373. # for consistency with /messages etc.
  374. # useful for getting the membership in retrospect as of a given /sync
  375. # response.
  376. at_token_string = parse_string(request, "at")
  377. if at_token_string is None:
  378. at_token = None
  379. else:
  380. at_token = StreamToken.from_string(at_token_string)
  381. # let you filter down on particular memberships.
  382. # XXX: this may not be the best shape for this API - we could pass in a filter
  383. # instead, except filters aren't currently aware of memberships.
  384. # See https://github.com/matrix-org/matrix-doc/issues/1337 for more details.
  385. membership = parse_string(request, "membership")
  386. not_membership = parse_string(request, "not_membership")
  387. events = await handler.get_state_events(
  388. room_id=room_id,
  389. user_id=requester.user.to_string(),
  390. at_token=at_token,
  391. state_filter=StateFilter.from_types([(EventTypes.Member, None)]),
  392. )
  393. chunk = []
  394. for event in events:
  395. if (membership and event["content"].get("membership") != membership) or (
  396. not_membership and event["content"].get("membership") == not_membership
  397. ):
  398. continue
  399. chunk.append(event)
  400. return 200, {"chunk": chunk}
  401. # deprecated in favour of /members?membership=join?
  402. # except it does custom AS logic and has a simpler return format
  403. class JoinedRoomMemberListRestServlet(RestServlet):
  404. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/joined_members$", v1=True)
  405. def __init__(self, hs):
  406. super(JoinedRoomMemberListRestServlet, self).__init__()
  407. self.message_handler = hs.get_message_handler()
  408. self.auth = hs.get_auth()
  409. async def on_GET(self, request, room_id):
  410. requester = await self.auth.get_user_by_req(request)
  411. users_with_profile = await self.message_handler.get_joined_members(
  412. requester, room_id
  413. )
  414. return 200, {"joined": users_with_profile}
  415. # TODO: Needs better unit testing
  416. class RoomMessageListRestServlet(RestServlet):
  417. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/messages$", v1=True)
  418. def __init__(self, hs):
  419. super(RoomMessageListRestServlet, self).__init__()
  420. self.pagination_handler = hs.get_pagination_handler()
  421. self.auth = hs.get_auth()
  422. async def on_GET(self, request, room_id):
  423. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  424. pagination_config = PaginationConfig.from_request(request, default_limit=10)
  425. as_client_event = b"raw" not in request.args
  426. filter_bytes = parse_string(request, b"filter", encoding=None)
  427. if filter_bytes:
  428. filter_json = urlparse.unquote(filter_bytes.decode("UTF-8"))
  429. event_filter = Filter(json.loads(filter_json)) # type: Optional[Filter]
  430. if (
  431. event_filter
  432. and event_filter.filter_json.get("event_format", "client")
  433. == "federation"
  434. ):
  435. as_client_event = False
  436. else:
  437. event_filter = None
  438. msgs = await self.pagination_handler.get_messages(
  439. room_id=room_id,
  440. requester=requester,
  441. pagin_config=pagination_config,
  442. as_client_event=as_client_event,
  443. event_filter=event_filter,
  444. )
  445. return 200, msgs
  446. # TODO: Needs unit testing
  447. class RoomStateRestServlet(RestServlet):
  448. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)
  449. def __init__(self, hs):
  450. super(RoomStateRestServlet, self).__init__()
  451. self.message_handler = hs.get_message_handler()
  452. self.auth = hs.get_auth()
  453. async def on_GET(self, request, room_id):
  454. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  455. # Get all the current state for this room
  456. events = await self.message_handler.get_state_events(
  457. room_id=room_id,
  458. user_id=requester.user.to_string(),
  459. is_guest=requester.is_guest,
  460. )
  461. return 200, events
  462. # TODO: Needs unit testing
  463. class RoomInitialSyncRestServlet(RestServlet):
  464. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)
  465. def __init__(self, hs):
  466. super(RoomInitialSyncRestServlet, self).__init__()
  467. self.initial_sync_handler = hs.get_initial_sync_handler()
  468. self.auth = hs.get_auth()
  469. async def on_GET(self, request, room_id):
  470. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  471. pagination_config = PaginationConfig.from_request(request)
  472. content = await self.initial_sync_handler.room_initial_sync(
  473. room_id=room_id, requester=requester, pagin_config=pagination_config
  474. )
  475. return 200, content
  476. class RoomEventServlet(RestServlet):
  477. PATTERNS = client_patterns(
  478. "/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True
  479. )
  480. def __init__(self, hs):
  481. super(RoomEventServlet, self).__init__()
  482. self.clock = hs.get_clock()
  483. self.event_handler = hs.get_event_handler()
  484. self._event_serializer = hs.get_event_client_serializer()
  485. self.auth = hs.get_auth()
  486. async def on_GET(self, request, room_id, event_id):
  487. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  488. try:
  489. event = await self.event_handler.get_event(
  490. requester.user, room_id, event_id
  491. )
  492. except AuthError:
  493. # This endpoint is supposed to return a 404 when the requester does
  494. # not have permission to access the event
  495. # https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-rooms-roomid-event-eventid
  496. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  497. time_now = self.clock.time_msec()
  498. if event:
  499. event = await self._event_serializer.serialize_event(event, time_now)
  500. return 200, event
  501. return SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  502. class RoomEventContextServlet(RestServlet):
  503. PATTERNS = client_patterns(
  504. "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True
  505. )
  506. def __init__(self, hs):
  507. super(RoomEventContextServlet, self).__init__()
  508. self.clock = hs.get_clock()
  509. self.room_context_handler = hs.get_room_context_handler()
  510. self._event_serializer = hs.get_event_client_serializer()
  511. self.auth = hs.get_auth()
  512. async def on_GET(self, request, room_id, event_id):
  513. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  514. limit = parse_integer(request, "limit", default=10)
  515. # picking the API shape for symmetry with /messages
  516. filter_bytes = parse_string(request, "filter")
  517. if filter_bytes:
  518. filter_json = urlparse.unquote(filter_bytes)
  519. event_filter = Filter(json.loads(filter_json)) # type: Optional[Filter]
  520. else:
  521. event_filter = None
  522. results = await self.room_context_handler.get_event_context(
  523. requester.user, room_id, event_id, limit, event_filter
  524. )
  525. if not results:
  526. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  527. time_now = self.clock.time_msec()
  528. results["events_before"] = await self._event_serializer.serialize_events(
  529. results["events_before"], time_now
  530. )
  531. results["event"] = await self._event_serializer.serialize_event(
  532. results["event"], time_now
  533. )
  534. results["events_after"] = await self._event_serializer.serialize_events(
  535. results["events_after"], time_now
  536. )
  537. results["state"] = await self._event_serializer.serialize_events(
  538. results["state"], time_now
  539. )
  540. return 200, results
  541. class RoomForgetRestServlet(TransactionRestServlet):
  542. def __init__(self, hs):
  543. super(RoomForgetRestServlet, self).__init__(hs)
  544. self.room_member_handler = hs.get_room_member_handler()
  545. self.auth = hs.get_auth()
  546. def register(self, http_server):
  547. PATTERNS = "/rooms/(?P<room_id>[^/]*)/forget"
  548. register_txn_path(self, PATTERNS, http_server)
  549. async def on_POST(self, request, room_id, txn_id=None):
  550. requester = await self.auth.get_user_by_req(request, allow_guest=False)
  551. await self.room_member_handler.forget(user=requester.user, room_id=room_id)
  552. return 200, {}
  553. def on_PUT(self, request, room_id, txn_id):
  554. set_tag("txn_id", txn_id)
  555. return self.txns.fetch_or_execute_request(
  556. request, self.on_POST, request, room_id, txn_id
  557. )
  558. # TODO: Needs unit testing
  559. class RoomMembershipRestServlet(TransactionRestServlet):
  560. def __init__(self, hs):
  561. super(RoomMembershipRestServlet, self).__init__(hs)
  562. self.room_member_handler = hs.get_room_member_handler()
  563. self.auth = hs.get_auth()
  564. def register(self, http_server):
  565. # /rooms/$roomid/[invite|join|leave]
  566. PATTERNS = (
  567. "/rooms/(?P<room_id>[^/]*)/"
  568. "(?P<membership_action>join|invite|leave|ban|unban|kick)"
  569. )
  570. register_txn_path(self, PATTERNS, http_server)
  571. async def on_POST(self, request, room_id, membership_action, txn_id=None):
  572. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  573. if requester.is_guest and membership_action not in {
  574. Membership.JOIN,
  575. Membership.LEAVE,
  576. }:
  577. raise AuthError(403, "Guest access not allowed")
  578. try:
  579. content = parse_json_object_from_request(request)
  580. except Exception:
  581. # Turns out we used to ignore the body entirely, and some clients
  582. # cheekily send invalid bodies.
  583. content = {}
  584. if membership_action == "invite" and self._has_3pid_invite_keys(content):
  585. await self.room_member_handler.do_3pid_invite(
  586. room_id,
  587. requester.user,
  588. content["medium"],
  589. content["address"],
  590. content["id_server"],
  591. requester,
  592. txn_id,
  593. content.get("id_access_token"),
  594. )
  595. return 200, {}
  596. target = requester.user
  597. if membership_action in ["invite", "ban", "unban", "kick"]:
  598. assert_params_in_dict(content, ["user_id"])
  599. target = UserID.from_string(content["user_id"])
  600. event_content = None
  601. if "reason" in content:
  602. event_content = {"reason": content["reason"]}
  603. await self.room_member_handler.update_membership(
  604. requester=requester,
  605. target=target,
  606. room_id=room_id,
  607. action=membership_action,
  608. txn_id=txn_id,
  609. third_party_signed=content.get("third_party_signed", None),
  610. content=event_content,
  611. )
  612. return_value = {}
  613. if membership_action == "join":
  614. return_value["room_id"] = room_id
  615. return 200, return_value
  616. def _has_3pid_invite_keys(self, content):
  617. for key in {"id_server", "medium", "address"}:
  618. if key not in content:
  619. return False
  620. return True
  621. def on_PUT(self, request, room_id, membership_action, txn_id):
  622. set_tag("txn_id", txn_id)
  623. return self.txns.fetch_or_execute_request(
  624. request, self.on_POST, request, room_id, membership_action, txn_id
  625. )
  626. class RoomRedactEventRestServlet(TransactionRestServlet):
  627. def __init__(self, hs):
  628. super(RoomRedactEventRestServlet, self).__init__(hs)
  629. self.handlers = hs.get_handlers()
  630. self.event_creation_handler = hs.get_event_creation_handler()
  631. self.auth = hs.get_auth()
  632. def register(self, http_server):
  633. PATTERNS = "/rooms/(?P<room_id>[^/]*)/redact/(?P<event_id>[^/]*)"
  634. register_txn_path(self, PATTERNS, http_server)
  635. async def on_POST(self, request, room_id, event_id, txn_id=None):
  636. requester = await self.auth.get_user_by_req(request)
  637. content = parse_json_object_from_request(request)
  638. event, _ = await self.event_creation_handler.create_and_send_nonmember_event(
  639. requester,
  640. {
  641. "type": EventTypes.Redaction,
  642. "content": content,
  643. "room_id": room_id,
  644. "sender": requester.user.to_string(),
  645. "redacts": event_id,
  646. },
  647. txn_id=txn_id,
  648. )
  649. set_tag("event_id", event.event_id)
  650. return 200, {"event_id": event.event_id}
  651. def on_PUT(self, request, room_id, event_id, txn_id):
  652. set_tag("txn_id", txn_id)
  653. return self.txns.fetch_or_execute_request(
  654. request, self.on_POST, request, room_id, event_id, txn_id
  655. )
  656. class RoomTypingRestServlet(RestServlet):
  657. PATTERNS = client_patterns(
  658. "/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True
  659. )
  660. def __init__(self, hs):
  661. super(RoomTypingRestServlet, self).__init__()
  662. self.presence_handler = hs.get_presence_handler()
  663. self.typing_handler = hs.get_typing_handler()
  664. self.auth = hs.get_auth()
  665. async def on_PUT(self, request, room_id, user_id):
  666. requester = await self.auth.get_user_by_req(request)
  667. room_id = urlparse.unquote(room_id)
  668. target_user = UserID.from_string(urlparse.unquote(user_id))
  669. content = parse_json_object_from_request(request)
  670. await self.presence_handler.bump_presence_active_time(requester.user)
  671. # Limit timeout to stop people from setting silly typing timeouts.
  672. timeout = min(content.get("timeout", 30000), 120000)
  673. if content["typing"]:
  674. await self.typing_handler.started_typing(
  675. target_user=target_user,
  676. auth_user=requester.user,
  677. room_id=room_id,
  678. timeout=timeout,
  679. )
  680. else:
  681. await self.typing_handler.stopped_typing(
  682. target_user=target_user, auth_user=requester.user, room_id=room_id
  683. )
  684. return 200, {}
  685. class RoomAliasListServlet(RestServlet):
  686. PATTERNS = [
  687. re.compile(
  688. r"^/_matrix/client/unstable/org\.matrix\.msc2432"
  689. r"/rooms/(?P<room_id>[^/]*)/aliases"
  690. ),
  691. ]
  692. def __init__(self, hs: "synapse.server.HomeServer"):
  693. super().__init__()
  694. self.auth = hs.get_auth()
  695. self.directory_handler = hs.get_handlers().directory_handler
  696. async def on_GET(self, request, room_id):
  697. requester = await self.auth.get_user_by_req(request)
  698. alias_list = await self.directory_handler.get_aliases_for_room(
  699. requester, room_id
  700. )
  701. return 200, {"aliases": alias_list}
  702. class SearchRestServlet(RestServlet):
  703. PATTERNS = client_patterns("/search$", v1=True)
  704. def __init__(self, hs):
  705. super(SearchRestServlet, self).__init__()
  706. self.handlers = hs.get_handlers()
  707. self.auth = hs.get_auth()
  708. async def on_POST(self, request):
  709. requester = await self.auth.get_user_by_req(request)
  710. content = parse_json_object_from_request(request)
  711. batch = parse_string(request, "next_batch")
  712. results = await self.handlers.search_handler.search(
  713. requester.user, content, batch
  714. )
  715. return 200, results
  716. class JoinedRoomsRestServlet(RestServlet):
  717. PATTERNS = client_patterns("/joined_rooms$", v1=True)
  718. def __init__(self, hs):
  719. super(JoinedRoomsRestServlet, self).__init__()
  720. self.store = hs.get_datastore()
  721. self.auth = hs.get_auth()
  722. async def on_GET(self, request):
  723. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  724. room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
  725. return 200, {"joined_rooms": list(room_ids)}
  726. def register_txn_path(servlet, regex_string, http_server, with_get=False):
  727. """Registers a transaction-based path.
  728. This registers two paths:
  729. PUT regex_string/$txnid
  730. POST regex_string
  731. Args:
  732. regex_string (str): The regex string to register. Must NOT have a
  733. trailing $ as this string will be appended to.
  734. http_server : The http_server to register paths with.
  735. with_get: True to also register respective GET paths for the PUTs.
  736. """
  737. http_server.register_paths(
  738. "POST",
  739. client_patterns(regex_string + "$", v1=True),
  740. servlet.on_POST,
  741. servlet.__class__.__name__,
  742. )
  743. http_server.register_paths(
  744. "PUT",
  745. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  746. servlet.on_PUT,
  747. servlet.__class__.__name__,
  748. )
  749. if with_get:
  750. http_server.register_paths(
  751. "GET",
  752. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  753. servlet.on_GET,
  754. servlet.__class__.__name__,
  755. )
  756. def register_servlets(hs, http_server):
  757. RoomStateEventRestServlet(hs).register(http_server)
  758. RoomCreateRestServlet(hs).register(http_server)
  759. RoomMemberListRestServlet(hs).register(http_server)
  760. JoinedRoomMemberListRestServlet(hs).register(http_server)
  761. RoomMessageListRestServlet(hs).register(http_server)
  762. JoinRoomAliasServlet(hs).register(http_server)
  763. RoomForgetRestServlet(hs).register(http_server)
  764. RoomMembershipRestServlet(hs).register(http_server)
  765. RoomSendEventRestServlet(hs).register(http_server)
  766. PublicRoomListRestServlet(hs).register(http_server)
  767. RoomStateRestServlet(hs).register(http_server)
  768. RoomRedactEventRestServlet(hs).register(http_server)
  769. RoomTypingRestServlet(hs).register(http_server)
  770. SearchRestServlet(hs).register(http_server)
  771. JoinedRoomsRestServlet(hs).register(http_server)
  772. RoomEventServlet(hs).register(http_server)
  773. RoomEventContextServlet(hs).register(http_server)
  774. RoomAliasListServlet(hs).register(http_server)
  775. def register_deprecated_servlets(hs, http_server):
  776. RoomInitialSyncRestServlet(hs).register(http_server)