summary refs log tree commit diff
path: root/synapse
diff options
context:
space:
mode:
Diffstat (limited to 'synapse')
-rw-r--r--synapse/handlers/_base.py51
-rw-r--r--synapse/handlers/room.py52
-rw-r--r--synapse/storage/event_federation.py8
3 files changed, 65 insertions, 46 deletions
diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py
index cad37f50e7..41e153c934 100644
--- a/synapse/handlers/_base.py
+++ b/synapse/handlers/_base.py
@@ -147,7 +147,7 @@ class BaseHandler(object):
 
     @defer.inlineCallbacks
     def _create_new_client_event(self, builder):
-        latest_ret = yield self.store.get_latest_events_in_room(
+        latest_ret = yield self.store.get_latest_event_ids_and_hashes_in_room(
             builder.room_id,
         )
 
@@ -156,7 +156,10 @@ class BaseHandler(object):
         else:
             depth = 1
 
-        prev_events = [(e, h) for e, h, _ in latest_ret]
+        prev_events = [
+            (event_id, prev_hashes)
+            for event_id, prev_hashes, _ in latest_ret
+        ]
 
         builder.prev_events = prev_events
         builder.depth = depth
@@ -165,6 +168,31 @@ class BaseHandler(object):
 
         context = yield state_handler.compute_event_context(builder)
 
+        # If we've received an invite over federation, there are no latest
+        # events in the room, because we don't know enough about the graph
+        # fragment we received to treat it like a graph, so the above returned
+        # no relevant events. It may have returned some events (if we have
+        # joined and left the room), but not useful ones, like the invite. So we
+        # forcibly set our context to the invite we received over federation.
+        if (
+            not self.is_host_in_room(context.current_state) and
+            builder.type == EventTypes.Member
+        ):
+            prev_member_event = yield self.store.get_room_member(
+                builder.sender, builder.room_id
+            )
+            if prev_member_event:
+                builder.prev_events = (
+                    prev_member_event.event_id,
+                    prev_member_event.prev_events
+                )
+
+                context = yield state_handler.compute_event_context(
+                    builder,
+                    old_state=(prev_member_event,),
+                    outlier=True
+                )
+
         if builder.is_state():
             builder.prev_state = yield self.store.add_event_hashes(
                 context.prev_state_events
@@ -187,6 +215,25 @@ class BaseHandler(object):
             (event, context,)
         )
 
+    def is_host_in_room(self, current_state):
+        room_members = [
+            (state_key, event.membership)
+            for ((event_type, state_key), event) in current_state.items()
+            if event_type == EventTypes.Member
+        ]
+        if len(room_members) == 0:
+            # has the room been created so we can join it?
+            create_event = current_state.get(("m.room.create", ""))
+            if create_event:
+                return True
+        for (state_key, membership) in room_members:
+            if (
+                UserID.from_string(state_key).domain == self.hs.hostname
+                and membership == Membership.JOIN
+            ):
+                return True
+        return False
+
     @defer.inlineCallbacks
     def handle_new_client_event(self, event, context, ratelimit=True, extra_users=[]):
         # We now need to go and hit out to wherever we need to hit out to.
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index d4bb21e69e..f85a5f2677 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -510,10 +510,9 @@ class RoomMemberHandler(BaseHandler):
                 # so don't really fit into the general auth process.
                 raise AuthError(403, "Guest access not allowed")
 
-            should_do_dance, room_hosts = yield self._should_do_dance(
-                room_id,
+            should_do_dance, room_hosts = self._should_do_dance(
                 context,
-                (yield self.get_inviter(target_user.to_string(), room_id)),
+                (self.get_inviter(target_user.to_string(), context.current_state)),
                 room_hosts,
             )
 
@@ -534,11 +533,11 @@ class RoomMemberHandler(BaseHandler):
                 )
                 handled = True
         if event.membership == Membership.LEAVE:
-            is_host_in_room = yield self.is_host_in_room(room_id, context)
+            is_host_in_room = self.is_host_in_room(context.current_state)
             if not is_host_in_room:
                 # Rejecting an invite, rather than leaving a joined room
                 handler = self.hs.get_handlers().federation_handler
-                inviter = yield self.get_inviter(target_user.to_string(), room_id)
+                inviter = self.get_inviter(target_user.to_string(), context.current_state)
                 if not inviter:
                     # return the same error as join_room_alias does
                     raise SynapseError(404, "No known servers")
@@ -584,20 +583,18 @@ class RoomMemberHandler(BaseHandler):
             and guest_access.content["guest_access"] == "can_join"
         )
 
-    @defer.inlineCallbacks
-    def _should_do_dance(self, room_id, context, inviter, room_hosts=None):
+    def _should_do_dance(self, context, inviter, room_hosts=None):
         # TODO: Shouldn't this be remote_room_host?
         room_hosts = room_hosts or []
 
-        # TODO(danielwh): This shouldn't need to yield for this check, we have a context.
-        is_host_in_room = yield self.is_host_in_room(room_id, context)
+        is_host_in_room = self.is_host_in_room(context.current_state)
         if is_host_in_room:
-            defer.returnValue((False, room_hosts))
+            return False, room_hosts
 
         if inviter and not self.hs.is_mine(inviter):
             room_hosts.append(inviter.domain)
 
-        defer.returnValue((True, room_hosts))
+        return True, room_hosts
 
     @defer.inlineCallbacks
     def lookup_room_alias(self, room_alias):
@@ -624,36 +621,11 @@ class RoomMemberHandler(BaseHandler):
 
         defer.returnValue((RoomID.from_string(room_id), hosts))
 
-    # TODO(danielwh): This should use the context, rather than looking up the store.
-    @defer.inlineCallbacks
-    def get_inviter(self, user_id, room_id):
-        # TODO(markjh): get prev_state from snapshot
-        prev_state = yield self.store.get_room_member(
-            user_id, room_id
-        )
+    def get_inviter(self, user_id, current_state):
+        prev_state = current_state.get((EventTypes.Member, user_id))
         if prev_state and prev_state.membership == Membership.INVITE:
-            defer.returnValue(UserID.from_string(prev_state.user_id))
-
-    # TODO(danielwh): This looks insane. Please make it not insane.
-    @defer.inlineCallbacks
-    def is_host_in_room(self, room_id, context):
-        is_host_in_room = yield self.auth.check_host_in_room(
-            room_id,
-            self.hs.hostname
-        )
-        if not is_host_in_room:
-            # is *anyone* in the room?
-            room_member_keys = [
-                v for (k, v) in context.current_state.keys() if (
-                    k == "m.room.member"
-                )
-            ]
-            if len(room_member_keys) == 0:
-                # has the room been created so we can join it?
-                create_event = context.current_state.get(("m.room.create", ""))
-                if create_event:
-                    is_host_in_room = True
-        defer.returnValue(is_host_in_room)
+            return UserID.from_string(prev_state.user_id)
+        return None
 
     @defer.inlineCallbacks
     def get_joined_rooms_for_user(self, user):
diff --git a/synapse/storage/event_federation.py b/synapse/storage/event_federation.py
index ce2c794025..3489315e0d 100644
--- a/synapse/storage/event_federation.py
+++ b/synapse/storage/event_federation.py
@@ -114,10 +114,10 @@ class EventFederationStore(SQLBaseStore):
             retcol="event_id",
         )
 
-    def get_latest_events_in_room(self, room_id):
+    def get_latest_event_ids_and_hashes_in_room(self, room_id):
         return self.runInteraction(
-            "get_latest_events_in_room",
-            self._get_latest_events_in_room,
+            "get_latest_event_ids_and_hashes_in_room",
+            self._get_latest_event_ids_and_hashes_in_room,
             room_id,
         )
 
@@ -132,7 +132,7 @@ class EventFederationStore(SQLBaseStore):
             desc="get_latest_event_ids_in_room",
         )
 
-    def _get_latest_events_in_room(self, txn, room_id):
+    def _get_latest_event_ids_and_hashes_in_room(self, txn, room_id):
         sql = (
             "SELECT e.event_id, e.depth FROM events as e "
             "INNER JOIN event_forward_extremities as f "