def account_data(self, writer, current_token, limit): current_position = current_token.account_data user_account_data = parse_integer(writer.request, "user_account_data") room_account_data = parse_integer(writer.request, "room_account_data") tag_account_data = parse_integer(writer.request, "tag_account_data") if user_account_data is not None or room_account_data is not None: if user_account_data is None: user_account_data = current_position if room_account_data is None: room_account_data = current_position user_rows, room_rows = yield self.store.get_all_updated_account_data( user_account_data, room_account_data, current_position, limit ) writer.write_header_and_rows("user_account_data", user_rows, ( "position", "user_id", "type", "content" )) writer.write_header_and_rows("room_account_data", room_rows, ( "position", "user_id", "room_id", "type", "content" )) if tag_account_data is not None: tag_rows = yield self.store.get_all_updated_tags( tag_account_data, current_position, limit ) writer.write_header_and_rows("tag_account_data", tag_rows, ( "position", "user_id", "room_id", "tags" ))
def on_GET(self, request, target_user_id): """Get request to get specific number of users from Synapse. This needs user to have administrator access in Synapse. """ target_user = UserID.from_string(target_user_id) requester = yield self.auth.get_user_by_req(request) is_admin = yield self.auth.is_server_admin(requester.user) if not is_admin: raise AuthError(403, "You are not a server admin") # To allow all users to get the users list # if not is_admin and target_user != auth_user: # raise AuthError(403, "You are not a server admin") if not self.hs.is_mine(target_user): raise SynapseError(400, "Can only users a local user") order = "name" # order by name in user table start = parse_integer(request, "start", required=True) limit = parse_integer(request, "limit", required=True) logger.info("limit: %s, start: %s", limit, start) ret = yield self.handlers.admin_handler.get_users_paginate( order, start, limit ) defer.returnValue((200, ret))
def _async_render_GET(self, request): limit = parse_integer(request, "limit", 100) timeout = parse_integer(request, "timeout", 10 * 1000) request.setHeader(b"Content-Type", b"application/json") request_streams = { name: parse_integer(request, name) for names in STREAM_NAMES for name in names } request_streams["streams"] = parse_string(request, "streams") def replicate(): return self.replicate(request_streams, limit) result = yield self.notifier.wait_for_replication(replicate, timeout) for stream_name, stream_content in result.items(): logger.info( "Replicating %d rows of %s from %s -> %s", len(stream_content["rows"]), stream_name, request_streams.get(stream_name), stream_content["position"], ) request.write(json.dumps(result, ensure_ascii=False)) finish_request(request)
def _async_render_GET(self, request): server_name, media_id, _ = parse_media_id(request) width = parse_integer(request, "width") height = parse_integer(request, "height") method = parse_string(request, "method", "scale") m_type = parse_string(request, "type", "image/png") if server_name == self.server_name: if self.dynamic_thumbnails: yield self._select_or_generate_local_thumbnail( request, media_id, width, height, method, m_type ) else: yield self._respond_local_thumbnail( request, media_id, width, height, method, m_type ) else: if self.dynamic_thumbnails: yield self._select_or_generate_remote_thumbnail( request, server_name, media_id, width, height, method, m_type ) else: yield self._respond_remote_thumbnail( request, server_name, media_id, width, height, method, m_type )
def _async_render_GET(self, request): limit = parse_integer(request, "limit", 100) timeout = parse_integer(request, "timeout", 10 * 1000) request.setHeader(b"Content-Type", b"application/json") writer = _Writer(request) @defer.inlineCallbacks def replicate(): current_token = yield self.current_replication_token() logger.info("Replicating up to %r", current_token) yield self.account_data(writer, current_token, limit) yield self.events(writer, current_token, limit) yield self.presence(writer, current_token) # TODO: implement limit yield self.typing(writer, current_token) # TODO: implement limit yield self.receipts(writer, current_token, limit) yield self.push_rules(writer, current_token, limit) yield self.pushers(writer, current_token, limit) self.streams(writer, current_token) logger.info("Replicated %d rows", writer.total) defer.returnValue(writer.total) yield self.notifier.wait_for_replication(replicate, timeout) writer.finish()
def events(self, writer, current_token, limit): request_events = parse_integer(writer.request, "events") request_backfill = parse_integer(writer.request, "backfill") if request_events is not None or request_backfill is not None: if request_events is None: request_events = current_token.events if request_backfill is None: request_backfill = current_token.backfill events_rows, backfill_rows = yield self.store.get_all_new_events( request_backfill, request_events, current_token.backfill, current_token.events, limit ) writer.write_header_and_rows("events", events_rows, ("position", "internal", "json")) writer.write_header_and_rows("backfill", backfill_rows, ("position", "internal", "json"))
def on_GET(self, request): user, client = yield self.auth.get_user_by_req(request) timeout = parse_integer(request, "timeout", default=0) limit = parse_integer(request, "limit", required=True) gap = parse_boolean(request, "gap", default=True) sort = parse_string(request, "sort", default="timeline,asc", allowed_values=self.ALLOWED_SORT) since = parse_string(request, "since") set_presence = parse_string(request, "set_presence", default="online", allowed_values=self.ALLOWED_PRESENCE) backfill = parse_boolean(request, "backfill", default=False) filter_id = parse_string(request, "filter", default=None) logger.info( "/sync: user=%r, timeout=%r, limit=%r, gap=%r, sort=%r, since=%r," " set_presence=%r, backfill=%r, filter_id=%r" % (user, timeout, limit, gap, sort, since, set_presence, backfill, filter_id) ) # TODO(mjark): Load filter and apply overrides. try: filter = yield self.filtering.get_user_filter(user.localpart, filter_id) except: filter = Filter({}) # filter = filter.apply_overrides(http_request) # if filter.matches(event): # # stuff sync_config = SyncConfig( user=user, client_info=client, gap=gap, limit=limit, sort=sort, backfill=backfill, filter=filter ) if since is not None: since_token = StreamToken.from_string(since) else: since_token = None sync_result = yield self.sync_handler.wait_for_sync_for_user( sync_config, since_token=since_token, timeout=timeout ) time_now = self.clock.time_msec() response_content = { "public_user_data": self.encode_user_data(sync_result.public_user_data, filter, time_now), "private_user_data": self.encode_user_data(sync_result.private_user_data, filter, time_now), "rooms": self.encode_rooms(sync_result.rooms, filter, time_now, client.token_id), "next_batch": sync_result.next_batch.to_string(), } defer.returnValue((200, response_content))
def pushers(self, writer, current_token, limit): current_position = current_token.pushers pushers = parse_integer(writer.request, "pushers") if pushers is not None: updated, deleted = yield self.store.get_all_updated_pushers(pushers, current_position, limit) writer.write_header_and_rows( "pushers", updated, ( "position", "user_id", "access_token", "profile_tag", "kind", "app_id", "app_display_name", "device_display_name", "pushkey", "ts", "lang", "data", ), ) writer.write_header_and_rows("deleted", deleted, ("position", "user_id", "app_id", "pushkey"))
def on_GET(self, request): server = parse_string(request, "server", default=None) try: yield self.auth.get_user_by_req(request, allow_guest=True) except AuthError as e: # We allow people to not be authed if they're just looking at our # room list, but require auth when we proxy the request. # In both cases we call the auth function, as that has the side # effect of logging who issued this request if an access token was # provided. if server: raise e else: pass limit = parse_integer(request, "limit", 0) since_token = parse_string(request, "since", None) handler = self.hs.get_room_list_handler() if server: data = yield handler.get_remote_public_room_list( server, limit=limit, since_token=since_token, ) else: data = yield handler.get_local_public_room_list( limit=limit, since_token=since_token, ) defer.returnValue((200, data))
def from_request(cls, request, raise_invalid_params=True, default_limit=None): direction = parse_string(request, "dir", default='f', allowed_values=['f', 'b']) from_tok = parse_string(request, "from") to_tok = parse_string(request, "to") try: if from_tok == "END": from_tok = None # For backwards compat. elif from_tok: from_tok = StreamToken.from_string(from_tok) except Exception: raise SynapseError(400, "'from' paramater is invalid") try: if to_tok: to_tok = StreamToken.from_string(to_tok) except Exception: raise SynapseError(400, "'to' paramater is invalid") limit = parse_integer(request, "limit", default=default_limit) if limit and limit < 0: raise SynapseError(400, "Limit must be 0 or above") try: return PaginationConfig(from_tok, to_tok, direction, limit) except Exception: logger.exception("Failed to create pagination config") raise SynapseError(400, "Invalid request.")
def on_GET(self, request, room_id, parent_id, relation_type=None, event_type=None): requester = yield self.auth.get_user_by_req(request, allow_guest=True) yield self.auth.check_in_room_or_world_readable( room_id, requester.user.to_string() ) # This checks that a) the event exists and b) the user is allowed to # view it. yield self.event_handler.get_event(requester.user, room_id, parent_id) if relation_type not in (RelationTypes.ANNOTATION, None): raise SynapseError(400, "Relation type must be 'annotation'") limit = parse_integer(request, "limit", default=5) from_token = parse_string(request, "from") to_token = parse_string(request, "to") if from_token: from_token = AggregationPaginationToken.from_string(from_token) if to_token: to_token = AggregationPaginationToken.from_string(to_token) res = yield self.store.get_aggregation_groups_for_event( event_id=parent_id, event_type=event_type, limit=limit, from_token=from_token, to_token=to_token, ) defer.returnValue((200, res.to_dict()))
def typing(self, writer, current_token): current_position = current_token.presence request_typing = parse_integer(writer.request, "typing") if request_typing is not None: typing_rows = yield self.typing_handler.get_all_typing_updates(request_typing, current_position) writer.write_header_and_rows("typing", typing_rows, ("position", "room_id", "typing"))
def on_GET(self, request, user_id, device_id, algorithm): yield self.auth.get_user_by_req(request) timeout = parse_integer(request, "timeout", 10 * 1000) result = yield self.e2e_keys_handler.claim_one_time_keys( {"one_time_keys": {user_id: {device_id: algorithm}}}, timeout, ) defer.returnValue((200, result))
def on_POST(self, request, user_id, device_id, algorithm): yield self.auth.get_user_by_req(request) timeout = parse_integer(request, "timeout", 10 * 1000) body = parse_json_object_from_request(request) result = yield self.e2e_keys_handler.claim_one_time_keys( body, timeout, ) defer.returnValue((200, result))
def on_POST(self, request): yield assert_requester_is_admin(self.auth, request) before_ts = parse_integer(request, "before_ts", required=True) logger.info("before_ts: %r", before_ts) ret = yield self.media_repository.delete_old_remote_media(before_ts) defer.returnValue((200, ret))
def on_GET(self, request): requester = yield self.auth.get_user_by_req(request) user_id = requester.user.to_string() from_token = parse_string(request, "from", required=False) limit = parse_integer(request, "limit", default=50) limit = min(limit, 500) push_actions = yield self.store.get_push_actions_for_user( user_id, from_token, limit) receipts_by_room = yield self.store.get_receipts_for_user_with_orderings( user_id, 'm.read') notif_event_ids = [pa["event_id"] for pa in push_actions] notif_events = yield self.store.get_events(notif_event_ids) returned_push_actions = [] next_token = None for pa in push_actions: returned_pa = { "room_id": pa["room_id"], "profile_tag": pa["profile_tag"], "actions": pa["actions"], "ts": pa["received_ts"], "event": serialize_event( notif_events[pa["event_id"]], self.clock.time_msec(), event_format=format_event_for_client_v2_without_room_id, ), } if pa["room_id"] not in receipts_by_room: returned_pa["read"] = False else: receipt = receipts_by_room[pa["room_id"]] returned_pa["read"] = (receipt["topological_ordering"], receipt["stream_ordering"]) >= ( pa["topological_ordering"], pa["stream_ordering"]) returned_push_actions.append(returned_pa) next_token = pa["stream_ordering"] defer.returnValue((200, { "notifications": returned_push_actions, "next_token": next_token, }))
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) user_id = requester.user.to_string() from_token = parse_string(request, "from", required=False) limit = parse_integer(request, "limit", default=50) only = parse_string(request, "only", required=False) limit = min(limit, 500) push_actions = await self.store.get_push_actions_for_user( user_id, from_token, limit, only_highlight=(only == "highlight")) receipts_by_room = await self.store.get_receipts_for_user_with_orderings( user_id, ReceiptTypes.READ) notif_event_ids = [pa.event_id for pa in push_actions] notif_events = await self.store.get_events(notif_event_ids) returned_push_actions = [] next_token = None for pa in push_actions: returned_pa = { "room_id": pa.room_id, "profile_tag": pa.profile_tag, "actions": pa.actions, "ts": pa.received_ts, "event": (self._event_serializer.serialize_event( notif_events[pa.event_id], self.clock.time_msec(), event_format=format_event_for_client_v2_without_room_id, )), } if pa.room_id not in receipts_by_room: returned_pa["read"] = False else: receipt = receipts_by_room[pa.room_id] returned_pa["read"] = ( receipt["topological_ordering"], receipt["stream_ordering"], ) >= (pa.topological_ordering, pa.stream_ordering) returned_push_actions.append(returned_pa) next_token = str(pa.stream_ordering) return 200, { "notifications": returned_push_actions, "next_token": next_token }
async def _handle_request(self, request, stream_name): stream = self.streams.get(stream_name) if stream is None: raise SynapseError(400, "Unknown stream") from_token = parse_integer(request, "from_token", required=True) upto_token = parse_integer(request, "upto_token", required=True) updates, upto_token, limited = await stream.get_updates_since( self._instance_name, from_token, upto_token) return ( 200, { "updates": updates, "upto_token": upto_token, "limited": limited }, )
def typing(self, writer, current_token): current_position = current_token.presence request_typing = parse_integer(writer.request, "typing") if request_typing is not None: typing_rows = yield self.typing_handler.get_all_typing_updates( request_typing, current_position) writer.write_header_and_rows("typing", typing_rows, ("position", "room_id", "typing"))
def receipts(self, writer, current_token, limit): current_position = current_token.receipts request_receipts = parse_integer(writer.request, "receipts") if request_receipts is not None: receipts_rows = yield self.store.get_all_updated_receipts(request_receipts, current_position, limit) writer.write_header_and_rows( "receipts", receipts_rows, ("position", "room_id", "receipt_type", "user_id", "event_id", "data") )
def on_GET(self, request, target_user_id): """Get request to get specific number of users from Synapse. This needs user to have administrator access in Synapse. """ yield assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(target_user_id) if not self.hs.is_mine(target_user): raise SynapseError(400, "Can only users a local user") order = "name" # order by name in user table start = parse_integer(request, "start", required=True) limit = parse_integer(request, "limit", required=True) logger.info("limit: %s, start: %s", limit, start) ret = yield self.handlers.admin_handler.get_users_paginate(order, start, limit) return (200, ret)
async def on_GET( self, request: SynapseRequest, room_id: str, parent_id: str, relation_type: Optional[str] = None, event_type: Optional[str] = None, ) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=True) await self.auth.check_user_in_room_or_world_readable( room_id, requester.user.to_string(), allow_departed_users=True, ) # This checks that a) the event exists and b) the user is allowed to # view it. event = await self.event_handler.get_event(requester.user, room_id, parent_id) if event is None: raise SynapseError(404, "Unknown parent event.") if relation_type not in (RelationTypes.ANNOTATION, None): raise SynapseError( 400, f"Relation type must be '{RelationTypes.ANNOTATION}'") limit = parse_integer(request, "limit", default=5) from_token_str = parse_string(request, "from") to_token_str = parse_string(request, "to") if event.internal_metadata.is_redacted(): # If the event is redacted, return an empty list of relations pagination_chunk = PaginationChunk(chunk=[]) else: # Return the relations from_token = None if from_token_str: from_token = AggregationPaginationToken.from_string( from_token_str) to_token = None if to_token_str: to_token = AggregationPaginationToken.from_string(to_token_str) pagination_chunk = await self.store.get_aggregation_groups_for_event( event_id=parent_id, room_id=room_id, event_type=event_type, limit=limit, from_token=from_token, to_token=to_token, ) return 200, pagination_chunk.to_dict()
def on_GET(self, request): requester = yield self.auth.get_user_by_req(request) user_id = requester.user.to_string() from_token = parse_string(request, "from", required=False) limit = parse_integer(request, "limit", default=50) only = parse_string(request, "only", required=False) limit = min(limit, 500) push_actions = yield self.store.get_push_actions_for_user( user_id, from_token, limit, only_highlight=(only == "highlight") ) receipts_by_room = yield self.store.get_receipts_for_user_with_orderings( user_id, 'm.read' ) notif_event_ids = [pa["event_id"] for pa in push_actions] notif_events = yield self.store.get_events(notif_event_ids) returned_push_actions = [] next_token = None for pa in push_actions: returned_pa = { "room_id": pa["room_id"], "profile_tag": pa["profile_tag"], "actions": pa["actions"], "ts": pa["received_ts"], "event": serialize_event( notif_events[pa["event_id"]], self.clock.time_msec(), event_format=format_event_for_client_v2_without_room_id, ), } if pa["room_id"] not in receipts_by_room: returned_pa["read"] = False else: receipt = receipts_by_room[pa["room_id"]] returned_pa["read"] = ( receipt["topological_ordering"], receipt["stream_ordering"] ) >= ( pa["topological_ordering"], pa["stream_ordering"] ) returned_push_actions.append(returned_pa) next_token = str(pa["stream_ordering"]) defer.returnValue((200, { "notifications": returned_push_actions, "next_token": next_token, }))
def on_GET(self, request, user_id, device_id): requester = yield self.auth.get_user_by_req(request) timeout = parse_integer(request, "timeout", 10 * 1000) auth_user_id = requester.user.to_string() user_id = user_id if user_id else auth_user_id device_ids = [device_id] if device_id else [] result = yield self.e2e_keys_handler.query_devices( {"device_keys": {user_id: device_ids}}, timeout, ) defer.returnValue((200, result))
async def on_GET( self, request: SynapseRequest, room_id: str ) -> Tuple[int, JsonDict]: requester = await self._auth.get_user_by_req(request, allow_guest=True) return 200, await self._space_summary_handler.get_space_summary( requester.user.to_string(), room_id, suggested_only=parse_boolean(request, "suggested_only", default=False), max_rooms_per_space=parse_integer(request, "max_rooms_per_space"), )
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self._auth, request) start = parse_integer(request, "from", default=0) limit = parse_integer(request, "limit", default=100) if start < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter from must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter limit must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) destination = parse_string(request, "destination") order_by = parse_string( request, "order_by", default=DestinationSortOrder.DESTINATION.value, allowed_values=[dest.value for dest in DestinationSortOrder], ) direction = parse_string(request, "dir", default="f", allowed_values=("f", "b")) destinations, total = await self._store.get_destinations_paginate( start, limit, destination, order_by, direction) response = {"destinations": destinations, "total": total} if (start + limit) < total: response["next_token"] = str(start + len(destinations)) return HTTPStatus.OK, response
def receipts(self, writer, current_token, limit): current_position = current_token.receipts request_receipts = parse_integer(writer.request, "receipts") if request_receipts is not None: receipts_rows = yield self.store.get_all_updated_receipts( request_receipts, current_position, limit) writer.write_header_and_rows( "receipts", receipts_rows, ("position", "room_id", "receipt_type", "user_id", "event_id", "data"))
def push_rules(self, writer, current_token, limit): current_position = current_token.push_rules push_rules = parse_integer(writer.request, "push_rules") if push_rules is not None: rows = yield self.store.get_all_push_rule_updates( push_rules, current_position, limit) writer.write_header_and_rows( "push_rules", rows, ("position", "event_stream_ordering", "user_id", "rule_id", "op", "priority_class", "priority", "conditions", "actions"))
async def on_GET(self, request: SynapseRequest, room_id: str, event_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=True) limit = parse_integer(request, "limit", default=10) # picking the API shape for symmetry with /messages filter_str = parse_string(request, "filter", encoding="utf-8") if filter_str: filter_json = urlparse.unquote(filter_str) event_filter: Optional[Filter] = Filter( self._hs, json_decoder.decode(filter_json)) else: event_filter = None event_context = await self.room_context_handler.get_event_context( requester, room_id, event_id, limit, event_filter) if not event_context: raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND) time_now = self.clock.time_msec() results = { "events_before": self._event_serializer.serialize_events( event_context.events_before, time_now, bundle_aggregations=event_context.aggregations, ), "event": self._event_serializer.serialize_event( event_context.event, time_now, bundle_aggregations=event_context.aggregations, ), "events_after": self._event_serializer.serialize_events( event_context.events_after, time_now, bundle_aggregations=event_context.aggregations, ), "state": self._event_serializer.serialize_events(event_context.state, time_now), "start": event_context.start, "end": event_context.end, } return 200, results
def on_GET(self, request, target_user_id): """Get request to get specific number of users from Synapse. This needs user to have administrator access in Synapse. """ yield assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(target_user_id) if not self.hs.is_mine(target_user): raise SynapseError(400, "Can only users a local user") order = "name" # order by name in user table start = parse_integer(request, "start", required=True) limit = parse_integer(request, "limit", required=True) logger.info("limit: %s, start: %s", limit, start) ret = yield self.handlers.admin_handler.get_users_paginate( order, start, limit ) defer.returnValue((200, ret))
async def on_POST(self, request: SynapseRequest, server_name: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) before_ts = parse_integer(request, "before_ts", required=True) size_gt = parse_integer(request, "size_gt", default=0) keep_profiles = parse_boolean(request, "keep_profiles", default=True) if before_ts < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter before_ts must be a positive integer.", errcode=Codes.INVALID_PARAM, ) elif before_ts < 30000000000: # Dec 1970 in milliseconds, Aug 2920 in seconds raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter before_ts you provided is from the year 1970. " + "Double check that you are providing a timestamp in milliseconds.", errcode=Codes.INVALID_PARAM, ) if size_gt < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter size_gt must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if self.server_name != server_name: raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only delete local media") logging.info( "Deleting local media by timestamp: %s, size larger than: %s, keep profile media: %s" % (before_ts, size_gt, keep_profiles)) deleted_media, total = await self.media_repository.delete_old_local_media( before_ts, size_gt, keep_profiles) return HTTPStatus.OK, {"deleted_media": deleted_media, "total": total}
def push_rules(self, writer, current_token, limit): current_position = current_token.push_rules push_rules = parse_integer(writer.request, "push_rules") if push_rules is not None: rows = yield self.store.get_all_push_rule_updates( push_rules, current_position, limit ) writer.write_header_and_rows("push_rules", rows, ( "position", "event_stream_ordering", "user_id", "rule_id", "op", "priority_class", "priority", "conditions", "actions" ))
def on_POST(self, request): requester = yield self.auth.get_user_by_req(request) is_admin = yield self.auth.is_server_admin(requester.user) if not is_admin: raise AuthError(403, "You are not a server admin") before_ts = parse_integer(request, "before_ts", required=True) logger.info("before_ts: %r", before_ts) ret = yield self.media_repository.delete_old_remote_media(before_ts) defer.returnValue((200, ret))
async def on_GET(self, request): server = parse_string(request, "server", default=None) try: await self.auth.get_user_by_req(request, allow_guest=True) except InvalidClientCredentialsError as e: # Option to allow servers to require auth when accessing # /publicRooms via CS API. This is especially helpful in private # federations. if not self.hs.config.allow_public_rooms_without_auth: raise # We allow people to not be authed if they're just looking at our # room list, but require auth when we proxy the request. # In both cases we call the auth function, as that has the side # effect of logging who issued this request if an access token was # provided. if server: raise e limit = parse_integer(request, "limit", 0) since_token = parse_string(request, "since", None) if limit == 0: # zero is a special value which corresponds to no limit. limit = None handler = self.hs.get_room_list_handler() if server and server != self.hs.config.server_name: # Ensure the server is valid. try: parse_and_validate_server_name(server) except ValueError: raise SynapseError( 400, "Invalid server name: %s" % (server,), Codes.INVALID_PARAM, ) try: data = await handler.get_remote_public_room_list( server, limit=limit, since_token=since_token ) except HttpResponseException as e: raise e.to_synapse_error() else: data = await handler.get_local_public_room_list( limit=limit, since_token=since_token ) return 200, data
async def on_GET( self, request: SynapseRequest, user_id: str ) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) if not self.is_mine(UserID.from_string(user_id)): raise SynapseError(400, "Can only lookup local users") user = await self.store.get_user_by_id(user_id) if user is None: raise NotFoundError("Unknown user") start = parse_integer(request, "from", default=0) limit = parse_integer(request, "limit", default=100) if start < 0: raise SynapseError( 400, "Query parameter from must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( 400, "Query parameter limit must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) media, total = await self.store.get_local_media_by_user_paginate( start, limit, user_id ) ret = {"media": media, "total": total} if (start + limit) < total: ret["next_token"] = start + len(media) return 200, ret
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) start = parse_integer(request, "from", default=0) limit = parse_integer(request, "limit", default=100) direction = parse_string(request, "dir", default="b") user_id = parse_string(request, "user_id") room_id = parse_string(request, "room_id") if start < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "The start parameter must be a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "The limit parameter must be a positive integer.", errcode=Codes.INVALID_PARAM, ) if direction not in ("f", "b"): raise SynapseError( HTTPStatus.BAD_REQUEST, "Unknown direction: %s" % (direction,), errcode=Codes.INVALID_PARAM, ) event_reports, total = await self.store.get_event_reports_paginate( start, limit, direction, user_id, room_id ) ret = {"event_reports": event_reports, "total": total} if (start + limit) < total: ret["next_token"] = start + len(event_reports) return HTTPStatus.OK, ret
def presence(self, writer, current_token): current_position = current_token.presence request_presence = parse_integer(writer.request, "presence") if request_presence is not None: presence_rows = yield self.presence_handler.get_all_presence_updates( request_presence, current_position ) writer.write_header_and_rows("presence", presence_rows, ( "position", "user_id", "state", "last_active_ts", "last_federation_update_ts", "last_user_sync_ts", "status_msg", "currently_active", ))
def _async_render_GET(self, request): server_name, media_id, _ = parse_media_id(request) width = parse_integer(request, "width") height = parse_integer(request, "height") method = parse_string(request, "method", "scale") m_type = parse_string(request, "type", "image/png") if server_name == self.server_name: if self.dynamic_thumbnails: yield self._select_or_generate_local_thumbnail( request, media_id, width, height, method, m_type) else: yield self._respond_local_thumbnail(request, media_id, width, height, method, m_type) else: if self.dynamic_thumbnails: yield self._select_or_generate_remote_thumbnail( request, server_name, media_id, width, height, method, m_type) else: yield self._respond_remote_thumbnail(request, server_name, media_id, width, height, method, m_type)
async def on_GET(self, request, room_id, parent_id, relation_type, event_type, key): requester = await self.auth.get_user_by_req(request, allow_guest=True) await self.auth.check_user_in_room_or_world_readable( room_id, requester.user.to_string(), allow_departed_users=True, ) # This checks that a) the event exists and b) the user is allowed to # view it. await self.event_handler.get_event(requester.user, room_id, parent_id) if relation_type != RelationTypes.ANNOTATION: raise SynapseError(400, "Relation type must be 'annotation'") limit = parse_integer(request, "limit", default=5) from_token_str = parse_string(request, "from") to_token_str = parse_string(request, "to") from_token = None if from_token_str: from_token = RelationPaginationToken.from_string(from_token_str) to_token = None if to_token_str: to_token = RelationPaginationToken.from_string(to_token_str) result = await self.store.get_relations_for_event( event_id=parent_id, relation_type=relation_type, event_type=event_type, aggregation_key=key, limit=limit, from_token=from_token, to_token=to_token, ) events = await self.store.get_events_as_list( [c["event_id"] for c in result.chunk]) now = self.clock.time_msec() events = await self._event_serializer.serialize_events(events, now) return_value = result.to_dict() return_value["chunk"] = events return 200, return_value
async def _async_render_GET(self, request): if len(request.postpath) == 1: (server,) = request.postpath query = {server.decode("ascii"): {}} elif len(request.postpath) == 2: server, key_id = request.postpath minimum_valid_until_ts = parse_integer(request, "minimum_valid_until_ts") arguments = {} if minimum_valid_until_ts is not None: arguments["minimum_valid_until_ts"] = minimum_valid_until_ts query = {server.decode("ascii"): {key_id.decode("ascii"): arguments}} else: raise SynapseError(404, "Not found %r" % request.postpath, Codes.NOT_FOUND) await self.query_keys(request, query, query_remote_on_cache_miss=True)
def pushers(self, writer, current_token, limit): current_position = current_token.pushers pushers = parse_integer(writer.request, "pushers") if pushers is not None: updated, deleted = yield self.store.get_all_updated_pushers( pushers, current_position, limit) writer.write_header_and_rows( "pushers", updated, ("position", "user_id", "access_token", "profile_tag", "kind", "app_id", "app_display_name", "device_display_name", "pushkey", "ts", "lang", "data")) writer.write_header_and_rows( "deleted", deleted, ("position", "user_id", "app_id", "pushkey"))
async def on_GET(self, request: SynapseRequest, destination: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self._auth, request) if not await self._store.is_destination_known(destination): raise NotFoundError("Unknown destination") start = parse_integer(request, "from", default=0) limit = parse_integer(request, "limit", default=100) if start < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter from must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "Query parameter limit must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) direction = parse_string(request, "dir", default="f", allowed_values=("f", "b")) rooms, total = await self._store.get_destination_rooms_paginate( destination, start, limit, direction) response = {"rooms": rooms, "total": total} if (start + limit) < total: response["next_token"] = str(start + len(rooms)) return HTTPStatus.OK, response
async def on_GET( self, request: SynapseRequest, room_id: str, event_id: str ) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=False) await assert_user_is_admin(self.auth, requester.user) limit = parse_integer(request, "limit", default=10) # picking the API shape for symmetry with /messages filter_str = parse_string(request, b"filter", encoding="utf-8") if filter_str: filter_json = urlparse.unquote(filter_str) event_filter = Filter( json_decoder.decode(filter_json) ) # type: Optional[Filter] else: event_filter = None results = await self.room_context_handler.get_event_context( requester, room_id, event_id, limit, event_filter, use_admin_priviledge=True, ) if not results: raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND) time_now = self.clock.time_msec() results["events_before"] = await self._event_serializer.serialize_events( results["events_before"], time_now ) results["event"] = await self._event_serializer.serialize_event( results["event"], time_now ) results["events_after"] = await self._event_serializer.serialize_events( results["events_after"], time_now ) results["state"] = await self._event_serializer.serialize_events( results["state"], time_now, # No need to bundle aggregations for state events bundle_aggregations=False, ) return 200, results
def on_GET(self, request, room_id, event_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) limit = parse_integer(request, "limit", default=10) # picking the API shape for symmetry with /messages filter_bytes = parse_string(request, "filter") if filter_bytes: filter_json = urlparse.unquote(filter_bytes) event_filter = Filter(json.loads(filter_json)) else: event_filter = None results = yield self.room_context_handler.get_event_context( requester.user, room_id, event_id, limit, event_filter, ) if not results: raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND) time_now = self.clock.time_msec() results[ "events_before"] = yield self._event_serializer.serialize_events( results["events_before"], time_now, ) results["event"] = yield self._event_serializer.serialize_event( results["event"], time_now, ) results[ "events_after"] = yield self._event_serializer.serialize_events( results["events_after"], time_now, ) results["state"] = yield self._event_serializer.serialize_events( results["state"], time_now, ) defer.returnValue((200, results))
async def on_GET( self, request: SynapseRequest, room_id: str, parent_id: str, relation_type: Optional[str] = None, event_type: Optional[str] = None, ) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request, allow_guest=True) limit = parse_integer(request, "limit", default=5) if self._msc3715_enabled: direction = parse_string( request, "org.matrix.msc3715.dir", default="b", allowed_values=["f", "b"], ) else: direction = "b" from_token_str = parse_string(request, "from") to_token_str = parse_string(request, "to") # Return the relations from_token = None if from_token_str: from_token = await StreamToken.from_string(self.store, from_token_str) to_token = None if to_token_str: to_token = await StreamToken.from_string(self.store, to_token_str) result = await self._relations_handler.get_relations( requester=requester, event_id=parent_id, room_id=room_id, relation_type=relation_type, event_type=event_type, limit=limit, direction=direction, from_token=from_token, to_token=to_token, ) return 200, result
def on_GET(self, request, room_id, parent_id, relation_type, event_type, key): requester = yield self.auth.get_user_by_req(request, allow_guest=True) yield self.auth.check_in_room_or_world_readable( room_id, requester.user.to_string() ) # This checks that a) the event exists and b) the user is allowed to # view it. yield self.event_handler.get_event(requester.user, room_id, parent_id) if relation_type != RelationTypes.ANNOTATION: raise SynapseError(400, "Relation type must be 'annotation'") limit = parse_integer(request, "limit", default=5) from_token = parse_string(request, "from") to_token = parse_string(request, "to") if from_token: from_token = RelationPaginationToken.from_string(from_token) if to_token: to_token = RelationPaginationToken.from_string(to_token) result = yield self.store.get_relations_for_event( event_id=parent_id, relation_type=relation_type, event_type=event_type, aggregation_key=key, limit=limit, from_token=from_token, to_token=to_token, ) events = yield self.store.get_events_as_list( [c["event_id"] for c in result.chunk] ) now = self.clock.time_msec() events = yield self._event_serializer.serialize_events(events, now) return_value = result.to_dict() return_value["chunk"] = events defer.returnValue((200, return_value))
def on_POST(self, request, room_id, event_type, txn_id=None): requester = yield self.auth.get_user_by_req(request, allow_guest=True) content = parse_json_object_from_request(request) event_dict = { "type": event_type, "content": content, "room_id": room_id, "sender": requester.user.to_string(), } if b"ts" in request.args and requester.app_service: event_dict["origin_server_ts"] = parse_integer(request, "ts", 0) event = yield self.event_creation_handler.create_and_send_nonmember_event( requester, event_dict, txn_id=txn_id) return (200, {"event_id": event.event_id})
def async_render_GET(self, request): if len(request.postpath) == 1: server, = request.postpath query = {server: {}} elif len(request.postpath) == 2: server, key_id = request.postpath minimum_valid_until_ts = parse_integer( request, "minimum_valid_until_ts" ) arguments = {} if minimum_valid_until_ts is not None: arguments["minimum_valid_until_ts"] = minimum_valid_until_ts query = {server: {key_id: arguments}} else: raise SynapseError( 404, "Not found %r" % request.postpath, Codes.NOT_FOUND ) yield self.query_keys(request, query, query_remote_on_cache_miss=True)
def presence(self, writer, current_token): current_position = current_token.presence request_presence = parse_integer(writer.request, "presence") if request_presence is not None: presence_rows = yield self.presence_handler.get_all_presence_updates( request_presence, current_position) writer.write_header_and_rows("presence", presence_rows, ( "position", "user_id", "state", "last_active_ts", "last_federation_update_ts", "last_user_sync_ts", "status_msg", "currently_active", ))
def on_GET(self, request, room_id, parent_id, relation_type=None, event_type=None): requester = yield self.auth.get_user_by_req(request, allow_guest=True) yield self.auth.check_in_room_or_world_readable( room_id, requester.user.to_string()) # This checks that a) the event exists and b) the user is allowed to # view it. yield self.event_handler.get_event(requester.user, room_id, parent_id) limit = parse_integer(request, "limit", default=5) from_token = parse_string(request, "from") to_token = parse_string(request, "to") if from_token: from_token = RelationPaginationToken.from_string(from_token) if to_token: to_token = RelationPaginationToken.from_string(to_token) result = yield self.store.get_relations_for_event( event_id=parent_id, relation_type=relation_type, event_type=event_type, limit=limit, from_token=from_token, to_token=to_token, ) events = yield self.store.get_events_as_list( [c["event_id"] for c in result.chunk]) now = self.clock.time_msec() events = yield self._event_serializer.serialize_events(events, now) return_value = result.to_dict() return_value["chunk"] = events defer.returnValue((200, return_value))
async def from_request( cls, store: "DataStore", request: SynapseRequest, raise_invalid_params: bool = True, default_limit: Optional[int] = None, ) -> "PaginationConfig": direction = parse_string(request, "dir", default="f", allowed_values=["f", "b"]) from_tok_str = parse_string(request, "from") to_tok_str = parse_string(request, "to") try: from_tok = None if from_tok_str == "END": from_tok = None # For backwards compat. elif from_tok_str: from_tok = await StreamToken.from_string(store, from_tok_str) except Exception: raise SynapseError(400, "'from' parameter is invalid") try: to_tok = None if to_tok_str: to_tok = await StreamToken.from_string(store, to_tok_str) except Exception: raise SynapseError(400, "'to' parameter is invalid") limit = parse_integer(request, "limit", default=default_limit) if limit: if limit < 0: raise SynapseError(400, "Limit must be 0 or above") limit = min(int(limit), MAX_LIMIT) try: return PaginationConfig(from_tok, to_tok, direction, limit) except Exception: logger.exception("Failed to create pagination config") raise SynapseError(400, "Invalid request.")
async def on_GET( self, request: SynapseRequest, room_id: str ) -> Tuple[int, JsonDict]: requester = await self._auth.get_user_by_req(request, allow_guest=True) max_rooms_per_space = parse_integer(request, "max_rooms_per_space") if max_rooms_per_space is not None and max_rooms_per_space < 0: raise SynapseError( 400, "Value for 'max_rooms_per_space' must be a non-negative integer", Codes.BAD_JSON, ) return 200, await self._room_summary_handler.get_space_summary( requester.user.to_string(), room_id, suggested_only=parse_boolean(request, "suggested_only", default=False), max_rooms_per_space=max_rooms_per_space, )
def on_GET(self, request, room_id, event_id): requester = yield self.auth.get_user_by_req(request, allow_guest=True) limit = parse_integer(request, "limit", default=10) # picking the API shape for symmetry with /messages filter_bytes = parse_string(request, "filter") if filter_bytes: filter_json = urlparse.unquote(filter_bytes) event_filter = Filter(json.loads(filter_json)) else: event_filter = None results = yield self.room_context_handler.get_event_context( requester.user, room_id, event_id, limit, event_filter, ) if not results: raise SynapseError( 404, "Event not found.", errcode=Codes.NOT_FOUND ) time_now = self.clock.time_msec() results["events_before"] = yield self._event_serializer.serialize_events( results["events_before"], time_now, ) results["event"] = yield self._event_serializer.serialize_event( results["event"], time_now, ) results["events_after"] = yield self._event_serializer.serialize_events( results["events_after"], time_now, ) results["state"] = yield self._event_serializer.serialize_events( results["state"], time_now, ) defer.returnValue((200, results))
def on_POST(self, request, room_id, event_type, txn_id=None): requester = yield self.auth.get_user_by_req(request, allow_guest=True) content = parse_json_object_from_request(request) event_dict = { "type": event_type, "content": content, "room_id": room_id, "sender": requester.user.to_string(), } if b'ts' in request.args and requester.app_service: event_dict['origin_server_ts'] = parse_integer(request, "ts", 0) event = yield self.event_creation_hander.create_and_send_nonmember_event( requester, event_dict, txn_id=txn_id, ) defer.returnValue((200, {"event_id": event.event_id}))
def on_GET(self, request): if "from" in request.args: # /events used to use 'from', but /sync uses 'since'. # Lets be helpful and whine if we see a 'from'. raise SynapseError( 400, "'from' is not a valid query parameter. Did you mean 'since'?" ) requester = yield self.auth.get_user_by_req( request, allow_guest=True ) user = requester.user device_id = requester.device_id timeout = parse_integer(request, "timeout", default=0) since = parse_string(request, "since") set_presence = parse_string( request, "set_presence", default="online", allowed_values=self.ALLOWED_PRESENCE ) filter_id = parse_string(request, "filter", default=None) full_state = parse_boolean(request, "full_state", default=False) logger.debug( "/sync: user=%r, timeout=%r, since=%r," " set_presence=%r, filter_id=%r, device_id=%r" % ( user, timeout, since, set_presence, filter_id, device_id ) ) request_key = (user, timeout, since, filter_id, full_state, device_id) if filter_id: if filter_id.startswith('{'): try: filter_object = json.loads(filter_id) set_timeline_upper_limit(filter_object, self.hs.config.filter_timeline_limit) except Exception: raise SynapseError(400, "Invalid filter JSON") self.filtering.check_valid_filter(filter_object) filter = FilterCollection(filter_object) else: filter = yield self.filtering.get_user_filter( user.localpart, filter_id ) else: filter = DEFAULT_FILTER_COLLECTION sync_config = SyncConfig( user=user, filter_collection=filter, is_guest=requester.is_guest, request_key=request_key, device_id=device_id, ) if since is not None: since_token = StreamToken.from_string(since) else: since_token = None affect_presence = set_presence != PresenceState.OFFLINE if affect_presence: yield self.presence_handler.set_state(user, {"presence": set_presence}, True) context = yield self.presence_handler.user_syncing( user.to_string(), affect_presence=affect_presence, ) with context: sync_result = yield self.sync_handler.wait_for_sync_for_user( sync_config, since_token=since_token, timeout=timeout, full_state=full_state ) time_now = self.clock.time_msec() response_content = self.encode_response( time_now, sync_result, requester.access_token_id, filter ) defer.returnValue((200, response_content))
def on_GET(self, request): user, token_id, _ = yield self.auth.get_user_by_req(request) timeout = parse_integer(request, "timeout", default=0) since = parse_string(request, "since") set_presence = parse_string( request, "set_presence", default="online", allowed_values=self.ALLOWED_PRESENCE ) filter_id = parse_string(request, "filter", default=None) full_state = parse_boolean(request, "full_state", default=False) logger.info( "/sync: user=%r, timeout=%r, since=%r," " set_presence=%r, filter_id=%r" % ( user, timeout, since, set_presence, filter_id ) ) try: filter = yield self.filtering.get_user_filter( user.localpart, filter_id ) except: filter = FilterCollection({}) sync_config = SyncConfig( user=user, filter=filter, ) if since is not None: since_token = StreamToken.from_string(since) else: since_token = None if set_presence == "online": yield self.event_stream_handler.started_stream(user) try: sync_result = yield self.sync_handler.wait_for_sync_for_user( sync_config, since_token=since_token, timeout=timeout, full_state=full_state ) finally: if set_presence == "online": self.event_stream_handler.stopped_stream(user) time_now = self.clock.time_msec() joined = self.encode_joined( sync_result.joined, filter, time_now, token_id ) invited = self.encode_invited( sync_result.invited, filter, time_now, token_id ) archived = self.encode_archived( sync_result.archived, filter, time_now, token_id ) response_content = { "presence": self.encode_presence( sync_result.presence, filter, time_now ), "rooms": { "join": joined, "invite": invited, "leave": archived, }, "next_batch": sync_result.next_batch.to_string(), } defer.returnValue((200, response_content))
def on_POST(self, request): yield self.auth.get_user_by_req(request, allow_guest=True) timeout = parse_integer(request, "timeout", 10 * 1000) body = parse_json_object_from_request(request) result = yield self.e2e_keys_handler.query_devices(body, timeout) defer.returnValue((200, result))
def _async_render_GET(self, request): # XXX: if get_user_by_req fails, what should we do in an async render? requester = yield self.auth.get_user_by_req(request) url = parse_string(request, "url") if b"ts" in request.args: ts = parse_integer(request, "ts") else: ts = self.clock.time_msec() # XXX: we could move this into _do_preview if we wanted. url_tuple = urlparse.urlsplit(url) for entry in self.url_preview_url_blacklist: match = True for attrib in entry: pattern = entry[attrib] value = getattr(url_tuple, attrib) logger.debug(( "Matching attrib '%s' with value '%s' against" " pattern '%s'" ) % (attrib, value, pattern)) if value is None: match = False continue if pattern.startswith('^'): if not re.match(pattern, getattr(url_tuple, attrib)): match = False continue else: if not fnmatch.fnmatch(getattr(url_tuple, attrib), pattern): match = False continue if match: logger.warn( "URL %s blocked by url_blacklist entry %s", url, entry ) raise SynapseError( 403, "URL blocked by url pattern blacklist entry", Codes.UNKNOWN ) # the in-memory cache: # * ensures that only one request is active at a time # * takes load off the DB for the thundering herds # * also caches any failures (unlike the DB) so we don't keep # requesting the same endpoint observable = self._cache.get(url) if not observable: download = run_in_background( self._do_preview, url, requester.user, ts, ) observable = ObservableDeferred( download, consumeErrors=True ) self._cache[url] = observable else: logger.info("Returning cached response") og = yield make_deferred_yieldable(observable.observe()) respond_with_json_bytes(request, 200, og, send_cors=True)