summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xsetup.py1
-rw-r--r--synapse/api/events/factory.py2
-rw-r--r--synapse/events/__init__.py120
-rw-r--r--synapse/events/builder.py74
-rw-r--r--synapse/handlers/directory.py8
-rw-r--r--synapse/handlers/message.py2
-rw-r--r--synapse/handlers/presence.py38
-rw-r--r--synapse/handlers/profile.py14
-rw-r--r--synapse/handlers/room.py19
-rw-r--r--synapse/handlers/typing.py4
-rw-r--r--synapse/rest/admin.py2
-rw-r--r--synapse/rest/login.py4
-rw-r--r--synapse/rest/presence.py4
-rw-r--r--synapse/server.py11
-rw-r--r--synapse/storage/__init__.py19
-rw-r--r--synapse/storage/_base.py77
-rw-r--r--synapse/storage/schema/im.sql10
-rw-r--r--synapse/types.py21
18 files changed, 308 insertions, 122 deletions
diff --git a/setup.py b/setup.py
index 9b38f790b9..d1b8f0680a 100755
--- a/setup.py
+++ b/setup.py
@@ -41,6 +41,7 @@ setup(
         "pynacl",
         "daemonize",
         "py-bcrypt",
+        "frozendict>=0.4",
     ],
     dependency_links=[
         "https://github.com/matrix-org/syutil/tarball/v0.0.2#egg=syutil-0.0.2",
diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py
index a1ec708a81..1b84e2b445 100644
--- a/synapse/api/events/factory.py
+++ b/synapse/api/events/factory.py
@@ -58,7 +58,7 @@ class EventFactory(object):
 
         local_part = str(int(self.clock.time())) + i + random_string(5)
 
-        e_id = EventID.create_local(local_part, self.hs)
+        e_id = EventID.create(local_part, self.hs.hostname)
 
         return e_id.to_string()
 
diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py
new file mode 100644
index 0000000000..eefc9d3b30
--- /dev/null
+++ b/synapse/events/__init__.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+# Copyright 2014 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from frozendict import frozendict
+
+
+class _EventInternalMetadata(object):
+    def __init__(self, internal_metadata_dict):
+        self.__dict__ = internal_metadata_dict
+
+    def get_dict(self):
+        return dict(self.__dict__)
+
+
+class Event(object):
+    def __init__(self, event_dict, internal_metadata_dict={}):
+        self._signatures = event_dict.get("signatures", {})
+        self._unsigned = event_dict.get("unsigned", {})
+
+        self._original = {
+            k: v
+            for k, v in event_dict.items()
+            if k not in ["signatures", "unsigned"]
+        }
+
+        self._event_dict = frozendict(self._original)
+
+        self.internal_metadata = _EventInternalMetadata(
+            internal_metadata_dict
+        )
+
+    @property
+    def auth_events(self):
+        return self._event_dict["auth_events"]
+
+    @property
+    def content(self):
+        return self._event_dict["content"]
+
+    @property
+    def event_id(self):
+        return self._event_dict["event_id"]
+
+    @property
+    def hashes(self):
+        return self._event_dict["hashes"]
+
+    @property
+    def origin(self):
+        return self._event_dict["origin"]
+
+    @property
+    def prev_events(self):
+        return self._event_dict["prev_events"]
+
+    @property
+    def prev_state(self):
+        return self._event_dict["prev_state"]
+
+    @property
+    def room_id(self):
+        return self._event_dict["room_id"]
+
+    @property
+    def signatures(self):
+        return self._signatures
+
+    @property
+    def state_key(self):
+        return self._event_dict["state_key"]
+
+    @property
+    def type(self):
+        return self._event_dict["type"]
+
+    @property
+    def unsigned(self):
+        return self._unsigned
+
+    @property
+    def user_id(self):
+        return self._event_dict["sender"]
+
+    @property
+    def sender(self):
+        return self._event_dict["sender"]
+
+    def get_dict(self):
+        d = dict(self._original)
+        d.update({
+            "signatures": self._signatures,
+            "unsigned": self._unsigned,
+        })
+
+        return d
+
+    def get_internal_metadata_dict(self):
+        return self.internal_metadata.get_dict()
+
+    def get_pdu_json(self, time_now=None):
+        pdu_json = self.get_dict()
+
+        if time_now is not None and "age_ts" in pdu_json["unsigned"]:
+            age = time_now - pdu_json["unsigned"]["age_ts"]
+            pdu_json.setdefault("unsigned", {})["age"] = int(age)
+            del pdu_json["unsigned"]["age_ts"]
+
+        return pdu_json
\ No newline at end of file
diff --git a/synapse/events/builder.py b/synapse/events/builder.py
new file mode 100644
index 0000000000..d741795bc5
--- /dev/null
+++ b/synapse/events/builder.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Copyright 2014 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import Event
+
+from synapse.types import EventID
+
+from synapse.util.stringutils import random_string
+
+
+class EventBuilder(object):
+    def __init__(self, key_values={}):
+        self._event_dict = dict(key_values)
+        self._metadata = {}
+
+    def update_event_key(self, key, value):
+        self._event_dict[key] = value
+
+    def update_event_keys(self, other_dict):
+        self._event_dict.update(other_dict)
+
+    def update_internal_key(self, key, value):
+        self._metadata[key] = value
+
+    def build(self):
+        return Event(
+            self._event_dict,
+            self._metadata,
+        )
+
+
+class EventBuilderFactory(object):
+    def __init__(self, clock, hostname):
+        self.clock = clock
+        self.hostname = hostname
+
+        self.event_id_count = 0
+
+    def create_event_id(self):
+        i = str(self.event_id_count)
+        self.event_id_count += 1
+
+        local_part = str(int(self.clock.time())) + i + random_string(5)
+
+        e_id = EventID.create(local_part, self.hostname)
+
+        return e_id.to_string()
+
+    def new(self, key_values={}):
+        if "event_id" not in key_values:
+            key_values["event_id"] = self.create_event_id()
+
+        time_now = self.clock.time_msec()
+
+        key_values.setdefault("origin", self.hostname)
+        key_values.setdefault("origin_server_ts", time_now)
+
+        if "unsigned" in key_values:
+            age = key_values["unsigned"].pop("age", 0)
+            key_values["unsigned"].setdefault("age_ts", time_now - age)
+
+        return EventBuilder(key_values=key_values,)
\ No newline at end of file
diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py
index 3b37e49e6f..b95c4b8bf7 100644
--- a/synapse/handlers/directory.py
+++ b/synapse/handlers/directory.py
@@ -40,7 +40,7 @@ class DirectoryHandler(BaseHandler):
 
         # TODO(erikj): Do auth.
 
-        if not room_alias.is_mine:
+        if not self.hs.is_mine(room_alias):
             raise SynapseError(400, "Room alias must be local")
             # TODO(erikj): Change this.
 
@@ -64,7 +64,7 @@ class DirectoryHandler(BaseHandler):
     def delete_association(self, user_id, room_alias):
         # TODO Check if server admin
 
-        if not room_alias.is_mine:
+        if not self.hs.is_mine(room_alias):
             raise SynapseError(400, "Room alias must be local")
 
         room_id = yield self.store.delete_room_alias(room_alias)
@@ -75,7 +75,7 @@ class DirectoryHandler(BaseHandler):
     @defer.inlineCallbacks
     def get_association(self, room_alias):
         room_id = None
-        if room_alias.is_mine:
+        if self.hs.is_mine(room_alias):
             result = yield self.store.get_association_from_room_alias(
                 room_alias
             )
@@ -123,7 +123,7 @@ class DirectoryHandler(BaseHandler):
     @defer.inlineCallbacks
     def on_directory_query(self, args):
         room_alias = self.hs.parse_roomalias(args["room_alias"])
-        if not room_alias.is_mine:
+        if not self.hs.is_mine(room_alias):
             raise SynapseError(
                 400, "Room Alias is not hosted on this Home Server"
             )
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 42dc4d46f3..269d6622e1 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -79,7 +79,7 @@ class MessageHandler(BaseHandler):
         self.ratelimit(event.user_id)
         # TODO(paul): Why does 'event' not have a 'user' object?
         user = self.hs.parse_userid(event.user_id)
-        assert user.is_mine, "User must be our own: %s" % (user,)
+        assert self.hs.is_mine(user), "User must be our own: %s" % (user,)
 
         snapshot = yield self.store.snapshot_room(event)
 
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 815d40f166..1808e00ae8 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -147,7 +147,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def is_presence_visible(self, observer_user, observed_user):
-        assert(observed_user.is_mine)
+        assert(self.hs.is_mine(observed_user))
 
         if observer_user == observed_user:
             defer.returnValue(True)
@@ -165,7 +165,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def get_state(self, target_user, auth_user, as_event=False):
-        if target_user.is_mine:
+        if self.hs.is_mine(target_user):
             visible = yield self.is_presence_visible(
                 observer_user=auth_user,
                 observed_user=target_user
@@ -212,7 +212,7 @@ class PresenceHandler(BaseHandler):
         # TODO (erikj): Turn this back on. Why did we end up sending EDUs
         # everywhere?
 
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         if target_user != auth_user:
@@ -291,7 +291,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def user_joined_room(self, user, room_id):
-        if user.is_mine:
+        if self.hs.is_mine(user):
             statuscache = self._get_or_make_usercache(user)
 
             # No actual update but we need to bump the serial anyway for the
@@ -309,7 +309,7 @@ class PresenceHandler(BaseHandler):
         rm_handler = self.homeserver.get_handlers().room_member_handler
         curr_users = yield rm_handler.get_room_members(room_id)
 
-        for local_user in [c for c in curr_users if c.is_mine]:
+        for local_user in [c for c in curr_users if self.hs.is_mine(c)]:
             self.push_update_to_local_and_remote(
                 observed_user=local_user,
                 users_to_push=[user],
@@ -318,14 +318,14 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def send_invite(self, observer_user, observed_user):
-        if not observer_user.is_mine:
+        if not self.hs.is_mine(observer_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         yield self.store.add_presence_list_pending(
             observer_user.localpart, observed_user.to_string()
         )
 
-        if observed_user.is_mine:
+        if self.hs.is_mine(observed_user):
             yield self.invite_presence(observed_user, observer_user)
         else:
             yield self.federation.send_edu(
@@ -339,7 +339,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def _should_accept_invite(self, observed_user, observer_user):
-        if not observed_user.is_mine:
+        if not self.hs.is_mine(observed_user):
             defer.returnValue(False)
 
         row = yield self.store.has_presence_state(observed_user.localpart)
@@ -359,7 +359,7 @@ class PresenceHandler(BaseHandler):
                 observed_user.localpart, observer_user.to_string()
             )
 
-        if observer_user.is_mine:
+        if self.hs.is_mine(observer_user):
             if accept:
                 yield self.accept_presence(observed_user, observer_user)
             else:
@@ -396,7 +396,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def drop(self, observed_user, observer_user):
-        if not observer_user.is_mine:
+        if not self.hs.is_mine(observer_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         yield self.store.del_presence_list(
@@ -410,7 +410,7 @@ class PresenceHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def get_presence_list(self, observer_user, accepted=None):
-        if not observer_user.is_mine:
+        if not self.hs.is_mine(observer_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         presence = yield self.store.get_presence_list(
@@ -465,7 +465,7 @@ class PresenceHandler(BaseHandler):
         )
 
         for target_user in target_users:
-            if target_user.is_mine:
+            if self.hs.is_mine(target_user):
                 self._start_polling_local(user, target_user)
 
                 # We want to tell the person that just came online
@@ -477,7 +477,7 @@ class PresenceHandler(BaseHandler):
                 )
 
         deferreds = []
-        remote_users = [u for u in target_users if not u.is_mine]
+        remote_users = [u for u in target_users if not self.hs.is_mine(u)]
         remoteusers_by_domain = partition(remote_users, lambda u: u.domain)
         # Only poll for people in our get_presence_list
         for domain in remoteusers_by_domain:
@@ -520,7 +520,7 @@ class PresenceHandler(BaseHandler):
     def stop_polling_presence(self, user, target_user=None):
         logger.debug("Stop polling for presence from %s", user)
 
-        if not target_user or target_user.is_mine:
+        if not target_user or self.hs.is_mine(target_user):
             self._stop_polling_local(user, target_user=target_user)
 
         deferreds = []
@@ -579,7 +579,7 @@ class PresenceHandler(BaseHandler):
     @defer.inlineCallbacks
     @log_function
     def push_presence(self, user, statuscache):
-        assert(user.is_mine)
+        assert(self.hs.is_mine(user))
 
         logger.debug("Pushing presence update from %s", user)
 
@@ -696,7 +696,7 @@ class PresenceHandler(BaseHandler):
         for poll in content.get("poll", []):
             user = self.hs.parse_userid(poll)
 
-            if not user.is_mine:
+            if not self.hs.is_mine(user):
                 continue
 
             # TODO(paul) permissions checks
@@ -711,7 +711,7 @@ class PresenceHandler(BaseHandler):
         for unpoll in content.get("unpoll", []):
             user = self.hs.parse_userid(unpoll)
 
-            if not user.is_mine:
+            if not self.hs.is_mine(user):
                 continue
 
             if user in self._remote_sendmap:
@@ -730,7 +730,7 @@ class PresenceHandler(BaseHandler):
 
         localusers, remoteusers = partitionbool(
             users_to_push,
-            lambda u: u.is_mine
+            lambda u: self.hs.is_mine(u)
         )
 
         localusers = set(localusers)
@@ -788,7 +788,7 @@ class PresenceEventSource(object):
                 [u.to_string() for u in observer_user, observed_user])):
             defer.returnValue(True)
 
-        if observed_user.is_mine:
+        if self.hs.is_mine(observed_user):
             pushmap = presence._local_pushmap
 
             defer.returnValue(
diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index 814b3b68fe..0116ba5358 100644
--- a/synapse/handlers/profile.py
+++ b/synapse/handlers/profile.py
@@ -51,7 +51,7 @@ class ProfileHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def get_displayname(self, target_user):
-        if target_user.is_mine:
+        if self.hs.is_mine(target_user):
             displayname = yield self.store.get_profile_displayname(
                 target_user.localpart
             )
@@ -81,7 +81,7 @@ class ProfileHandler(BaseHandler):
     def set_displayname(self, target_user, auth_user, new_displayname):
         """target_user is the user whose displayname is to be changed;
         auth_user is the user attempting to make this change."""
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         if target_user != auth_user:
@@ -101,7 +101,7 @@ class ProfileHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def get_avatar_url(self, target_user):
-        if target_user.is_mine:
+        if self.hs.is_mine(target_user):
             avatar_url = yield self.store.get_profile_avatar_url(
                 target_user.localpart
             )
@@ -130,7 +130,7 @@ class ProfileHandler(BaseHandler):
     def set_avatar_url(self, target_user, auth_user, new_avatar_url):
         """target_user is the user whose avatar_url is to be changed;
         auth_user is the user attempting to make this change."""
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         if target_user != auth_user:
@@ -150,7 +150,7 @@ class ProfileHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def collect_presencelike_data(self, user, state):
-        if not user.is_mine:
+        if not self.hs.is_mine(user):
             defer.returnValue(None)
 
         with PreserveLoggingContext():
@@ -170,7 +170,7 @@ class ProfileHandler(BaseHandler):
     @defer.inlineCallbacks
     def on_profile_query(self, args):
         user = self.hs.parse_userid(args["user_id"])
-        if not user.is_mine:
+        if not self.hs.is_mine(user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         just_field = args.get("field", None)
@@ -191,7 +191,7 @@ class ProfileHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def _update_join_states(self, user):
-        if not user.is_mine:
+        if not self.hs.is_mine(user):
             return
 
         joins = yield self.store.get_rooms_for_user_where_membership_is(
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index a000b44036..6e1c37df03 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -52,9 +52,9 @@ class RoomCreationHandler(BaseHandler):
         self.ratelimit(user_id)
 
         if "room_alias_name" in config:
-            room_alias = RoomAlias.create_local(
+            room_alias = RoomAlias.create(
                 config["room_alias_name"],
-                self.hs
+                self.hs.hostname,
             )
             mapping = yield self.store.get_association_from_room_alias(
                 room_alias
@@ -77,7 +77,7 @@ class RoomCreationHandler(BaseHandler):
         if room_id:
             # Ensure room_id is the correct type
             room_id_obj = RoomID.from_string(room_id, self.hs)
-            if not room_id_obj.is_mine:
+            if not self.hs.is_mine(room_id_obj):
                 raise SynapseError(400, "Room id must be local")
 
             yield self.store.store_room(
@@ -93,7 +93,10 @@ class RoomCreationHandler(BaseHandler):
             while attempts < 5:
                 try:
                     random_string = stringutils.random_string(18)
-                    gen_room_id = RoomID.create_local(random_string, self.hs)
+                    gen_room_id = RoomID.create(
+                        random_string,
+                        self.hs.hostname,
+                    )
                     yield self.store.store_room(
                         room_id=gen_room_id.to_string(),
                         room_creator_user_id=user_id,
@@ -287,7 +290,7 @@ class RoomMemberHandler(BaseHandler):
             if ignore_user is not None and member == ignore_user:
                 continue
 
-            if member.is_mine:
+            if self.hs.is_mine(member):
                 if localusers is not None:
                     localusers.add(member)
             else:
@@ -457,7 +460,7 @@ class RoomMemberHandler(BaseHandler):
                     prev_state.user_id, self.hs
                 )
 
-                should_do_dance = not inviter.is_mine and not room
+                should_do_dance = not self.hs.is_mine(inviter) and not room
                 room_host = inviter.domain
             else:
                 should_do_dance = False
@@ -504,7 +507,7 @@ class RoomMemberHandler(BaseHandler):
                 prev_state.sender, self.hs
             )
 
-            is_remote_invite_join = not inviter.is_mine and not room
+            is_remote_invite_join = not self.hs.is_mine(inviter) and not room
             room_host = inviter.domain
         else:
             is_remote_invite_join = False
@@ -534,7 +537,7 @@ class RoomMemberHandler(BaseHandler):
         # HS.
         target_user_id = event.state_key
         target_user = self.hs.parse_userid(target_user_id)
-        if membership == Membership.INVITE and not target_user.is_mine:
+        if membership == Membership.INVITE and not self.hs.is_mine(target_user):
             do_invite_host = target_user.domain
         else:
             do_invite_host = None
diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py
index d88a53242c..be67fb2fc2 100644
--- a/synapse/handlers/typing.py
+++ b/synapse/handlers/typing.py
@@ -47,7 +47,7 @@ class TypingNotificationHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def started_typing(self, target_user, auth_user, room_id, timeout):
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         if target_user != auth_user:
@@ -72,7 +72,7 @@ class TypingNotificationHandler(BaseHandler):
 
     @defer.inlineCallbacks
     def stopped_typing(self, target_user, auth_user, room_id):
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this Home Server")
 
         if target_user != auth_user:
diff --git a/synapse/rest/admin.py b/synapse/rest/admin.py
index ed9b484623..d74c551512 100644
--- a/synapse/rest/admin.py
+++ b/synapse/rest/admin.py
@@ -35,7 +35,7 @@ class WhoisRestServlet(RestServlet):
         if not is_admin and target_user != auth_user:
             raise AuthError(403, "You are not a server admin")
 
-        if not target_user.is_mine:
+        if not self.hs.is_mine(target_user):
             raise SynapseError(400, "Can only whois a local user")
 
         ret = yield self.handlers.admin_handler.get_whois(target_user)
diff --git a/synapse/rest/login.py b/synapse/rest/login.py
index ad71f6c61d..875da076af 100644
--- a/synapse/rest/login.py
+++ b/synapse/rest/login.py
@@ -47,8 +47,8 @@ class LoginRestServlet(RestServlet):
     @defer.inlineCallbacks
     def do_password_login(self, login_submission):
         if not login_submission["user"].startswith('@'):
-            login_submission["user"] = UserID.create_local(
-                login_submission["user"], self.hs).to_string()
+            login_submission["user"] = UserID.create(
+                login_submission["user"], self.hs.hostname).to_string()
 
         handler = self.handlers.login_handler
         token = yield handler.login(
diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py
index 502ed0d4ca..062c895595 100644
--- a/synapse/rest/presence.py
+++ b/synapse/rest/presence.py
@@ -83,7 +83,7 @@ class PresenceListRestServlet(RestServlet):
         user_id = urllib.unquote(user_id)
         user = self.hs.parse_userid(user_id)
 
-        if not user.is_mine:
+        if not self.hs.is_mine(user):
             raise SynapseError(400, "User not hosted on this Home Server")
 
         if auth_user != user:
@@ -104,7 +104,7 @@ class PresenceListRestServlet(RestServlet):
         user_id = urllib.unquote(user_id)
         user = self.hs.parse_userid(user_id)
 
-        if not user.is_mine:
+        if not self.hs.is_mine(user):
             raise SynapseError(400, "User not hosted on this Home Server")
 
         if auth_user != user:
diff --git a/synapse/server.py b/synapse/server.py
index da0a44433a..c3b54221d6 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -133,22 +133,22 @@ class BaseHomeServer(object):
     def parse_userid(self, s):
         """Parse the string given by 's' as a User ID and return a UserID
         object."""
-        return UserID.from_string(s, hs=self)
+        return UserID.from_string(s)
 
     def parse_roomalias(self, s):
         """Parse the string given by 's' as a Room Alias and return a RoomAlias
         object."""
-        return RoomAlias.from_string(s, hs=self)
+        return RoomAlias.from_string(s)
 
     def parse_roomid(self, s):
         """Parse the string given by 's' as a Room ID and return a RoomID
         object."""
-        return RoomID.from_string(s, hs=self)
+        return RoomID.from_string(s)
 
     def parse_eventid(self, s):
         """Parse the string given by 's' as a Event ID and return a EventID
         object."""
-        return EventID.from_string(s, hs=self)
+        return EventID.from_string(s)
 
     def serialize_event(self, e):
         return serialize_event(self, e)
@@ -165,6 +165,9 @@ class BaseHomeServer(object):
 
         return ip_addr
 
+    def is_mine(self, domain_specific_string):
+        return domain_specific_string.domain == self.hostname
+
 # Build magic accessors for every dependency
 for depname in BaseHomeServer.DEPENDENCIES:
     BaseHomeServer._make_dependency_method(depname)
diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py
index f15e3dfe62..205d125642 100644
--- a/synapse/storage/__init__.py
+++ b/synapse/storage/__init__.py
@@ -155,6 +155,25 @@ class DataStore(RoomMemberStore, RoomStore,
         if hasattr(event, "outlier"):
             outlier = event.outlier
 
+        event_dict = {
+            k: v
+            for k, v in event.get_full_dict().items()
+            if k not in [
+                "redacted",
+                "redacted_because",
+            ]
+        }
+
+        self._simple_insert_txn(
+            txn,
+            table="event_json",
+            values={
+                "event_id": event.event_id,
+                "json": json.dumps(event_dict, separators=(',', ':')),
+            },
+            or_replace=True,
+        )
+
         vals = {
             "topological_ordering": event.depth,
             "event_id": event.event_id,
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index 4881f03368..bb61c20150 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -479,66 +479,30 @@ class SQLBaseStore(object):
         )
 
     def _parse_events_txn(self, txn, rows):
-        events = [self._parse_event_from_row(r) for r in rows]
-
-        select_event_sql = (
-            "SELECT * FROM events WHERE event_id = ? ORDER BY rowid asc"
-        )
-
-        for i, ev in enumerate(events):
-            signatures = self._get_event_signatures_txn(
-                txn, ev.event_id,
+        event_ids = [r["event_id"] for r in rows]
+
+        events = []
+        for event_id in event_ids:
+            js = self._simple_select_one_onecol_txn(
+                txn,
+                table="event_json",
+                keyvalues={"event_id": event_id},
+                retcol="json",
+                allow_none=True,
             )
 
-            ev.signatures = {
-                n: {
-                    k: encode_base64(v) for k, v in s.items()
-                }
-                for n, s in signatures.items()
-            }
-
-            hashes = self._get_event_content_hashes_txn(
-                txn, ev.event_id,
-            )
+            if not js:
+                # FIXME (erikj): What should we actually do here?
+                continue
 
-            ev.hashes = {
-                k: encode_base64(v) for k, v in hashes.items()
-            }
-
-            prevs = self._get_prev_events_and_state(txn, ev.event_id)
-
-            ev.prev_events = [
-                (e_id, h)
-                for e_id, h, is_state in prevs
-                if is_state == 0
-            ]
-
-            ev.auth_events = self._get_auth_events(txn, ev.event_id)
-
-            if hasattr(ev, "state_key"):
-                ev.prev_state = [
-                    (e_id, h)
-                    for e_id, h, is_state in prevs
-                    if is_state == 1
-                ]
-
-                if hasattr(ev, "replaces_state"):
-                    # Load previous state_content.
-                    # FIXME (erikj): Handle multiple prev_states.
-                    cursor = txn.execute(
-                        select_event_sql,
-                        (ev.replaces_state,)
-                    )
-                    prevs = self.cursor_to_dict(cursor)
-                    if prevs:
-                        prev = self._parse_event_from_row(prevs[0])
-                        ev.prev_content = prev.content
+            d = json.loads(js)
 
-            if not hasattr(ev, "redacted"):
-                logger.debug("Doesn't have redacted key: %s", ev)
-                ev.redacted = self._has_been_redacted_txn(txn, ev)
+            ev = self.event_factory.create_event(
+                etype=d["type"],
+                **d
+            )
 
-            if ev.redacted:
+            if hasattr(ev, "redacted") and ev.redacted:
                 # Get the redaction event.
                 select_event_sql = "SELECT * FROM events WHERE event_id = ?"
                 txn.execute(select_event_sql, (ev.redacted,))
@@ -549,9 +513,10 @@ class SQLBaseStore(object):
 
                 if del_evs:
                     ev = prune_event(ev)
-                    events[i] = ev
                     ev.redacted_because = del_evs[0]
 
+            events.append(ev)
+
         return events
 
     def _has_been_redacted_txn(self, txn, event):
diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql
index 8ba732a23b..cb0c494ddf 100644
--- a/synapse/storage/schema/im.sql
+++ b/synapse/storage/schema/im.sql
@@ -32,6 +32,16 @@ CREATE INDEX IF NOT EXISTS events_stream_ordering ON events (stream_ordering);
 CREATE INDEX IF NOT EXISTS events_topological_ordering ON events (topological_ordering);
 CREATE INDEX IF NOT EXISTS events_room_id ON events (room_id);
 
+
+CREATE TABLE IF NOT EXISTS event_json(
+    event_id TEXT NOT NULL,
+    json BLOB NOT NULL,
+    CONSTRAINT ev_j_uniq UNIQUE (event_id)
+);
+
+CREATE INDEX IF NOT EXISTS event_json_id ON event_json(event_id);
+
+
 CREATE TABLE IF NOT EXISTS state_events(
     event_id TEXT NOT NULL,
     room_id TEXT NOT NULL,
diff --git a/synapse/types.py b/synapse/types.py
index 649ff2f7d7..7c533193e1 100644
--- a/synapse/types.py
+++ b/synapse/types.py
@@ -19,7 +19,7 @@ from collections import namedtuple
 
 
 class DomainSpecificString(
-        namedtuple("DomainSpecificString", ("localpart", "domain", "is_mine"))
+        namedtuple("DomainSpecificString", ("localpart", "domain"))
 ):
     """Common base class among ID/name strings that have a local part and a
     domain name, prefixed with a sigil.
@@ -28,15 +28,13 @@ class DomainSpecificString(
 
         'localpart' : The local part of the name (without the leading sigil)
         'domain' : The domain part of the name
-        'is_mine' : Boolean indicating if the domain name is recognised by the
-            HomeServer as being its own
     """
 
     # Deny iteration because it will bite you if you try to create a singleton
     # set by:
     #    users = set(user)
     def __iter__(self):
-        raise ValueError("Attempted to iterate a %s" % (type(self).__name__))
+        raise ValueError("Attempted to iterate a %s" % (type(self).__name__,))
 
     # Because this class is a namedtuple of strings and booleans, it is deeply
     # immutable.
@@ -47,7 +45,7 @@ class DomainSpecificString(
         return self
 
     @classmethod
-    def from_string(cls, s, hs):
+    def from_string(cls, s):
         """Parse the string given by 's' into a structure object."""
         if s[0] != cls.SIGIL:
             raise SynapseError(400, "Expected %s string to start with '%s'" % (
@@ -66,22 +64,15 @@ class DomainSpecificString(
 
         # This code will need changing if we want to support multiple domain
         # names on one HS
-        is_mine = domain == hs.hostname
-        return cls(localpart=parts[0], domain=domain, is_mine=is_mine)
+        return cls(localpart=parts[0], domain=domain)
 
     def to_string(self):
         """Return a string encoding the fields of the structure object."""
         return "%s%s:%s" % (self.SIGIL, self.localpart, self.domain)
 
     @classmethod
-    def create_local(cls, localpart, hs):
-        """Create a structure on the local domain"""
-        return cls(localpart=localpart, domain=hs.hostname, is_mine=True)
-
-    @classmethod
-    def create(cls, localpart, domain, hs):
-        is_mine = domain == hs.hostname
-        return cls(localpart=localpart, domain=domain, is_mine=is_mine)
+    def create(cls, localpart, domain,):
+        return cls(localpart=localpart, domain=domain)
 
 
 class UserID(DomainSpecificString):