room.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305
  1. # Copyright 2014-2016 OpenMarket Ltd
  2. # Copyright 2018 New Vector 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. """ This module contains REST servlets to do with rooms: /rooms/<paths> """
  16. import logging
  17. import re
  18. from typing import TYPE_CHECKING, Awaitable, Dict, List, Optional, Tuple
  19. from urllib import parse as urlparse
  20. from twisted.web.server import Request
  21. from synapse import event_auth
  22. from synapse.api.constants import EventTypes, Membership
  23. from synapse.api.errors import (
  24. AuthError,
  25. Codes,
  26. InvalidClientCredentialsError,
  27. MissingClientTokenError,
  28. ShadowBanError,
  29. SynapseError,
  30. UnredactedContentDeletedError,
  31. )
  32. from synapse.api.filtering import Filter
  33. from synapse.events.utils import format_event_for_client_v2
  34. from synapse.http.server import HttpServer, cancellable
  35. from synapse.http.servlet import (
  36. ResolveRoomIdMixin,
  37. RestServlet,
  38. assert_params_in_dict,
  39. parse_boolean,
  40. parse_integer,
  41. parse_json_object_from_request,
  42. parse_string,
  43. parse_strings_from_args,
  44. )
  45. from synapse.http.site import SynapseRequest
  46. from synapse.logging.opentracing import set_tag
  47. from synapse.rest.client._base import client_patterns
  48. from synapse.rest.client.transactions import HttpTransactionCache
  49. from synapse.storage.state import StateFilter
  50. from synapse.streams.config import PaginationConfig
  51. from synapse.types import JsonDict, StreamToken, ThirdPartyInstanceID, UserID
  52. from synapse.util import json_decoder
  53. from synapse.util.stringutils import parse_and_validate_server_name, random_string
  54. if TYPE_CHECKING:
  55. from synapse.server import HomeServer
  56. logger = logging.getLogger(__name__)
  57. class TransactionRestServlet(RestServlet):
  58. def __init__(self, hs: "HomeServer"):
  59. super().__init__()
  60. self.txns = HttpTransactionCache(hs)
  61. class RoomCreateRestServlet(TransactionRestServlet):
  62. # No PATTERN; we have custom dispatch rules here
  63. def __init__(self, hs: "HomeServer"):
  64. super().__init__(hs)
  65. self._room_creation_handler = hs.get_room_creation_handler()
  66. self.auth = hs.get_auth()
  67. def register(self, http_server: HttpServer) -> None:
  68. PATTERNS = "/createRoom"
  69. register_txn_path(self, PATTERNS, http_server)
  70. def on_PUT(
  71. self, request: SynapseRequest, txn_id: str
  72. ) -> Awaitable[Tuple[int, JsonDict]]:
  73. set_tag("txn_id", txn_id)
  74. return self.txns.fetch_or_execute_request(request, self.on_POST, request)
  75. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  76. requester = await self.auth.get_user_by_req(request)
  77. info, _ = await self._room_creation_handler.create_room(
  78. requester, self.get_room_config(request)
  79. )
  80. return 200, info
  81. def get_room_config(self, request: Request) -> JsonDict:
  82. user_supplied_config = parse_json_object_from_request(request)
  83. return user_supplied_config
  84. # TODO: Needs unit testing for generic events
  85. class RoomStateEventRestServlet(TransactionRestServlet):
  86. def __init__(self, hs: "HomeServer"):
  87. super().__init__(hs)
  88. self.event_creation_handler = hs.get_event_creation_handler()
  89. self.room_member_handler = hs.get_room_member_handler()
  90. self.message_handler = hs.get_message_handler()
  91. self.auth = hs.get_auth()
  92. def register(self, http_server: HttpServer) -> None:
  93. # /rooms/$roomid/state/$eventtype
  94. no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"
  95. # /rooms/$roomid/state/$eventtype/$statekey
  96. state_key = (
  97. "/rooms/(?P<room_id>[^/]*)/state/"
  98. "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$"
  99. )
  100. http_server.register_paths(
  101. "GET",
  102. client_patterns(state_key, v1=True),
  103. self.on_GET,
  104. self.__class__.__name__,
  105. )
  106. http_server.register_paths(
  107. "PUT",
  108. client_patterns(state_key, v1=True),
  109. self.on_PUT,
  110. self.__class__.__name__,
  111. )
  112. http_server.register_paths(
  113. "GET",
  114. client_patterns(no_state_key, v1=True),
  115. self.on_GET_no_state_key,
  116. self.__class__.__name__,
  117. )
  118. http_server.register_paths(
  119. "PUT",
  120. client_patterns(no_state_key, v1=True),
  121. self.on_PUT_no_state_key,
  122. self.__class__.__name__,
  123. )
  124. @cancellable
  125. def on_GET_no_state_key(
  126. self, request: SynapseRequest, room_id: str, event_type: str
  127. ) -> Awaitable[Tuple[int, JsonDict]]:
  128. return self.on_GET(request, room_id, event_type, "")
  129. def on_PUT_no_state_key(
  130. self, request: SynapseRequest, room_id: str, event_type: str
  131. ) -> Awaitable[Tuple[int, JsonDict]]:
  132. return self.on_PUT(request, room_id, event_type, "")
  133. @cancellable
  134. async def on_GET(
  135. self, request: SynapseRequest, room_id: str, event_type: str, state_key: str
  136. ) -> Tuple[int, JsonDict]:
  137. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  138. format = parse_string(
  139. request, "format", default="content", allowed_values=["content", "event"]
  140. )
  141. msg_handler = self.message_handler
  142. data = await msg_handler.get_room_data(
  143. user_id=requester.user.to_string(),
  144. room_id=room_id,
  145. event_type=event_type,
  146. state_key=state_key,
  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. # Format must be event or content, per the parse_string call above.
  156. raise RuntimeError(f"Unknown format: {format:r}.")
  157. async def on_PUT(
  158. self,
  159. request: SynapseRequest,
  160. room_id: str,
  161. event_type: str,
  162. state_key: str,
  163. txn_id: Optional[str] = None,
  164. ) -> Tuple[int, JsonDict]:
  165. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  166. if txn_id:
  167. set_tag("txn_id", txn_id)
  168. content = parse_json_object_from_request(request)
  169. event_dict = {
  170. "type": event_type,
  171. "content": content,
  172. "room_id": room_id,
  173. "sender": requester.user.to_string(),
  174. }
  175. if state_key is not None:
  176. event_dict["state_key"] = state_key
  177. try:
  178. if event_type == EventTypes.Member:
  179. membership = content.get("membership", None)
  180. event_id, _ = await self.room_member_handler.update_membership(
  181. requester,
  182. target=UserID.from_string(state_key),
  183. room_id=room_id,
  184. action=membership,
  185. content=content,
  186. )
  187. else:
  188. (
  189. event,
  190. _,
  191. ) = await self.event_creation_handler.create_and_send_nonmember_event(
  192. requester, event_dict, txn_id=txn_id
  193. )
  194. event_id = event.event_id
  195. except ShadowBanError:
  196. event_id = "$" + random_string(43)
  197. set_tag("event_id", event_id)
  198. ret = {"event_id": event_id}
  199. return 200, ret
  200. # TODO: Needs unit testing for generic events + feedback
  201. class RoomSendEventRestServlet(TransactionRestServlet):
  202. def __init__(self, hs: "HomeServer"):
  203. super().__init__(hs)
  204. self.event_creation_handler = hs.get_event_creation_handler()
  205. self.auth = hs.get_auth()
  206. def register(self, http_server: HttpServer) -> None:
  207. # /rooms/$roomid/send/$event_type[/$txn_id]
  208. PATTERNS = "/rooms/(?P<room_id>[^/]*)/send/(?P<event_type>[^/]*)"
  209. register_txn_path(self, PATTERNS, http_server, with_get=True)
  210. async def on_POST(
  211. self,
  212. request: SynapseRequest,
  213. room_id: str,
  214. event_type: str,
  215. txn_id: Optional[str] = None,
  216. ) -> Tuple[int, JsonDict]:
  217. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  218. content = parse_json_object_from_request(request)
  219. event_dict: JsonDict = {
  220. "type": event_type,
  221. "content": content,
  222. "room_id": room_id,
  223. "sender": requester.user.to_string(),
  224. }
  225. # Twisted will have processed the args by now.
  226. assert request.args is not None
  227. if b"ts" in request.args and requester.app_service:
  228. event_dict["origin_server_ts"] = parse_integer(request, "ts", 0)
  229. try:
  230. (
  231. event,
  232. _,
  233. ) = await self.event_creation_handler.create_and_send_nonmember_event(
  234. requester, event_dict, txn_id=txn_id
  235. )
  236. event_id = event.event_id
  237. except ShadowBanError:
  238. event_id = "$" + random_string(43)
  239. set_tag("event_id", event_id)
  240. return 200, {"event_id": event_id}
  241. def on_GET(
  242. self, request: SynapseRequest, room_id: str, event_type: str, txn_id: str
  243. ) -> Tuple[int, str]:
  244. return 200, "Not implemented"
  245. def on_PUT(
  246. self, request: SynapseRequest, room_id: str, event_type: str, txn_id: str
  247. ) -> Awaitable[Tuple[int, JsonDict]]:
  248. set_tag("txn_id", txn_id)
  249. return self.txns.fetch_or_execute_request(
  250. request, self.on_POST, request, room_id, event_type, txn_id
  251. )
  252. # TODO: Needs unit testing for room ID + alias joins
  253. class JoinRoomAliasServlet(ResolveRoomIdMixin, TransactionRestServlet):
  254. def __init__(self, hs: "HomeServer"):
  255. super().__init__(hs)
  256. super(ResolveRoomIdMixin, self).__init__(hs) # ensure the Mixin is set up
  257. self.auth = hs.get_auth()
  258. def register(self, http_server: HttpServer) -> None:
  259. # /join/$room_identifier[/$txn_id]
  260. PATTERNS = "/join/(?P<room_identifier>[^/]*)"
  261. register_txn_path(self, PATTERNS, http_server)
  262. async def on_POST(
  263. self,
  264. request: SynapseRequest,
  265. room_identifier: str,
  266. txn_id: Optional[str] = None,
  267. ) -> Tuple[int, JsonDict]:
  268. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  269. try:
  270. content = parse_json_object_from_request(request)
  271. except Exception:
  272. # Turns out we used to ignore the body entirely, and some clients
  273. # cheekily send invalid bodies.
  274. content = {}
  275. # twisted.web.server.Request.args is incorrectly defined as Optional[Any]
  276. args: Dict[bytes, List[bytes]] = request.args # type: ignore
  277. remote_room_hosts = parse_strings_from_args(args, "server_name", required=False)
  278. room_id, remote_room_hosts = await self.resolve_room_id(
  279. room_identifier,
  280. remote_room_hosts,
  281. )
  282. await self.room_member_handler.update_membership(
  283. requester=requester,
  284. target=requester.user,
  285. room_id=room_id,
  286. action="join",
  287. txn_id=txn_id,
  288. remote_room_hosts=remote_room_hosts,
  289. content=content,
  290. third_party_signed=content.get("third_party_signed", None),
  291. )
  292. return 200, {"room_id": room_id}
  293. def on_PUT(
  294. self, request: SynapseRequest, room_identifier: str, txn_id: str
  295. ) -> Awaitable[Tuple[int, JsonDict]]:
  296. set_tag("txn_id", txn_id)
  297. return self.txns.fetch_or_execute_request(
  298. request, self.on_POST, request, room_identifier, txn_id
  299. )
  300. # TODO: Needs unit testing
  301. class PublicRoomListRestServlet(TransactionRestServlet):
  302. PATTERNS = client_patterns("/publicRooms$", v1=True)
  303. def __init__(self, hs: "HomeServer"):
  304. super().__init__(hs)
  305. self.hs = hs
  306. self.auth = hs.get_auth()
  307. async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  308. server = parse_string(request, "server")
  309. try:
  310. await self.auth.get_user_by_req(request, allow_guest=True)
  311. except InvalidClientCredentialsError as e:
  312. # Option to allow servers to require auth when accessing
  313. # /publicRooms via CS API. This is especially helpful in private
  314. # federations.
  315. if not self.hs.config.server.allow_public_rooms_without_auth:
  316. raise
  317. # We allow people to not be authed if they're just looking at our
  318. # room list, but require auth when we proxy the request.
  319. # In both cases we call the auth function, as that has the side
  320. # effect of logging who issued this request if an access token was
  321. # provided.
  322. if server:
  323. raise e
  324. limit: Optional[int] = parse_integer(request, "limit", 0)
  325. since_token = parse_string(request, "since")
  326. if limit == 0:
  327. # zero is a special value which corresponds to no limit.
  328. limit = None
  329. handler = self.hs.get_room_list_handler()
  330. if server and server != self.hs.config.server.server_name:
  331. # Ensure the server is valid.
  332. try:
  333. parse_and_validate_server_name(server)
  334. except ValueError:
  335. raise SynapseError(
  336. 400,
  337. "Invalid server name: %s" % (server,),
  338. Codes.INVALID_PARAM,
  339. )
  340. data = await handler.get_remote_public_room_list(
  341. server, limit=limit, since_token=since_token
  342. )
  343. else:
  344. data = await handler.get_local_public_room_list(
  345. limit=limit, since_token=since_token
  346. )
  347. return 200, data
  348. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  349. await self.auth.get_user_by_req(request, allow_guest=True)
  350. server = parse_string(request, "server")
  351. content = parse_json_object_from_request(request)
  352. limit: Optional[int] = int(content.get("limit", 100))
  353. since_token = content.get("since", None)
  354. search_filter = content.get("filter", None)
  355. include_all_networks = content.get("include_all_networks", False)
  356. third_party_instance_id = content.get("third_party_instance_id", None)
  357. if include_all_networks:
  358. network_tuple = None
  359. if third_party_instance_id is not None:
  360. raise SynapseError(
  361. 400, "Can't use include_all_networks with an explicit network"
  362. )
  363. elif third_party_instance_id is None:
  364. network_tuple = ThirdPartyInstanceID(None, None)
  365. else:
  366. network_tuple = ThirdPartyInstanceID.from_string(third_party_instance_id)
  367. if limit == 0:
  368. # zero is a special value which corresponds to no limit.
  369. limit = None
  370. handler = self.hs.get_room_list_handler()
  371. if server and server != self.hs.config.server.server_name:
  372. # Ensure the server is valid.
  373. try:
  374. parse_and_validate_server_name(server)
  375. except ValueError:
  376. raise SynapseError(
  377. 400,
  378. "Invalid server name: %s" % (server,),
  379. Codes.INVALID_PARAM,
  380. )
  381. data = await handler.get_remote_public_room_list(
  382. server,
  383. limit=limit,
  384. since_token=since_token,
  385. search_filter=search_filter,
  386. include_all_networks=include_all_networks,
  387. third_party_instance_id=third_party_instance_id,
  388. )
  389. else:
  390. data = await handler.get_local_public_room_list(
  391. limit=limit,
  392. since_token=since_token,
  393. search_filter=search_filter,
  394. network_tuple=network_tuple,
  395. )
  396. return 200, data
  397. # TODO: Needs unit testing
  398. class RoomMemberListRestServlet(RestServlet):
  399. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)
  400. def __init__(self, hs: "HomeServer"):
  401. super().__init__()
  402. self.message_handler = hs.get_message_handler()
  403. self.auth = hs.get_auth()
  404. self.store = hs.get_datastores().main
  405. @cancellable
  406. async def on_GET(
  407. self, request: SynapseRequest, room_id: str
  408. ) -> Tuple[int, JsonDict]:
  409. # TODO support Pagination stream API (limit/tokens)
  410. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  411. handler = self.message_handler
  412. # request the state as of a given event, as identified by a stream token,
  413. # for consistency with /messages etc.
  414. # useful for getting the membership in retrospect as of a given /sync
  415. # response.
  416. at_token_string = parse_string(request, "at")
  417. if at_token_string is None:
  418. at_token = None
  419. else:
  420. at_token = await StreamToken.from_string(self.store, at_token_string)
  421. # let you filter down on particular memberships.
  422. # XXX: this may not be the best shape for this API - we could pass in a filter
  423. # instead, except filters aren't currently aware of memberships.
  424. # See https://github.com/matrix-org/matrix-doc/issues/1337 for more details.
  425. membership = parse_string(request, "membership")
  426. not_membership = parse_string(request, "not_membership")
  427. events = await handler.get_state_events(
  428. room_id=room_id,
  429. user_id=requester.user.to_string(),
  430. at_token=at_token,
  431. state_filter=StateFilter.from_types([(EventTypes.Member, None)]),
  432. )
  433. chunk = []
  434. for event in events:
  435. if (membership and event["content"].get("membership") != membership) or (
  436. not_membership and event["content"].get("membership") == not_membership
  437. ):
  438. continue
  439. chunk.append(event)
  440. return 200, {"chunk": chunk}
  441. # deprecated in favour of /members?membership=join?
  442. # except it does custom AS logic and has a simpler return format
  443. class JoinedRoomMemberListRestServlet(RestServlet):
  444. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/joined_members$", v1=True)
  445. def __init__(self, hs: "HomeServer"):
  446. super().__init__()
  447. self.message_handler = hs.get_message_handler()
  448. self.auth = hs.get_auth()
  449. async def on_GET(
  450. self, request: SynapseRequest, room_id: str
  451. ) -> Tuple[int, JsonDict]:
  452. requester = await self.auth.get_user_by_req(request)
  453. users_with_profile = await self.message_handler.get_joined_members(
  454. requester, room_id
  455. )
  456. return 200, {"joined": users_with_profile}
  457. # TODO: Needs better unit testing
  458. class RoomMessageListRestServlet(RestServlet):
  459. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/messages$", v1=True)
  460. def __init__(self, hs: "HomeServer"):
  461. super().__init__()
  462. self._hs = hs
  463. self.pagination_handler = hs.get_pagination_handler()
  464. self.auth = hs.get_auth()
  465. self.store = hs.get_datastores().main
  466. async def on_GET(
  467. self, request: SynapseRequest, room_id: str
  468. ) -> Tuple[int, JsonDict]:
  469. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  470. pagination_config = await PaginationConfig.from_request(
  471. self.store, request, default_limit=10
  472. )
  473. # Twisted will have processed the args by now.
  474. assert request.args is not None
  475. as_client_event = b"raw" not in request.args
  476. filter_str = parse_string(request, "filter", encoding="utf-8")
  477. if filter_str:
  478. filter_json = urlparse.unquote(filter_str)
  479. event_filter: Optional[Filter] = Filter(
  480. self._hs, json_decoder.decode(filter_json)
  481. )
  482. if (
  483. event_filter
  484. and event_filter.filter_json.get("event_format", "client")
  485. == "federation"
  486. ):
  487. as_client_event = False
  488. else:
  489. event_filter = None
  490. msgs = await self.pagination_handler.get_messages(
  491. room_id=room_id,
  492. requester=requester,
  493. pagin_config=pagination_config,
  494. as_client_event=as_client_event,
  495. event_filter=event_filter,
  496. )
  497. return 200, msgs
  498. # TODO: Needs unit testing
  499. class RoomStateRestServlet(RestServlet):
  500. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)
  501. def __init__(self, hs: "HomeServer"):
  502. super().__init__()
  503. self.message_handler = hs.get_message_handler()
  504. self.auth = hs.get_auth()
  505. @cancellable
  506. async def on_GET(
  507. self, request: SynapseRequest, room_id: str
  508. ) -> Tuple[int, List[JsonDict]]:
  509. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  510. # Get all the current state for this room
  511. events = await self.message_handler.get_state_events(
  512. room_id=room_id,
  513. user_id=requester.user.to_string(),
  514. is_guest=requester.is_guest,
  515. )
  516. return 200, events
  517. # TODO: Needs unit testing
  518. class RoomInitialSyncRestServlet(RestServlet):
  519. PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)
  520. def __init__(self, hs: "HomeServer"):
  521. super().__init__()
  522. self.initial_sync_handler = hs.get_initial_sync_handler()
  523. self.auth = hs.get_auth()
  524. self.store = hs.get_datastores().main
  525. async def on_GET(
  526. self, request: SynapseRequest, room_id: str
  527. ) -> Tuple[int, JsonDict]:
  528. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  529. pagination_config = await PaginationConfig.from_request(self.store, request)
  530. content = await self.initial_sync_handler.room_initial_sync(
  531. room_id=room_id, requester=requester, pagin_config=pagination_config
  532. )
  533. return 200, content
  534. class RoomEventServlet(RestServlet):
  535. PATTERNS = client_patterns(
  536. "/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True
  537. )
  538. def __init__(self, hs: "HomeServer"):
  539. super().__init__()
  540. self.clock = hs.get_clock()
  541. self._store = hs.get_datastores().main
  542. self._state = hs.get_state_handler()
  543. self.event_handler = hs.get_event_handler()
  544. self._event_serializer = hs.get_event_client_serializer()
  545. self._relations_handler = hs.get_relations_handler()
  546. self.auth = hs.get_auth()
  547. self.content_keep_ms = hs.config.server.redaction_retention_period
  548. self.msc2815_enabled = hs.config.experimental.msc2815_enabled
  549. async def on_GET(
  550. self, request: SynapseRequest, room_id: str, event_id: str
  551. ) -> Tuple[int, JsonDict]:
  552. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  553. include_unredacted_content = self.msc2815_enabled and (
  554. parse_string(
  555. request,
  556. "fi.mau.msc2815.include_unredacted_content",
  557. allowed_values=("true", "false"),
  558. )
  559. == "true"
  560. )
  561. if include_unredacted_content and not await self.auth.is_server_admin(
  562. requester.user
  563. ):
  564. power_level_event = await self._state.get_current_state(
  565. room_id, EventTypes.PowerLevels, ""
  566. )
  567. auth_events = {}
  568. if power_level_event:
  569. auth_events[(EventTypes.PowerLevels, "")] = power_level_event
  570. redact_level = event_auth.get_named_level(auth_events, "redact", 50)
  571. user_level = event_auth.get_user_power_level(
  572. requester.user.to_string(), auth_events
  573. )
  574. if user_level < redact_level:
  575. raise SynapseError(
  576. 403,
  577. "You don't have permission to view redacted events in this room.",
  578. errcode=Codes.FORBIDDEN,
  579. )
  580. try:
  581. event = await self.event_handler.get_event(
  582. requester.user,
  583. room_id,
  584. event_id,
  585. show_redacted=include_unredacted_content,
  586. )
  587. except AuthError:
  588. # This endpoint is supposed to return a 404 when the requester does
  589. # not have permission to access the event
  590. # https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-rooms-roomid-event-eventid
  591. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  592. if event:
  593. if include_unredacted_content and await self._store.have_censored_event(
  594. event_id
  595. ):
  596. raise UnredactedContentDeletedError(self.content_keep_ms)
  597. # Ensure there are bundled aggregations available.
  598. aggregations = await self._relations_handler.get_bundled_aggregations(
  599. [event], requester.user.to_string()
  600. )
  601. time_now = self.clock.time_msec()
  602. # per MSC2676, /rooms/{roomId}/event/{eventId}, should return the
  603. # *original* event, rather than the edited version
  604. event_dict = self._event_serializer.serialize_event(
  605. event, time_now, bundle_aggregations=aggregations, apply_edits=False
  606. )
  607. return 200, event_dict
  608. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  609. class RoomEventContextServlet(RestServlet):
  610. PATTERNS = client_patterns(
  611. "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True
  612. )
  613. def __init__(self, hs: "HomeServer"):
  614. super().__init__()
  615. self._hs = hs
  616. self.clock = hs.get_clock()
  617. self.room_context_handler = hs.get_room_context_handler()
  618. self._event_serializer = hs.get_event_client_serializer()
  619. self.auth = hs.get_auth()
  620. async def on_GET(
  621. self, request: SynapseRequest, room_id: str, event_id: str
  622. ) -> Tuple[int, JsonDict]:
  623. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  624. limit = parse_integer(request, "limit", default=10)
  625. # picking the API shape for symmetry with /messages
  626. filter_str = parse_string(request, "filter", encoding="utf-8")
  627. if filter_str:
  628. filter_json = urlparse.unquote(filter_str)
  629. event_filter: Optional[Filter] = Filter(
  630. self._hs, json_decoder.decode(filter_json)
  631. )
  632. else:
  633. event_filter = None
  634. event_context = await self.room_context_handler.get_event_context(
  635. requester, room_id, event_id, limit, event_filter
  636. )
  637. if not event_context:
  638. raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
  639. time_now = self.clock.time_msec()
  640. results = {
  641. "events_before": self._event_serializer.serialize_events(
  642. event_context.events_before,
  643. time_now,
  644. bundle_aggregations=event_context.aggregations,
  645. ),
  646. "event": self._event_serializer.serialize_event(
  647. event_context.event,
  648. time_now,
  649. bundle_aggregations=event_context.aggregations,
  650. ),
  651. "events_after": self._event_serializer.serialize_events(
  652. event_context.events_after,
  653. time_now,
  654. bundle_aggregations=event_context.aggregations,
  655. ),
  656. "state": self._event_serializer.serialize_events(
  657. event_context.state, time_now
  658. ),
  659. "start": event_context.start,
  660. "end": event_context.end,
  661. }
  662. return 200, results
  663. class RoomForgetRestServlet(TransactionRestServlet):
  664. def __init__(self, hs: "HomeServer"):
  665. super().__init__(hs)
  666. self.room_member_handler = hs.get_room_member_handler()
  667. self.auth = hs.get_auth()
  668. def register(self, http_server: HttpServer) -> None:
  669. PATTERNS = "/rooms/(?P<room_id>[^/]*)/forget"
  670. register_txn_path(self, PATTERNS, http_server)
  671. async def on_POST(
  672. self, request: SynapseRequest, room_id: str, txn_id: Optional[str] = None
  673. ) -> Tuple[int, JsonDict]:
  674. requester = await self.auth.get_user_by_req(request, allow_guest=False)
  675. await self.room_member_handler.forget(user=requester.user, room_id=room_id)
  676. return 200, {}
  677. def on_PUT(
  678. self, request: SynapseRequest, room_id: str, txn_id: str
  679. ) -> Awaitable[Tuple[int, JsonDict]]:
  680. set_tag("txn_id", txn_id)
  681. return self.txns.fetch_or_execute_request(
  682. request, self.on_POST, request, room_id, txn_id
  683. )
  684. # TODO: Needs unit testing
  685. class RoomMembershipRestServlet(TransactionRestServlet):
  686. def __init__(self, hs: "HomeServer"):
  687. super().__init__(hs)
  688. self.room_member_handler = hs.get_room_member_handler()
  689. self.auth = hs.get_auth()
  690. def register(self, http_server: HttpServer) -> None:
  691. # /rooms/$roomid/[invite|join|leave]
  692. PATTERNS = (
  693. "/rooms/(?P<room_id>[^/]*)/"
  694. "(?P<membership_action>join|invite|leave|ban|unban|kick)"
  695. )
  696. register_txn_path(self, PATTERNS, http_server)
  697. async def on_POST(
  698. self,
  699. request: SynapseRequest,
  700. room_id: str,
  701. membership_action: str,
  702. txn_id: Optional[str] = None,
  703. ) -> Tuple[int, JsonDict]:
  704. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  705. if requester.is_guest and membership_action not in {
  706. Membership.JOIN,
  707. Membership.LEAVE,
  708. }:
  709. raise AuthError(403, "Guest access not allowed")
  710. try:
  711. content = parse_json_object_from_request(request)
  712. except Exception:
  713. # Turns out we used to ignore the body entirely, and some clients
  714. # cheekily send invalid bodies.
  715. content = {}
  716. if membership_action == "invite" and self._has_3pid_invite_keys(content):
  717. try:
  718. await self.room_member_handler.do_3pid_invite(
  719. room_id,
  720. requester.user,
  721. content["medium"],
  722. content["address"],
  723. content["id_server"],
  724. requester,
  725. txn_id,
  726. content.get("id_access_token"),
  727. )
  728. except ShadowBanError:
  729. # Pretend the request succeeded.
  730. pass
  731. return 200, {}
  732. target = requester.user
  733. if membership_action in ["invite", "ban", "unban", "kick"]:
  734. assert_params_in_dict(content, ["user_id"])
  735. target = UserID.from_string(content["user_id"])
  736. event_content = None
  737. if "reason" in content:
  738. event_content = {"reason": content["reason"]}
  739. try:
  740. await self.room_member_handler.update_membership(
  741. requester=requester,
  742. target=target,
  743. room_id=room_id,
  744. action=membership_action,
  745. txn_id=txn_id,
  746. third_party_signed=content.get("third_party_signed", None),
  747. content=event_content,
  748. )
  749. except ShadowBanError:
  750. # Pretend the request succeeded.
  751. pass
  752. return_value = {}
  753. if membership_action == "join":
  754. return_value["room_id"] = room_id
  755. return 200, return_value
  756. def _has_3pid_invite_keys(self, content: JsonDict) -> bool:
  757. for key in {"id_server", "medium", "address"}:
  758. if key not in content:
  759. return False
  760. return True
  761. def on_PUT(
  762. self, request: SynapseRequest, room_id: str, membership_action: str, txn_id: str
  763. ) -> Awaitable[Tuple[int, JsonDict]]:
  764. set_tag("txn_id", txn_id)
  765. return self.txns.fetch_or_execute_request(
  766. request, self.on_POST, request, room_id, membership_action, txn_id
  767. )
  768. class RoomRedactEventRestServlet(TransactionRestServlet):
  769. def __init__(self, hs: "HomeServer"):
  770. super().__init__(hs)
  771. self.event_creation_handler = hs.get_event_creation_handler()
  772. self.auth = hs.get_auth()
  773. def register(self, http_server: HttpServer) -> None:
  774. PATTERNS = "/rooms/(?P<room_id>[^/]*)/redact/(?P<event_id>[^/]*)"
  775. register_txn_path(self, PATTERNS, http_server)
  776. async def on_POST(
  777. self,
  778. request: SynapseRequest,
  779. room_id: str,
  780. event_id: str,
  781. txn_id: Optional[str] = None,
  782. ) -> Tuple[int, JsonDict]:
  783. requester = await self.auth.get_user_by_req(request)
  784. content = parse_json_object_from_request(request)
  785. try:
  786. (
  787. event,
  788. _,
  789. ) = await self.event_creation_handler.create_and_send_nonmember_event(
  790. requester,
  791. {
  792. "type": EventTypes.Redaction,
  793. "content": content,
  794. "room_id": room_id,
  795. "sender": requester.user.to_string(),
  796. "redacts": event_id,
  797. },
  798. txn_id=txn_id,
  799. )
  800. event_id = event.event_id
  801. except ShadowBanError:
  802. event_id = "$" + random_string(43)
  803. set_tag("event_id", event_id)
  804. return 200, {"event_id": event_id}
  805. def on_PUT(
  806. self, request: SynapseRequest, room_id: str, event_id: str, txn_id: str
  807. ) -> Awaitable[Tuple[int, JsonDict]]:
  808. set_tag("txn_id", txn_id)
  809. return self.txns.fetch_or_execute_request(
  810. request, self.on_POST, request, room_id, event_id, txn_id
  811. )
  812. class RoomTypingRestServlet(RestServlet):
  813. PATTERNS = client_patterns(
  814. "/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True
  815. )
  816. def __init__(self, hs: "HomeServer"):
  817. super().__init__()
  818. self.hs = hs
  819. self.presence_handler = hs.get_presence_handler()
  820. self.auth = hs.get_auth()
  821. # If we're not on the typing writer instance we should scream if we get
  822. # requests.
  823. self._is_typing_writer = (
  824. hs.get_instance_name() in hs.config.worker.writers.typing
  825. )
  826. async def on_PUT(
  827. self, request: SynapseRequest, room_id: str, user_id: str
  828. ) -> Tuple[int, JsonDict]:
  829. requester = await self.auth.get_user_by_req(request)
  830. if not self._is_typing_writer:
  831. raise Exception("Got /typing request on instance that is not typing writer")
  832. room_id = urlparse.unquote(room_id)
  833. target_user = UserID.from_string(urlparse.unquote(user_id))
  834. content = parse_json_object_from_request(request)
  835. await self.presence_handler.bump_presence_active_time(requester.user)
  836. # Limit timeout to stop people from setting silly typing timeouts.
  837. timeout = min(content.get("timeout", 30000), 120000)
  838. # Defer getting the typing handler since it will raise on workers.
  839. typing_handler = self.hs.get_typing_writer_handler()
  840. try:
  841. if content["typing"]:
  842. await typing_handler.started_typing(
  843. target_user=target_user,
  844. requester=requester,
  845. room_id=room_id,
  846. timeout=timeout,
  847. )
  848. else:
  849. await typing_handler.stopped_typing(
  850. target_user=target_user, requester=requester, room_id=room_id
  851. )
  852. except ShadowBanError:
  853. # Pretend this worked without error.
  854. pass
  855. return 200, {}
  856. class RoomAliasListServlet(RestServlet):
  857. PATTERNS = [
  858. re.compile(
  859. r"^/_matrix/client/unstable/org\.matrix\.msc2432"
  860. r"/rooms/(?P<room_id>[^/]*)/aliases"
  861. ),
  862. ] + list(client_patterns("/rooms/(?P<room_id>[^/]*)/aliases$", unstable=False))
  863. def __init__(self, hs: "HomeServer"):
  864. super().__init__()
  865. self.auth = hs.get_auth()
  866. self.directory_handler = hs.get_directory_handler()
  867. async def on_GET(
  868. self, request: SynapseRequest, room_id: str
  869. ) -> Tuple[int, JsonDict]:
  870. requester = await self.auth.get_user_by_req(request)
  871. alias_list = await self.directory_handler.get_aliases_for_room(
  872. requester, room_id
  873. )
  874. return 200, {"aliases": alias_list}
  875. class SearchRestServlet(RestServlet):
  876. PATTERNS = client_patterns("/search$", v1=True)
  877. def __init__(self, hs: "HomeServer"):
  878. super().__init__()
  879. self.search_handler = hs.get_search_handler()
  880. self.auth = hs.get_auth()
  881. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  882. requester = await self.auth.get_user_by_req(request)
  883. content = parse_json_object_from_request(request)
  884. batch = parse_string(request, "next_batch")
  885. results = await self.search_handler.search(requester.user, content, batch)
  886. return 200, results
  887. class JoinedRoomsRestServlet(RestServlet):
  888. PATTERNS = client_patterns("/joined_rooms$", v1=True)
  889. def __init__(self, hs: "HomeServer"):
  890. super().__init__()
  891. self.store = hs.get_datastores().main
  892. self.auth = hs.get_auth()
  893. async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  894. requester = await self.auth.get_user_by_req(request, allow_guest=True)
  895. room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
  896. return 200, {"joined_rooms": list(room_ids)}
  897. def register_txn_path(
  898. servlet: RestServlet,
  899. regex_string: str,
  900. http_server: HttpServer,
  901. with_get: bool = False,
  902. ) -> None:
  903. """Registers a transaction-based path.
  904. This registers two paths:
  905. PUT regex_string/$txnid
  906. POST regex_string
  907. Args:
  908. regex_string: The regex string to register. Must NOT have a
  909. trailing $ as this string will be appended to.
  910. http_server: The http_server to register paths with.
  911. with_get: True to also register respective GET paths for the PUTs.
  912. """
  913. on_POST = getattr(servlet, "on_POST", None)
  914. on_PUT = getattr(servlet, "on_PUT", None)
  915. if on_POST is None or on_PUT is None:
  916. raise RuntimeError("on_POST and on_PUT must exist when using register_txn_path")
  917. http_server.register_paths(
  918. "POST",
  919. client_patterns(regex_string + "$", v1=True),
  920. on_POST,
  921. servlet.__class__.__name__,
  922. )
  923. http_server.register_paths(
  924. "PUT",
  925. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  926. on_PUT,
  927. servlet.__class__.__name__,
  928. )
  929. on_GET = getattr(servlet, "on_GET", None)
  930. if with_get:
  931. if on_GET is None:
  932. raise RuntimeError(
  933. "register_txn_path called with with_get = True, but no on_GET method exists"
  934. )
  935. http_server.register_paths(
  936. "GET",
  937. client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
  938. on_GET,
  939. servlet.__class__.__name__,
  940. )
  941. class TimestampLookupRestServlet(RestServlet):
  942. """
  943. API endpoint to fetch the `event_id` of the closest event to the given
  944. timestamp (`ts` query parameter) in the given direction (`dir` query
  945. parameter).
  946. Useful for cases like jump to date so you can start paginating messages from
  947. a given date in the archive.
  948. `ts` is a timestamp in milliseconds where we will find the closest event in
  949. the given direction.
  950. `dir` can be `f` or `b` to indicate forwards and backwards in time from the
  951. given timestamp.
  952. GET /_matrix/client/unstable/org.matrix.msc3030/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
  953. {
  954. "event_id": ...
  955. }
  956. """
  957. PATTERNS = (
  958. re.compile(
  959. "^/_matrix/client/unstable/org.matrix.msc3030"
  960. "/rooms/(?P<room_id>[^/]*)/timestamp_to_event$"
  961. ),
  962. )
  963. def __init__(self, hs: "HomeServer"):
  964. super().__init__()
  965. self._auth = hs.get_auth()
  966. self._store = hs.get_datastores().main
  967. self.timestamp_lookup_handler = hs.get_timestamp_lookup_handler()
  968. async def on_GET(
  969. self, request: SynapseRequest, room_id: str
  970. ) -> Tuple[int, JsonDict]:
  971. requester = await self._auth.get_user_by_req(request)
  972. await self._auth.check_user_in_room(room_id, requester.user.to_string())
  973. timestamp = parse_integer(request, "ts", required=True)
  974. direction = parse_string(request, "dir", default="f", allowed_values=["f", "b"])
  975. (
  976. event_id,
  977. origin_server_ts,
  978. ) = await self.timestamp_lookup_handler.get_event_for_timestamp(
  979. requester, room_id, timestamp, direction
  980. )
  981. return 200, {
  982. "event_id": event_id,
  983. "origin_server_ts": origin_server_ts,
  984. }
  985. class RoomHierarchyRestServlet(RestServlet):
  986. PATTERNS = (
  987. re.compile(
  988. "^/_matrix/client/(v1|unstable/org.matrix.msc2946)"
  989. "/rooms/(?P<room_id>[^/]*)/hierarchy$"
  990. ),
  991. )
  992. def __init__(self, hs: "HomeServer"):
  993. super().__init__()
  994. self._auth = hs.get_auth()
  995. self._room_summary_handler = hs.get_room_summary_handler()
  996. async def on_GET(
  997. self, request: SynapseRequest, room_id: str
  998. ) -> Tuple[int, JsonDict]:
  999. requester = await self._auth.get_user_by_req(request, allow_guest=True)
  1000. max_depth = parse_integer(request, "max_depth")
  1001. if max_depth is not None and max_depth < 0:
  1002. raise SynapseError(
  1003. 400, "'max_depth' must be a non-negative integer", Codes.BAD_JSON
  1004. )
  1005. limit = parse_integer(request, "limit")
  1006. if limit is not None and limit <= 0:
  1007. raise SynapseError(
  1008. 400, "'limit' must be a positive integer", Codes.BAD_JSON
  1009. )
  1010. return 200, await self._room_summary_handler.get_room_hierarchy(
  1011. requester,
  1012. room_id,
  1013. suggested_only=parse_boolean(request, "suggested_only", default=False),
  1014. max_depth=max_depth,
  1015. limit=limit,
  1016. from_token=parse_string(request, "from"),
  1017. )
  1018. class RoomSummaryRestServlet(ResolveRoomIdMixin, RestServlet):
  1019. PATTERNS = (
  1020. re.compile(
  1021. "^/_matrix/client/unstable/im.nheko.summary"
  1022. "/rooms/(?P<room_identifier>[^/]*)/summary$"
  1023. ),
  1024. )
  1025. def __init__(self, hs: "HomeServer"):
  1026. super().__init__(hs)
  1027. self._auth = hs.get_auth()
  1028. self._room_summary_handler = hs.get_room_summary_handler()
  1029. async def on_GET(
  1030. self, request: SynapseRequest, room_identifier: str
  1031. ) -> Tuple[int, JsonDict]:
  1032. try:
  1033. requester = await self._auth.get_user_by_req(request, allow_guest=True)
  1034. requester_user_id: Optional[str] = requester.user.to_string()
  1035. except MissingClientTokenError:
  1036. # auth is optional
  1037. requester_user_id = None
  1038. # twisted.web.server.Request.args is incorrectly defined as Optional[Any]
  1039. args: Dict[bytes, List[bytes]] = request.args # type: ignore
  1040. remote_room_hosts = parse_strings_from_args(args, "via", required=False)
  1041. room_id, remote_room_hosts = await self.resolve_room_id(
  1042. room_identifier,
  1043. remote_room_hosts,
  1044. )
  1045. return 200, await self._room_summary_handler.get_room_summary(
  1046. requester_user_id,
  1047. room_id,
  1048. remote_room_hosts,
  1049. )
  1050. def register_servlets(
  1051. hs: "HomeServer", http_server: HttpServer, is_worker: bool = False
  1052. ) -> None:
  1053. RoomStateEventRestServlet(hs).register(http_server)
  1054. RoomMemberListRestServlet(hs).register(http_server)
  1055. JoinedRoomMemberListRestServlet(hs).register(http_server)
  1056. RoomMessageListRestServlet(hs).register(http_server)
  1057. JoinRoomAliasServlet(hs).register(http_server)
  1058. RoomMembershipRestServlet(hs).register(http_server)
  1059. RoomSendEventRestServlet(hs).register(http_server)
  1060. PublicRoomListRestServlet(hs).register(http_server)
  1061. RoomStateRestServlet(hs).register(http_server)
  1062. RoomRedactEventRestServlet(hs).register(http_server)
  1063. RoomTypingRestServlet(hs).register(http_server)
  1064. RoomEventContextServlet(hs).register(http_server)
  1065. RoomHierarchyRestServlet(hs).register(http_server)
  1066. if hs.config.experimental.msc3266_enabled:
  1067. RoomSummaryRestServlet(hs).register(http_server)
  1068. RoomEventServlet(hs).register(http_server)
  1069. JoinedRoomsRestServlet(hs).register(http_server)
  1070. RoomAliasListServlet(hs).register(http_server)
  1071. SearchRestServlet(hs).register(http_server)
  1072. RoomCreateRestServlet(hs).register(http_server)
  1073. if hs.config.experimental.msc3030_enabled:
  1074. TimestampLookupRestServlet(hs).register(http_server)
  1075. # Some servlets only get registered for the main process.
  1076. if not is_worker:
  1077. RoomForgetRestServlet(hs).register(http_server)
  1078. def register_deprecated_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
  1079. RoomInitialSyncRestServlet(hs).register(http_server)