Example #1
0
File: events.py Project: Xe/synapse
    def on_GET(self, request):
        auth_user, _, is_guest = yield self.auth.get_user_by_req(
            request,
            allow_guest=True
        )
        room_id = None
        if is_guest:
            if "room_id" not in request.args:
                raise SynapseError(400, "Guest users must specify room_id param")
            room_id = request.args["room_id"][0]
        try:
            handler = self.handlers.event_stream_handler
            pagin_config = PaginationConfig.from_request(request)
            timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS
            if "timeout" in request.args:
                try:
                    timeout = int(request.args["timeout"][0])
                except ValueError:
                    raise SynapseError(400, "timeout must be in milliseconds.")

            as_client_event = "raw" not in request.args

            chunk = yield handler.get_stream(
                auth_user.to_string(), pagin_config, timeout=timeout,
                as_client_event=as_client_event, affect_presence=(not is_guest),
                room_id=room_id, is_guest=is_guest
            )
        except:
            logger.exception("Event stream failed")
            raise

        defer.returnValue((200, chunk))
Example #2
0
    def on_GET(self, request):
        requester = yield self.auth.get_user_by_req(
            request,
            allow_guest=True,
        )
        is_guest = requester.is_guest
        room_id = None
        if is_guest:
            if b"room_id" not in request.args:
                raise SynapseError(400, "Guest users must specify room_id param")
        if b"room_id" in request.args:
            room_id = request.args[b"room_id"][0].decode('ascii')

        pagin_config = PaginationConfig.from_request(request)
        timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS
        if b"timeout" in request.args:
            try:
                timeout = int(request.args[b"timeout"][0])
            except ValueError:
                raise SynapseError(400, "timeout must be in milliseconds.")

        as_client_event = b"raw" not in request.args

        chunk = yield self.event_stream_handler.get_stream(
            requester.user.to_string(),
            pagin_config,
            timeout=timeout,
            as_client_event=as_client_event,
            affect_presence=(not is_guest),
            room_id=room_id,
            is_guest=is_guest,
        )

        defer.returnValue((200, chunk))
Example #3
0
 def on_GET(self, request, room_id):
     user = yield self.auth.get_user_by_req(request)
     pagination_config = PaginationConfig.from_request(request)
     content = yield self.handlers.message_handler.room_initial_sync(
         room_id=urllib.unquote(room_id),
         user_id=user.to_string(),
         pagin_config=pagination_config,
     )
     defer.returnValue((200, content))
Example #4
0
 def on_GET(self, request, room_id):
     requester = yield self.auth.get_user_by_req(request, allow_guest=True)
     pagination_config = PaginationConfig.from_request(request)
     content = yield self.handlers.message_handler.room_initial_sync(
         room_id=room_id,
         requester=requester,
         pagin_config=pagination_config,
     )
     defer.returnValue((200, content))
Example #5
0
    def initial_sync(self, sync_config):
        """Get a sync for a client which is starting without any state
        Returns:
            A Deferred SyncResult.
        """
        if sync_config.sort == "timeline,desc":
            # TODO(mjark): Handle going through events in reverse order?.
            # What does "most recent events" mean when applying the limits mean
            # in this case?
            raise NotImplementedError()

        now_token = yield self.event_sources.get_current_token()

        presence_stream = self.event_sources.sources["presence"]
        # TODO (mjark): This looks wrong, shouldn't we be getting the presence
        # UP to the present rather than after the present?
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user=sync_config.user,
            pagination_config=pagination_config.get_source_config("presence"),
            key=None
        )
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=sync_config.user.to_string(),
            membership_list=[Membership.INVITE, Membership.JOIN]
        )

        # TODO (mjark): Does public mean "published"?
        published_rooms = yield self.store.get_rooms(is_public=True)
        published_room_ids = set(r["room_id"] for r in published_rooms)

        rooms = []
        for event in room_list:
            room_sync = yield self.initial_sync_for_room(
                event.room_id, sync_config, now_token, published_room_ids
            )
            rooms.append(room_sync)

        defer.returnValue(SyncResult(
            public_user_data=presence,
            private_user_data=[],
            rooms=rooms,
            next_batch=now_token,
        ))
Example #6
0
    def on_GET(self, request):
        user = yield self.auth.get_user_by_req(request)
        with_feedback = "feedback" in request.args
        pagination_config = PaginationConfig.from_request(request)
        handler = self.handlers.message_handler
        content = yield handler.snapshot_all_rooms(
            user_id=user.to_string(),
            pagin_config=pagination_config,
            feedback=with_feedback)

        defer.returnValue((200, content))
Example #7
0
    def on_GET(self, request, room_id):
        user = yield self.auth.get_user_by_req(request)
        pagination_config = PaginationConfig.from_request(request)
        with_feedback = "feedback" in request.args
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(
            room_id=urllib.unquote(room_id),
            user_id=user.to_string(),
            pagin_config=pagination_config,
            feedback=with_feedback)

        defer.returnValue((200, msgs))
Example #8
0
    def on_GET(self, request, room_id):
        user = yield self.auth.get_user_by_req(request)
        pagination_config = PaginationConfig.from_request(request)
        with_feedback = "feedback" in request.args
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(
            room_id=urllib.unquote(room_id),
            user_id=user.to_string(),
            pagin_config=pagination_config,
            feedback=with_feedback)

        defer.returnValue((200, msgs))
    def on_GET(self, request):
        requester = yield self.auth.get_user_by_req(request)
        as_client_event = "raw" not in request.args
        pagination_config = PaginationConfig.from_request(request)
        include_archived = request.args.get("archived", None) == ["true"]
        content = yield self.initial_sync_handler.snapshot_all_rooms(
            user_id=requester.user.to_string(),
            pagin_config=pagination_config,
            as_client_event=as_client_event,
            include_archived=include_archived,
        )

        defer.returnValue((200, content))
Example #10
0
    def on_GET(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = PaginationConfig.from_request(
            request,
            default_limit=10,
        )
        as_client_event = "raw" not in request.args
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(room_id=room_id,
                                          requester=requester,
                                          pagin_config=pagination_config,
                                          as_client_event=as_client_event)

        defer.returnValue((200, msgs))
Example #11
0
    def on_GET(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = PaginationConfig.from_request(
            request, default_limit=10,
        )
        as_client_event = "raw" not in request.args
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(
            room_id=room_id,
            requester=requester,
            pagin_config=pagination_config,
            as_client_event=as_client_event
        )

        defer.returnValue((200, msgs))
Example #12
0
    def on_GET(self, request):
        auth_user = yield self.auth.get_user_by_req(request)

        handler = self.handlers.event_stream_handler
        pagin_config = PaginationConfig.from_request(request)
        timeout = EventStreamRestServlet.DEFAULT_LONGPOLL_TIME_MS
        if "timeout" in request.args:
            try:
                timeout = int(request.args["timeout"][0])
            except ValueError:
                raise SynapseError(400, "timeout must be in milliseconds.")

        chunk = yield handler.get_stream(auth_user.to_string(), pagin_config,
                                         timeout=timeout)

        defer.returnValue((200, chunk))
Example #13
0
File: room.py Project: rrix/synapse
    def on_GET(self, request, room_id):
        user, client = yield self.auth.get_user_by_req(request)
        pagination_config = PaginationConfig.from_request(
            request, default_limit=10,
        )
        with_feedback = "feedback" in request.args
        as_client_event = "raw" not in request.args
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(
            room_id=room_id,
            user_id=user.to_string(),
            pagin_config=pagination_config,
            feedback=with_feedback,
            as_client_event=as_client_event
        )

        defer.returnValue((200, msgs))
Example #14
0
    def on_GET(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = PaginationConfig.from_request(
            request, default_limit=10,
        )
        as_client_event = "raw" not in request.args
        filter_bytes = parse_string(request, "filter")
        if filter_bytes:
            filter_json = urlparse.unquote(filter_bytes).decode("UTF-8")
            event_filter = Filter(json.loads(filter_json))
        else:
            event_filter = None
        msgs = yield self.pagination_handler.get_messages(
            room_id=room_id,
            requester=requester,
            pagin_config=pagination_config,
            as_client_event=as_client_event,
            event_filter=event_filter,
        )

        defer.returnValue((200, msgs))
Example #15
0
    def on_GET(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = PaginationConfig.from_request(
            request, default_limit=10,
        )
        as_client_event = b"raw" not in request.args
        filter_bytes = parse_string(request, b"filter", encoding=None)
        if filter_bytes:
            filter_json = urlparse.unquote(filter_bytes.decode("UTF-8"))
            event_filter = Filter(json.loads(filter_json))
        else:
            event_filter = None
        msgs = yield self.pagination_handler.get_messages(
            room_id=room_id,
            requester=requester,
            pagin_config=pagination_config,
            as_client_event=as_client_event,
            event_filter=event_filter,
        )

        defer.returnValue((200, msgs))
Example #16
0
    def on_GET(self, request, room_id):
        requester = yield self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = PaginationConfig.from_request(
            request, default_limit=10,
        )
        as_client_event = "raw" not in request.args
        filter_bytes = request.args.get("filter", None)
        if filter_bytes:
            filter_json = urllib.unquote(filter_bytes[-1]).decode("UTF-8")
            event_filter = Filter(json.loads(filter_json))
        else:
            event_filter = None
        handler = self.handlers.message_handler
        msgs = yield handler.get_messages(
            room_id=room_id,
            requester=requester,
            pagin_config=pagination_config,
            as_client_event=as_client_event,
            event_filter=event_filter,
        )

        defer.returnValue((200, msgs))
Example #17
0
    def _snapshot_all_rooms(self, user_id=None, pagin_config=None,
                            as_client_event=True, include_archived=False):

        memberships = [Membership.INVITE, Membership.JOIN]
        if include_archived:
            memberships.append(Membership.LEAVE)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id, membership_list=memberships
        )

        user = UserID.from_string(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        receipt_stream = self.hs.get_event_sources().sources["receipt"]
        receipt, _ = yield receipt_stream.get_pagination_rows(
            user, pagination_config.get_source_config("receipt"), None
        )

        tags_by_room = yield self.store.get_tags_for_user(user_id)

        account_data, account_data_by_room = (
            yield self.store.get_account_data_for_user(user_id)
        )

        public_room_ids = yield self.store.get_public_room_ids()

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        @defer.inlineCallbacks
        def handle_room(event):
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                time_now = self.clock.time_msec()
                d["inviter"] = event.sender

                invite_event = yield self.store.get_event(event.event_id)
                d["invite"] = yield self._event_serializer.serialize_event(
                    invite_event, time_now, as_client_event,
                )

            rooms_ret.append(d)

            if event.membership not in (Membership.JOIN, Membership.LEAVE):
                return

            try:
                if event.membership == Membership.JOIN:
                    room_end_token = now_token.room_key
                    deferred_room_state = run_in_background(
                        self.state_handler.get_current_state,
                        event.room_id,
                    )
                elif event.membership == Membership.LEAVE:
                    room_end_token = "s%d" % (event.stream_ordering,)
                    deferred_room_state = run_in_background(
                        self.store.get_state_for_events,
                        [event.event_id],
                    )
                    deferred_room_state.addCallback(
                        lambda states: states[event.event_id]
                    )

                (messages, token), current_state = yield make_deferred_yieldable(
                    defer.gatherResults(
                        [
                            run_in_background(
                                self.store.get_recent_events_for_room,
                                event.room_id,
                                limit=limit,
                                end_token=room_end_token,
                            ),
                            deferred_room_state,
                        ]
                    )
                ).addErrback(unwrapFirstError)

                messages = yield filter_events_for_client(
                    self.store, user_id, messages
                )

                start_token = now_token.copy_and_replace("room_key", token)
                end_token = now_token.copy_and_replace("room_key", room_end_token)
                time_now = self.clock.time_msec()

                d["messages"] = {
                    "chunk": (
                        yield self._event_serializer.serialize_events(
                            messages, time_now=time_now,
                            as_client_event=as_client_event,
                        )
                    ),
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                d["state"] = yield self._event_serializer.serialize_events(
                    current_state.values(),
                    time_now=time_now,
                    as_client_event=as_client_event
                )

                account_data_events = []
                tags = tags_by_room.get(event.room_id)
                if tags:
                    account_data_events.append({
                        "type": "m.tag",
                        "content": {"tags": tags},
                    })

                account_data = account_data_by_room.get(event.room_id, {})
                for account_data_type, content in account_data.items():
                    account_data_events.append({
                        "type": account_data_type,
                        "content": content,
                    })

                d["account_data"] = account_data_events
            except Exception:
                logger.exception("Failed to get snapshot")

        yield concurrently_execute(handle_room, room_list, 10)

        account_data_events = []
        for account_data_type, content in account_data.items():
            account_data_events.append({
                "type": account_data_type,
                "content": content,
            })

        now = self.clock.time_msec()

        ret = {
            "rooms": rooms_ret,
            "presence": [
                {
                    "type": "m.presence",
                    "content": format_user_presence_state(event, now),
                }
                for event in presence
            ],
            "account_data": account_data_events,
            "receipts": receipt,
            "end": now_token.to_string(),
        }

        defer.returnValue(ret)
Example #18
0
    def _snapshot_all_rooms(self, user_id=None, pagin_config=None,
                            as_client_event=True, include_archived=False):

        memberships = [Membership.INVITE, Membership.JOIN]
        if include_archived:
            memberships.append(Membership.LEAVE)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id, membership_list=memberships
        )

        user = UserID.from_string(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        receipt_stream = self.hs.get_event_sources().sources["receipt"]
        receipt, _ = yield receipt_stream.get_pagination_rows(
            user, pagination_config.get_source_config("receipt"), None
        )

        tags_by_room = yield self.store.get_tags_for_user(user_id)

        account_data, account_data_by_room = (
            yield self.store.get_account_data_for_user(user_id)
        )

        public_room_ids = yield self.store.get_public_room_ids()

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        @defer.inlineCallbacks
        def handle_room(event):
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                time_now = self.clock.time_msec()
                d["inviter"] = event.sender

                invite_event = yield self.store.get_event(event.event_id)
                d["invite"] = serialize_event(invite_event, time_now, as_client_event)

            rooms_ret.append(d)

            if event.membership not in (Membership.JOIN, Membership.LEAVE):
                return

            try:
                if event.membership == Membership.JOIN:
                    room_end_token = now_token.room_key
                    deferred_room_state = self.state_handler.get_current_state(
                        event.room_id
                    )
                elif event.membership == Membership.LEAVE:
                    room_end_token = "s%d" % (event.stream_ordering,)
                    deferred_room_state = self.store.get_state_for_events(
                        [event.event_id], None
                    )
                    deferred_room_state.addCallback(
                        lambda states: states[event.event_id]
                    )

                (messages, token), current_state = yield defer.gatherResults(
                    [
                        self.store.get_recent_events_for_room(
                            event.room_id,
                            limit=limit,
                            end_token=room_end_token,
                        ),
                        deferred_room_state,
                    ]
                ).addErrback(unwrapFirstError)

                messages = yield self._filter_events_for_client(
                    user_id, messages
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])
                time_now = self.clock.time_msec()

                d["messages"] = {
                    "chunk": [
                        serialize_event(m, time_now, as_client_event)
                        for m in messages
                    ],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                d["state"] = [
                    serialize_event(c, time_now, as_client_event)
                    for c in current_state.values()
                ]

                account_data_events = []
                tags = tags_by_room.get(event.room_id)
                if tags:
                    account_data_events.append({
                        "type": "m.tag",
                        "content": {"tags": tags},
                    })

                account_data = account_data_by_room.get(event.room_id, {})
                for account_data_type, content in account_data.items():
                    account_data_events.append({
                        "type": account_data_type,
                        "content": content,
                    })

                d["account_data"] = account_data_events
            except:
                logger.exception("Failed to get snapshot")

        # Only do N rooms at once
        n = 5
        d_list = [handle_room(e) for e in room_list]
        for i in range(0, len(d_list), n):
            yield defer.gatherResults(
                d_list[i:i + n],
                consumeErrors=True
            ).addErrback(unwrapFirstError)

        account_data_events = []
        for account_data_type, content in account_data.items():
            account_data_events.append({
                "type": account_data_type,
                "content": content,
            })

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "account_data": account_data_events,
            "receipts": receipt,
            "end": now_token.to_string(),
        }

        defer.returnValue(ret)
Example #19
0
File: sync.py Project: Xe/synapse
    def full_state_sync(self, sync_config, timeline_since_token):
        """Get a sync for a client which is starting without any state.

        If a 'message_since_token' is given, only timeline events which have
        happened since that token will be returned.

        Returns:
            A Deferred SyncResult.
        """
        now_token = yield self.event_sources.get_current_token()

        now_token, ephemeral_by_room = yield self.ephemeral_by_room(
            sync_config, now_token
        )

        presence_stream = self.event_sources.sources["presence"]
        # TODO (mjark): This looks wrong, shouldn't we be getting the presence
        # UP to the present rather than after the present?
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user=sync_config.user,
            pagination_config=pagination_config.get_source_config("presence"),
            key=None
        )
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=sync_config.user.to_string(),
            membership_list=(
                Membership.INVITE,
                Membership.JOIN,
                Membership.LEAVE,
                Membership.BAN
            )
        )

        tags_by_room = yield self.store.get_tags_for_user(
            sync_config.user.to_string()
        )

        joined = []
        invited = []
        archived = []
        for event in room_list:
            if event.membership == Membership.JOIN:
                room_sync = yield self.full_state_sync_for_joined_room(
                    room_id=event.room_id,
                    sync_config=sync_config,
                    now_token=now_token,
                    timeline_since_token=timeline_since_token,
                    ephemeral_by_room=ephemeral_by_room,
                    tags_by_room=tags_by_room,
                )
                joined.append(room_sync)
            elif event.membership == Membership.INVITE:
                invite = yield self.store.get_event(event.event_id)
                invited.append(InvitedSyncResult(
                    room_id=event.room_id,
                    invite=invite,
                ))
            elif event.membership in (Membership.LEAVE, Membership.BAN):
                leave_token = now_token.copy_and_replace(
                    "room_key", "s%d" % (event.stream_ordering,)
                )
                room_sync = yield self.full_state_sync_for_archived_room(
                    sync_config=sync_config,
                    room_id=event.room_id,
                    leave_event_id=event.event_id,
                    leave_token=leave_token,
                    timeline_since_token=timeline_since_token,
                    tags_by_room=tags_by_room,
                )
                archived.append(room_sync)

        defer.returnValue(SyncResult(
            presence=presence,
            joined=joined,
            invited=invited,
            archived=archived,
            next_batch=now_token,
        ))
Example #20
0
    def snapshot_all_rooms(self,
                           user_id=None,
                           pagin_config=None,
                           feedback=False):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            feedback (bool): True to get feedback along with these messages.
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id,
            membership_list=[Membership.INVITE, Membership.JOIN])

        user = self.hs.parse_userid(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config, None)

        public_rooms = yield self.store.get_rooms(is_public=True)
        public_room_ids = [r["room_id"] for r in public_rooms]

        limit = pagin_config.limit
        if not limit:
            limit = 10

        for event in room_list:
            d = {
                "room_id":
                event.room_id,
                "membership":
                event.membership,
                "visibility":
                ("public" if event.room_id in public_room_ids else "private"),
            }

            if event.membership == Membership.INVITE:
                d["inviter"] = event.user_id

            rooms_ret.append(d)

            if event.membership != Membership.JOIN:
                continue
            try:
                messages, token = yield self.store.get_recent_events_for_room(
                    event.room_id,
                    limit=limit,
                    end_token=now_token.room_key,
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])

                d["messages"] = {
                    "chunk": [self.hs.serialize_event(m) for m in messages],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                current_state = yield self.store.get_current_state(
                    event.room_id)
                d["state"] = [
                    self.hs.serialize_event(c) for c in current_state
                ]
            except:
                logger.exception("Failed to get snapshot")

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "end": now_token.to_string()
        }

        defer.returnValue(ret)
Example #21
0
    def snapshot_all_rooms(self, user_id=None, pagin_config=None,
                           feedback=False):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            feedback (bool): True to get feedback along with these messages.
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id,
            membership_list=[Membership.INVITE, Membership.JOIN]
        )

        user = self.hs.parse_userid(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        public_rooms = yield self.store.get_rooms(is_public=True)
        public_room_ids = [r["room_id"] for r in public_rooms]

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        for event in room_list:
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                d["inviter"] = event.user_id

            rooms_ret.append(d)

            if event.membership != Membership.JOIN:
                continue
            try:
                messages, token = yield self.store.get_recent_events_for_room(
                    event.room_id,
                    limit=limit,
                    end_token=now_token.room_key,
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])

                d["messages"] = {
                    "chunk": [self.hs.serialize_event(m) for m in messages],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                current_state = yield self.state_handler.get_current_state(
                    event.room_id
                )
                d["state"] = [
                    self.hs.serialize_event(c) for c in current_state
                ]
            except:
                logger.exception("Failed to get snapshot")

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "end": now_token.to_string()
        }

        defer.returnValue(ret)
Example #22
0
    def full_state_sync(self, sync_config, timeline_since_token):
        """Get a sync for a client which is starting without any state.

        If a 'message_since_token' is given, only timeline events which have
        happened since that token will be returned.

        Returns:
            A Deferred SyncResult.
        """
        now_token = yield self.event_sources.get_current_token()

        now_token, ephemeral_by_room = yield self.ephemeral_by_room(
            sync_config, now_token)

        presence_stream = self.event_sources.sources["presence"]
        # TODO (mjark): This looks wrong, shouldn't we be getting the presence
        # UP to the present rather than after the present?
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user=sync_config.user,
            pagination_config=pagination_config.get_source_config("presence"),
            key=None)

        membership_list = (Membership.INVITE, Membership.JOIN,
                           Membership.LEAVE, Membership.BAN)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=sync_config.user.to_string(),
            membership_list=membership_list)

        account_data, account_data_by_room = (
            yield
            self.store.get_account_data_for_user(sync_config.user.to_string()))

        account_data['m.push_rules'] = yield self.push_rules_for_user(
            sync_config.user)

        tags_by_room = yield self.store.get_tags_for_user(
            sync_config.user.to_string())

        joined = []
        invited = []
        archived = []
        deferreds = []

        room_list_chunks = [
            room_list[i:i + 10] for i in xrange(0, len(room_list), 10)
        ]
        for room_list_chunk in room_list_chunks:
            for event in room_list_chunk:
                if event.membership == Membership.JOIN:
                    room_sync_deferred = preserve_fn(
                        self.full_state_sync_for_joined_room)(
                            room_id=event.room_id,
                            sync_config=sync_config,
                            now_token=now_token,
                            timeline_since_token=timeline_since_token,
                            ephemeral_by_room=ephemeral_by_room,
                            tags_by_room=tags_by_room,
                            account_data_by_room=account_data_by_room,
                        )
                    room_sync_deferred.addCallback(joined.append)
                    deferreds.append(room_sync_deferred)
                elif event.membership == Membership.INVITE:
                    invite = yield self.store.get_event(event.event_id)
                    invited.append(
                        InvitedSyncResult(
                            room_id=event.room_id,
                            invite=invite,
                        ))
                elif event.membership in (Membership.LEAVE, Membership.BAN):
                    # Always send down rooms we were banned or kicked from.
                    if not sync_config.filter_collection.include_leave:
                        if event.membership == Membership.LEAVE:
                            if sync_config.user.to_string() == event.sender:
                                continue

                    leave_token = now_token.copy_and_replace(
                        "room_key", "s%d" % (event.stream_ordering, ))
                    room_sync_deferred = preserve_fn(
                        self.full_state_sync_for_archived_room)(
                            sync_config=sync_config,
                            room_id=event.room_id,
                            leave_event_id=event.event_id,
                            leave_token=leave_token,
                            timeline_since_token=timeline_since_token,
                            tags_by_room=tags_by_room,
                            account_data_by_room=account_data_by_room,
                        )
                    room_sync_deferred.addCallback(archived.append)
                    deferreds.append(room_sync_deferred)

            yield defer.gatherResults(
                deferreds, consumeErrors=True).addErrback(unwrapFirstError)

        account_data_for_user = sync_config.filter_collection.filter_account_data(
            self.account_data_for_user(account_data))

        presence = sync_config.filter_collection.filter_presence(presence)

        defer.returnValue(
            SyncResult(
                presence=presence,
                account_data=account_data_for_user,
                joined=joined,
                invited=invited,
                archived=archived,
                next_batch=now_token,
            ))
Example #23
0
    def _snapshot_all_rooms(self, user_id=None, pagin_config=None,
                            as_client_event=True, include_archived=False):

        memberships = [Membership.INVITE, Membership.JOIN]
        if include_archived:
            memberships.append(Membership.LEAVE)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id, membership_list=memberships
        )

        user = UserID.from_string(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        receipt_stream = self.hs.get_event_sources().sources["receipt"]
        receipt, _ = yield receipt_stream.get_pagination_rows(
            user, pagination_config.get_source_config("receipt"), None
        )

        tags_by_room = yield self.store.get_tags_for_user(user_id)

        account_data, account_data_by_room = (
            yield self.store.get_account_data_for_user(user_id)
        )

        public_room_ids = yield self.store.get_public_room_ids()

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        @defer.inlineCallbacks
        def handle_room(event):
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                time_now = self.clock.time_msec()
                d["inviter"] = event.sender

                invite_event = yield self.store.get_event(event.event_id)
                d["invite"] = serialize_event(invite_event, time_now, as_client_event)

            rooms_ret.append(d)

            if event.membership not in (Membership.JOIN, Membership.LEAVE):
                return

            try:
                if event.membership == Membership.JOIN:
                    room_end_token = now_token.room_key
                    deferred_room_state = self.state_handler.get_current_state(
                        event.room_id
                    )
                elif event.membership == Membership.LEAVE:
                    room_end_token = "s%d" % (event.stream_ordering,)
                    deferred_room_state = self.store.get_state_for_events(
                        [event.event_id], None
                    )
                    deferred_room_state.addCallback(
                        lambda states: states[event.event_id]
                    )

                (messages, token), current_state = yield defer.gatherResults(
                    [
                        self.store.get_recent_events_for_room(
                            event.room_id,
                            limit=limit,
                            end_token=room_end_token,
                        ),
                        deferred_room_state,
                    ]
                ).addErrback(unwrapFirstError)

                messages = yield self._filter_events_for_client(
                    user_id, messages
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])
                time_now = self.clock.time_msec()

                d["messages"] = {
                    "chunk": [
                        serialize_event(m, time_now, as_client_event)
                        for m in messages
                    ],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                d["state"] = [
                    serialize_event(c, time_now, as_client_event)
                    for c in current_state.values()
                ]

                account_data_events = []
                tags = tags_by_room.get(event.room_id)
                if tags:
                    account_data_events.append({
                        "type": "m.tag",
                        "content": {"tags": tags},
                    })

                account_data = account_data_by_room.get(event.room_id, {})
                for account_data_type, content in account_data.items():
                    account_data_events.append({
                        "type": account_data_type,
                        "content": content,
                    })

                d["account_data"] = account_data_events
            except:
                logger.exception("Failed to get snapshot")

        # Only do N rooms at once
        n = 5
        d_list = [handle_room(e) for e in room_list]
        for i in range(0, len(d_list), n):
            yield defer.gatherResults(
                d_list[i:i + n],
                consumeErrors=True
            ).addErrback(unwrapFirstError)

        account_data_events = []
        for account_data_type, content in account_data.items():
            account_data_events.append({
                "type": account_data_type,
                "content": content,
            })

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "account_data": account_data_events,
            "receipts": receipt,
            "end": now_token.to_string(),
        }

        defer.returnValue(ret)
Example #24
0
    def get_and_dispatch(self):
        from_tok = StreamToken.from_string(self.last_token)
        config = PaginationConfig(from_token=from_tok, limit='1')
        timeout = (300 + random.randint(-60, 60)) * 1000
        chunk = yield self.evStreamHandler.get_stream(
            self.user_id,
            config,
            timeout=timeout,
            affect_presence=False,
            only_keys=(
                "room",
                "receipt",
            ),
        )

        # limiting to 1 may get 1 event plus 1 presence event, so
        # pick out the actual event
        single_event = None
        read_receipt = None
        for c in chunk['chunk']:
            if 'event_id' in c:  # Hmmm...
                single_event = c
            elif c['type'] == 'm.receipt':
                read_receipt = c

        have_updated_badge = False
        if read_receipt:
            for receipt_part in read_receipt['content'].values():
                if 'm.read' in receipt_part:
                    if self.user_id in receipt_part['m.read'].keys():
                        have_updated_badge = True

        if not single_event:
            if have_updated_badge:
                yield self.update_badge()
            self.last_token = chunk['end']
            yield self.store.update_pusher_last_token(self.app_id,
                                                      self.pushkey,
                                                      self.user_id,
                                                      self.last_token)
            return

        if not self.alive:
            return

        processed = False

        rule_evaluator = yield \
            evaluator_for_user_id(
                self.user_id, single_event['room_id'], self.store
            )

        actions = yield rule_evaluator.actions_for_event(single_event)
        tweaks = rule_evaluator.tweaks_for_actions(actions)

        if 'notify' in actions:
            self.badge = yield self._get_badge_count()
            rejected = yield self.dispatch_push(single_event, tweaks,
                                                self.badge)
            self.has_unread = True
            if isinstance(rejected, list) or isinstance(rejected, tuple):
                processed = True
                for pk in rejected:
                    if pk != self.pushkey:
                        # for sanity, we only remove the pushkey if it
                        # was the one we actually sent...
                        logger.warn(("Ignoring rejected pushkey %s because we"
                                     " didn't send it"), pk)
                    else:
                        logger.info("Pushkey %s was rejected: removing", pk)
                        yield self.hs.get_pusherpool().remove_pusher(
                            self.app_id, pk, self.user_id)
        else:
            if have_updated_badge:
                yield self.update_badge()
            processed = True

        if not self.alive:
            return

        if processed:
            self.backoff_delay = Pusher.INITIAL_BACKOFF
            self.last_token = chunk['end']
            yield self.store.update_pusher_last_token_and_success(
                self.app_id, self.pushkey, self.user_id, self.last_token,
                self.clock.time_msec())
            if self.failing_since:
                self.failing_since = None
                yield self.store.update_pusher_failing_since(
                    self.app_id, self.pushkey, self.user_id,
                    self.failing_since)
        else:
            if not self.failing_since:
                self.failing_since = self.clock.time_msec()
                yield self.store.update_pusher_failing_since(
                    self.app_id, self.pushkey, self.user_id,
                    self.failing_since)

            if (self.failing_since and self.failing_since <
                    self.clock.time_msec() - Pusher.GIVE_UP_AFTER):
                # we really only give up so that if the URL gets
                # fixed, we don't suddenly deliver a load
                # of old notifications.
                logger.warn(
                    "Giving up on a notification to user %s, "
                    "pushkey %s", self.user_id, self.pushkey)
                self.backoff_delay = Pusher.INITIAL_BACKOFF
                self.last_token = chunk['end']
                yield self.store.update_pusher_last_token(
                    self.app_id, self.pushkey, self.user_id, self.last_token)

                self.failing_since = None
                yield self.store.update_pusher_failing_since(
                    self.app_id, self.pushkey, self.user_id,
                    self.failing_since)
            else:
                logger.warn(
                    "Failed to dispatch push for user %s "
                    "(failing for %dms)."
                    "Trying again in %dms", self.user_id,
                    self.clock.time_msec() - self.failing_since,
                    self.backoff_delay)
                yield synapse.util. async .sleep(self.backoff_delay / 1000.0)
                self.backoff_delay *= 2
                if self.backoff_delay > Pusher.MAX_BACKOFF:
                    self.backoff_delay = Pusher.MAX_BACKOFF
Example #25
0
    def full_state_sync(self, sync_config, timeline_since_token):
        """Get a sync for a client which is starting without any state.

        If a 'message_since_token' is given, only timeline events which have
        happened since that token will be returned.

        Returns:
            A Deferred SyncResult.
        """
        now_token = yield self.event_sources.get_current_token()

        now_token, ephemeral_by_room = yield self.ephemeral_by_room(
            sync_config, now_token
        )

        presence_stream = self.event_sources.sources["presence"]
        # TODO (mjark): This looks wrong, shouldn't we be getting the presence
        # UP to the present rather than after the present?
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user=sync_config.user,
            pagination_config=pagination_config.get_source_config("presence"),
            key=None
        )

        membership_list = (Membership.INVITE, Membership.JOIN)
        if sync_config.filter_collection.include_leave:
            membership_list += (Membership.LEAVE, Membership.BAN)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=sync_config.user.to_string(),
            membership_list=membership_list
        )

        account_data, account_data_by_room = (
            yield self.store.get_account_data_for_user(
                sync_config.user.to_string()
            )
        )

        tags_by_room = yield self.store.get_tags_for_user(
            sync_config.user.to_string()
        )

        joined = []
        invited = []
        archived = []
        deferreds = []

        room_list_chunks = [room_list[i:i + 10] for i in xrange(0, len(room_list), 10)]
        for room_list_chunk in room_list_chunks:
            for event in room_list_chunk:
                if event.membership == Membership.JOIN:
                    room_sync_deferred = preserve_fn(
                        self.full_state_sync_for_joined_room
                    )(
                        room_id=event.room_id,
                        sync_config=sync_config,
                        now_token=now_token,
                        timeline_since_token=timeline_since_token,
                        ephemeral_by_room=ephemeral_by_room,
                        tags_by_room=tags_by_room,
                        account_data_by_room=account_data_by_room,
                    )
                    room_sync_deferred.addCallback(joined.append)
                    deferreds.append(room_sync_deferred)
                elif event.membership == Membership.INVITE:
                    invite = yield self.store.get_event(event.event_id)
                    invited.append(InvitedSyncResult(
                        room_id=event.room_id,
                        invite=invite,
                    ))
                elif event.membership in (Membership.LEAVE, Membership.BAN):
                    leave_token = now_token.copy_and_replace(
                        "room_key", "s%d" % (event.stream_ordering,)
                    )
                    room_sync_deferred = preserve_fn(
                        self.full_state_sync_for_archived_room
                    )(
                        sync_config=sync_config,
                        room_id=event.room_id,
                        leave_event_id=event.event_id,
                        leave_token=leave_token,
                        timeline_since_token=timeline_since_token,
                        tags_by_room=tags_by_room,
                        account_data_by_room=account_data_by_room,
                    )
                    room_sync_deferred.addCallback(archived.append)
                    deferreds.append(room_sync_deferred)

            yield defer.gatherResults(
                deferreds, consumeErrors=True
            ).addErrback(unwrapFirstError)

        account_data_for_user = sync_config.filter_collection.filter_account_data(
            self.account_data_for_user(account_data)
        )

        presence = sync_config.filter_collection.filter_presence(
            presence
        )

        defer.returnValue(SyncResult(
            presence=presence,
            account_data=account_data_for_user,
            joined=joined,
            invited=invited,
            archived=archived,
            next_batch=now_token,
        ))
Example #26
0
    async def get_messages(
        self,
        requester: Requester,
        room_id: str,
        pagin_config: PaginationConfig,
        as_client_event: bool = True,
        event_filter: Optional[Filter] = None,
    ) -> Dict[str, Any]:
        """Get messages in a room.

        Args:
            requester: The user requesting messages.
            room_id: The room they want messages from.
            pagin_config: The pagination config rules to apply, if any.
            as_client_event: True to get events in client-server format.
            event_filter: Filter to apply to results or None
        Returns:
            Pagination API results
        """
        user_id = requester.user.to_string()

        if pagin_config.from_token:
            room_token = pagin_config.from_token.room_key
        else:
            pagin_config.from_token = (
                self.hs.get_event_sources().get_current_token_for_pagination())
            room_token = pagin_config.from_token.room_key

        room_token = RoomStreamToken.parse(room_token)

        pagin_config.from_token = pagin_config.from_token.copy_and_replace(
            "room_key", str(room_token))

        source_config = pagin_config.get_source_config("room")

        with await self.pagination_lock.read(room_id):
            (
                membership,
                member_event_id,
            ) = await self.auth.check_user_in_room_or_world_readable(
                room_id, user_id, allow_departed_users=True)

            if source_config.direction == "b":
                # if we're going backwards, we might need to backfill. This
                # requires that we have a topo token.
                if room_token.topological:
                    curr_topo = room_token.topological
                else:
                    curr_topo = await self.store.get_current_topological_token(
                        room_id, room_token.stream)

                if membership == Membership.LEAVE:
                    # If they have left the room then clamp the token to be before
                    # they left the room, to save the effort of loading from the
                    # database.

                    # This is only None if the room is world_readable, in which
                    # case "JOIN" would have been returned.
                    assert member_event_id

                    leave_token = await self.store.get_topological_token_for_event(
                        member_event_id)
                    if RoomStreamToken.parse(
                            leave_token).topological < curr_topo:
                        source_config.from_key = str(leave_token)

                await self.hs.get_handlers().federation_handler.maybe_backfill(
                    room_id,
                    curr_topo,
                    limit=source_config.limit,
                )

            events, next_key = await self.store.paginate_room_events(
                room_id=room_id,
                from_key=source_config.from_key,
                to_key=source_config.to_key,
                direction=source_config.direction,
                limit=source_config.limit,
                event_filter=event_filter,
            )

            next_token = pagin_config.from_token.copy_and_replace(
                "room_key", next_key)

        if events:
            if event_filter:
                events = event_filter.filter(events)

            events = await filter_events_for_client(
                self.storage,
                user_id,
                events,
                is_peeking=(member_event_id is None))

        if not events:
            return {
                "chunk": [],
                "start": pagin_config.from_token.to_string(),
                "end": next_token.to_string(),
            }

        state = None
        if event_filter and event_filter.lazy_load_members(
        ) and len(events) > 0:
            # TODO: remove redundant members

            # FIXME: we also care about invite targets etc.
            state_filter = StateFilter.from_types(
                (EventTypes.Member, event.sender) for event in events)

            state_ids = await self.state_store.get_state_ids_for_event(
                events[0].event_id, state_filter=state_filter)

            if state_ids:
                state_dict = await self.store.get_events(
                    list(state_ids.values()))
                state = state_dict.values()

        time_now = self.clock.time_msec()

        chunk = {
            "chunk": (await self._event_serializer.serialize_events(
                events, time_now, as_client_event=as_client_event)),
            "start":
            pagin_config.from_token.to_string(),
            "end":
            next_token.to_string(),
        }

        if state:
            chunk["state"] = await self._event_serializer.serialize_events(
                state, time_now, as_client_event=as_client_event)

        return chunk
Example #27
0
    def snapshot_all_rooms(self, user_id=None, pagin_config=None,
                           feedback=False, as_client_event=True):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            feedback (bool): True to get feedback along with these messages.
            as_client_event (bool): True to get events in client-server format.
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id,
            membership_list=[Membership.INVITE, Membership.JOIN]
        )

        user = UserID.from_string(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        public_room_ids = yield self.store.get_public_room_ids()

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        @defer.inlineCallbacks
        def handle_room(event):
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                d["inviter"] = event.sender

            rooms_ret.append(d)

            if event.membership != Membership.JOIN:
                return
            try:
                (messages, token), current_state = yield defer.gatherResults(
                    [
                        self.store.get_recent_events_for_room(
                            event.room_id,
                            limit=limit,
                            end_token=now_token.room_key,
                        ),
                        self.state_handler.get_current_state(
                            event.room_id
                        ),
                    ]
                ).addErrback(unwrapFirstError)

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])
                time_now = self.clock.time_msec()

                d["messages"] = {
                    "chunk": [
                        serialize_event(m, time_now, as_client_event)
                        for m in messages
                    ],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                d["state"] = [
                    serialize_event(c, time_now, as_client_event)
                    for c in current_state.values()
                ]
            except:
                logger.exception("Failed to get snapshot")

        yield defer.gatherResults(
            [handle_room(e) for e in room_list],
            consumeErrors=True
        ).addErrback(unwrapFirstError)

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "end": now_token.to_string()
        }

        defer.returnValue(ret)
Example #28
0
    def snapshot_all_rooms(self, user_id=None, pagin_config=None,
                           as_client_event=True, include_archived=False):
        """Retrieve a snapshot of all rooms the user is invited or has joined.

        This snapshot may include messages for all rooms where the user is
        joined, depending on the pagination config.

        Args:
            user_id (str): The ID of the user making the request.
            pagin_config (synapse.api.streams.PaginationConfig): The pagination
            config used to determine how many messages *PER ROOM* to return.
            as_client_event (bool): True to get events in client-server format.
            include_archived (bool): True to get rooms that the user has left
        Returns:
            A list of dicts with "room_id" and "membership" keys for all rooms
            the user is currently invited or joined in on. Rooms where the user
            is joined on, may return a "messages" key with messages, depending
            on the specified PaginationConfig.
        """
        memberships = [Membership.INVITE, Membership.JOIN]
        if include_archived:
            memberships.append(Membership.LEAVE)

        room_list = yield self.store.get_rooms_for_user_where_membership_is(
            user_id=user_id, membership_list=memberships
        )

        user = UserID.from_string(user_id)

        rooms_ret = []

        now_token = yield self.hs.get_event_sources().get_current_token()

        presence_stream = self.hs.get_event_sources().sources["presence"]
        pagination_config = PaginationConfig(from_token=now_token)
        presence, _ = yield presence_stream.get_pagination_rows(
            user, pagination_config.get_source_config("presence"), None
        )

        receipt_stream = self.hs.get_event_sources().sources["receipt"]
        receipt, _ = yield receipt_stream.get_pagination_rows(
            user, pagination_config.get_source_config("receipt"), None
        )

        tags_by_room = yield self.store.get_tags_for_user(user_id)

        public_room_ids = yield self.store.get_public_room_ids()

        limit = pagin_config.limit
        if limit is None:
            limit = 10

        @defer.inlineCallbacks
        def handle_room(event):
            d = {
                "room_id": event.room_id,
                "membership": event.membership,
                "visibility": (
                    "public" if event.room_id in public_room_ids
                    else "private"
                ),
            }

            if event.membership == Membership.INVITE:
                time_now = self.clock.time_msec()
                d["inviter"] = event.sender

                invite_event = yield self.store.get_event(event.event_id)
                d["invite"] = serialize_event(invite_event, time_now, as_client_event)

            rooms_ret.append(d)

            if event.membership not in (Membership.JOIN, Membership.LEAVE):
                return

            try:
                if event.membership == Membership.JOIN:
                    room_end_token = now_token.room_key
                    deferred_room_state = self.state_handler.get_current_state(
                        event.room_id
                    )
                elif event.membership == Membership.LEAVE:
                    room_end_token = "s%d" % (event.stream_ordering,)
                    deferred_room_state = self.store.get_state_for_events(
                        [event.event_id], None
                    )
                    deferred_room_state.addCallback(
                        lambda states: states[event.event_id]
                    )

                (messages, token), current_state = yield defer.gatherResults(
                    [
                        self.store.get_recent_events_for_room(
                            event.room_id,
                            limit=limit,
                            end_token=room_end_token,
                        ),
                        deferred_room_state,
                    ]
                ).addErrback(unwrapFirstError)

                messages = yield self._filter_events_for_client(
                    user_id, messages
                )

                start_token = now_token.copy_and_replace("room_key", token[0])
                end_token = now_token.copy_and_replace("room_key", token[1])
                time_now = self.clock.time_msec()

                d["messages"] = {
                    "chunk": [
                        serialize_event(m, time_now, as_client_event)
                        for m in messages
                    ],
                    "start": start_token.to_string(),
                    "end": end_token.to_string(),
                }

                d["state"] = [
                    serialize_event(c, time_now, as_client_event)
                    for c in current_state.values()
                ]

                account_data = []
                tags = tags_by_room.get(event.room_id)
                if tags:
                    account_data.append({
                        "type": "m.tag",
                        "content": {"tags": tags},
                    })
                d["account_data"] = account_data
            except:
                logger.exception("Failed to get snapshot")

        # Only do N rooms at once
        n = 5
        d_list = [handle_room(e) for e in room_list]
        for i in range(0, len(d_list), n):
            yield defer.gatherResults(
                d_list[i:i + n],
                consumeErrors=True
            ).addErrback(unwrapFirstError)

        ret = {
            "rooms": rooms_ret,
            "presence": presence,
            "receipts": receipt,
            "end": now_token.to_string(),
        }

        defer.returnValue(ret)