summary refs log tree commit diff
path: root/synapse/api
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/api')
-rw-r--r--synapse/api/auth.py106
-rw-r--r--synapse/api/constants.py15
-rw-r--r--synapse/api/errors.py1
-rw-r--r--synapse/api/events/__init__.py148
-rw-r--r--synapse/api/events/factory.py90
-rw-r--r--synapse/api/events/room.py170
-rw-r--r--synapse/api/events/utils.py85
-rw-r--r--synapse/api/events/validator.py87
-rw-r--r--synapse/api/urls.py1
9 files changed, 72 insertions, 631 deletions
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 2b0475543d..70245aba04 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -17,14 +17,10 @@
 
 from twisted.internet import defer
 
-from synapse.api.constants import Membership, JoinRules
+from synapse.api.constants import EventTypes, Membership, JoinRules
 from synapse.api.errors import AuthError, StoreError, Codes, SynapseError
-from synapse.api.events.room import (
-    RoomMemberEvent, RoomPowerLevelsEvent, RoomRedactionEvent,
-    RoomJoinRulesEvent, RoomCreateEvent, RoomAliasesEvent,
-)
 from synapse.util.logutils import log_function
-from syutil.base64util import encode_base64
+from synapse.util.async import run_on_reactor
 
 import logging
 
@@ -53,15 +49,17 @@ class Auth(object):
                 logger.warn("Trusting event: %s", event.event_id)
                 return True
 
-            if event.type == RoomCreateEvent.TYPE:
+            if event.type == EventTypes.Create:
                 # FIXME
                 return True
 
             # FIXME: Temp hack
-            if event.type == RoomAliasesEvent.TYPE:
+            if event.type == EventTypes.Aliases:
                 return True
 
-            if event.type == RoomMemberEvent.TYPE:
+            logger.debug("Auth events: %s", auth_events)
+
+            if event.type == EventTypes.Member:
                 allowed = self.is_membership_change_allowed(
                     event, auth_events
                 )
@@ -74,10 +72,10 @@ class Auth(object):
             self.check_event_sender_in_room(event, auth_events)
             self._can_send_event(event, auth_events)
 
-            if event.type == RoomPowerLevelsEvent.TYPE:
+            if event.type == EventTypes.PowerLevels:
                 self._check_power_levels(event, auth_events)
 
-            if event.type == RoomRedactionEvent.TYPE:
+            if event.type == EventTypes.Redaction:
                 self._check_redaction(event, auth_events)
 
             logger.debug("Allowing! %s", event)
@@ -93,7 +91,7 @@ class Auth(object):
     def check_joined_room(self, room_id, user_id):
         member = yield self.state.get_current_state(
             room_id=room_id,
-            event_type=RoomMemberEvent.TYPE,
+            event_type=EventTypes.Member,
             state_key=user_id
         )
         self._check_joined_room(member, user_id, room_id)
@@ -104,7 +102,7 @@ class Auth(object):
         curr_state = yield self.state.get_current_state(room_id)
 
         for event in curr_state:
-            if event.type == RoomMemberEvent.TYPE:
+            if event.type == EventTypes.Member:
                 try:
                     if self.hs.parse_userid(event.state_key).domain != host:
                         continue
@@ -118,7 +116,7 @@ class Auth(object):
         defer.returnValue(False)
 
     def check_event_sender_in_room(self, event, auth_events):
-        key = (RoomMemberEvent.TYPE, event.user_id, )
+        key = (EventTypes.Member, event.user_id, )
         member_event = auth_events.get(key)
 
         return self._check_joined_room(
@@ -140,7 +138,7 @@ class Auth(object):
         # Check if this is the room creator joining:
         if len(event.prev_events) == 1 and Membership.JOIN == membership:
             # Get room creation event:
-            key = (RoomCreateEvent.TYPE, "", )
+            key = (EventTypes.Create, "", )
             create = auth_events.get(key)
             if create and event.prev_events[0][0] == create.event_id:
                 if create.content["creator"] == event.state_key:
@@ -149,19 +147,19 @@ class Auth(object):
         target_user_id = event.state_key
 
         # get info about the caller
-        key = (RoomMemberEvent.TYPE, event.user_id, )
+        key = (EventTypes.Member, event.user_id, )
         caller = auth_events.get(key)
 
         caller_in_room = caller and caller.membership == Membership.JOIN
         caller_invited = caller and caller.membership == Membership.INVITE
 
         # get info about the target
-        key = (RoomMemberEvent.TYPE, target_user_id, )
+        key = (EventTypes.Member, target_user_id, )
         target = auth_events.get(key)
 
         target_in_room = target and target.membership == Membership.JOIN
 
-        key = (RoomJoinRulesEvent.TYPE, "", )
+        key = (EventTypes.JoinRules, "", )
         join_rule_event = auth_events.get(key)
         if join_rule_event:
             join_rule = join_rule_event.content.get(
@@ -256,7 +254,7 @@ class Auth(object):
         return True
 
     def _get_power_level_from_event_state(self, event, user_id, auth_events):
-        key = (RoomPowerLevelsEvent.TYPE, "", )
+        key = (EventTypes.PowerLevels, "", )
         power_level_event = auth_events.get(key)
         level = None
         if power_level_event:
@@ -264,7 +262,7 @@ class Auth(object):
             if not level:
                 level = power_level_event.content.get("users_default", 0)
         else:
-            key = (RoomCreateEvent.TYPE, "", )
+            key = (EventTypes.Create, "", )
             create_event = auth_events.get(key)
             if (create_event is not None and
                     create_event.content["creator"] == user_id):
@@ -273,7 +271,7 @@ class Auth(object):
         return level
 
     def _get_ops_level_from_event_state(self, event, auth_events):
-        key = (RoomPowerLevelsEvent.TYPE, "", )
+        key = (EventTypes.PowerLevels, "", )
         power_level_event = auth_events.get(key)
 
         if power_level_event:
@@ -351,29 +349,31 @@ class Auth(object):
         return self.store.is_server_admin(user)
 
     @defer.inlineCallbacks
-    def add_auth_events(self, event):
-        if event.type == RoomCreateEvent.TYPE:
-            event.auth_events = []
+    def add_auth_events(self, builder, context):
+        yield run_on_reactor()
+
+        if builder.type == EventTypes.Create:
+            builder.auth_events = []
             return
 
-        auth_events = []
+        auth_ids = []
 
-        key = (RoomPowerLevelsEvent.TYPE, "", )
-        power_level_event = event.old_state_events.get(key)
+        key = (EventTypes.PowerLevels, "", )
+        power_level_event = context.current_state.get(key)
 
         if power_level_event:
-            auth_events.append(power_level_event.event_id)
+            auth_ids.append(power_level_event.event_id)
 
-        key = (RoomJoinRulesEvent.TYPE, "", )
-        join_rule_event = event.old_state_events.get(key)
+        key = (EventTypes.JoinRules, "", )
+        join_rule_event = context.current_state.get(key)
 
-        key = (RoomMemberEvent.TYPE, event.user_id, )
-        member_event = event.old_state_events.get(key)
+        key = (EventTypes.Member, builder.user_id, )
+        member_event = context.current_state.get(key)
 
-        key = (RoomCreateEvent.TYPE, "", )
-        create_event = event.old_state_events.get(key)
+        key = (EventTypes.Create, "", )
+        create_event = context.current_state.get(key)
         if create_event:
-            auth_events.append(create_event.event_id)
+            auth_ids.append(create_event.event_id)
 
         if join_rule_event:
             join_rule = join_rule_event.content.get("join_rule")
@@ -381,33 +381,37 @@ class Auth(object):
         else:
             is_public = False
 
-        if event.type == RoomMemberEvent.TYPE:
-            e_type = event.content["membership"]
+        if builder.type == EventTypes.Member:
+            e_type = builder.content["membership"]
             if e_type in [Membership.JOIN, Membership.INVITE]:
                 if join_rule_event:
-                    auth_events.append(join_rule_event.event_id)
+                    auth_ids.append(join_rule_event.event_id)
 
+            if e_type == Membership.JOIN:
                 if member_event and not is_public:
-                    auth_events.append(member_event.event_id)
+                    auth_ids.append(member_event.event_id)
+            else:
+                if member_event:
+                    auth_ids.append(member_event.event_id)
         elif member_event:
             if member_event.content["membership"] == Membership.JOIN:
-                auth_events.append(member_event.event_id)
+                auth_ids.append(member_event.event_id)
 
-        hashes = yield self.store.get_event_reference_hashes(
-            auth_events
+        auth_events_entries = yield self.store.add_event_hashes(
+            auth_ids
         )
-        hashes = [
-            {
-                k: encode_base64(v) for k, v in h.items()
-                if k == "sha256"
-            }
-            for h in hashes
-        ]
-        event.auth_events = zip(auth_events, hashes)
+
+        builder.auth_events = auth_events_entries
+
+        context.auth_events = {
+            k: v
+            for k, v in context.current_state.items()
+            if v.event_id in auth_ids
+        }
 
     @log_function
     def _can_send_event(self, event, auth_events):
-        key = (RoomPowerLevelsEvent.TYPE, "", )
+        key = (EventTypes.PowerLevels, "", )
         send_level_event = auth_events.get(key)
         send_level = None
         if send_level_event:
diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 3cafff0e32..4fc8b79a40 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -59,3 +59,18 @@ class LoginType(object):
     EMAIL_URL = u"m.login.email.url"
     EMAIL_IDENTITY = u"m.login.email.identity"
     RECAPTCHA = u"m.login.recaptcha"
+
+
+class EventTypes(object):
+    Member = "m.room.member"
+    Create = "m.room.create"
+    JoinRules = "m.room.join_rules"
+    PowerLevels = "m.room.power_levels"
+    Aliases = "m.room.aliases"
+    Redaction = "m.room.redaction"
+    Feedback = "m.room.message.feedback"
+
+    # These are used for validation
+    Message = "m.room.message"
+    Topic = "m.room.topic"
+    Name = "m.room.name"
diff --git a/synapse/api/errors.py b/synapse/api/errors.py
index 581439ceb3..e250b9b211 100644
--- a/synapse/api/errors.py
+++ b/synapse/api/errors.py
@@ -34,6 +34,7 @@ class Codes(object):
     LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
     CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
     CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
+    TOO_LARGE = "M_TOO_LARGE"
 
 
 class CodeMessageException(Exception):
diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py
deleted file mode 100644
index 22939d011a..0000000000
--- a/synapse/api/events/__init__.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- 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 synapse.util.jsonobject import JsonEncodedObject
-
-
-def serialize_event(hs, e):
-    # FIXME(erikj): To handle the case of presence events and the like
-    if not isinstance(e, SynapseEvent):
-        return e
-
-    # Should this strip out None's?
-    d = {k: v for k, v in e.get_dict().items()}
-    if "age_ts" in d:
-        d["age"] = int(hs.get_clock().time_msec()) - d["age_ts"]
-        del d["age_ts"]
-
-    return d
-
-
-class SynapseEvent(JsonEncodedObject):
-
-    """Base class for Synapse events. These are JSON objects which must abide
-    by a certain well-defined structure.
-    """
-
-    # Attributes that are currently assumed by the federation side:
-    # Mandatory:
-    # - event_id
-    # - room_id
-    # - type
-    # - is_state
-    #
-    # Optional:
-    # - state_key (mandatory when is_state is True)
-    # - prev_events (these can be filled out by the federation layer itself.)
-    # - prev_state
-
-    valid_keys = [
-        "event_id",
-        "type",
-        "room_id",
-        "user_id",  # sender/initiator
-        "content",  # HTTP body, JSON
-        "state_key",
-        "age_ts",
-        "prev_content",
-        "replaces_state",
-        "redacted_because",
-        "origin_server_ts",
-    ]
-
-    internal_keys = [
-        "is_state",
-        "depth",
-        "destinations",
-        "origin",
-        "outlier",
-        "redacted",
-        "prev_events",
-        "hashes",
-        "signatures",
-        "prev_state",
-        "auth_events",
-        "state_hash",
-    ]
-
-    required_keys = [
-        "event_id",
-        "room_id",
-        "content",
-    ]
-
-    outlier = False
-
-    def __init__(self, raises=True, **kwargs):
-        super(SynapseEvent, self).__init__(**kwargs)
-        # if "content" in kwargs:
-        #     self.check_json(self.content, raises=raises)
-
-    def get_content_template(self):
-        """ Retrieve the JSON template for this event as a dict.
-
-        The template must be a dict representing the JSON to match. Only
-        required keys should be present. The values of the keys in the template
-        are checked via type() to the values of the same keys in the actual
-        event JSON.
-
-        NB: If loading content via json.loads, you MUST define strings as
-        unicode.
-
-        For example:
-            Content:
-                {
-                    "name": u"bob",
-                    "age": 18,
-                    "friends": [u"mike", u"jill"]
-                }
-            Template:
-                {
-                    "name": u"string",
-                    "age": 0,
-                    "friends": [u"string"]
-                }
-            The values "string" and 0 could be anything, so long as the types
-            are the same as the content.
-        """
-        raise NotImplementedError("get_content_template not implemented.")
-
-    def get_pdu_json(self, time_now=None):
-        pdu_json = self.get_full_dict()
-        pdu_json.pop("destinations", None)
-        pdu_json.pop("outlier", None)
-        pdu_json.pop("replaces_state", None)
-        pdu_json.pop("redacted", None)
-        pdu_json.pop("prev_content", None)
-        state_hash = pdu_json.pop("state_hash", None)
-        if state_hash is not None:
-            pdu_json.setdefault("unsigned", {})["state_hash"] = state_hash
-        content = pdu_json.get("content", {})
-        content.pop("prev", None)
-        if time_now is not None and "age_ts" in pdu_json:
-            age = time_now - pdu_json["age_ts"]
-            pdu_json.setdefault("unsigned", {})["age"] = int(age)
-            del pdu_json["age_ts"]
-        user_id = pdu_json.pop("user_id")
-        pdu_json["sender"] = user_id
-        return pdu_json
-
-
-class SynapseStateEvent(SynapseEvent):
-
-    def __init__(self, **kwargs):
-        if "state_key" not in kwargs:
-            kwargs["state_key"] = ""
-        super(SynapseStateEvent, self).__init__(**kwargs)
diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py
deleted file mode 100644
index a1ec708a81..0000000000
--- a/synapse/api/events/factory.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- 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 synapse.api.events.room import (
-    RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
-    InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
-    RoomPowerLevelsEvent, RoomJoinRulesEvent,
-    RoomCreateEvent,
-    RoomRedactionEvent,
-)
-
-from synapse.types import EventID
-
-from synapse.util.stringutils import random_string
-
-
-class EventFactory(object):
-
-    _event_classes = [
-        RoomTopicEvent,
-        RoomNameEvent,
-        MessageEvent,
-        RoomMemberEvent,
-        FeedbackEvent,
-        InviteJoinEvent,
-        RoomConfigEvent,
-        RoomPowerLevelsEvent,
-        RoomJoinRulesEvent,
-        RoomCreateEvent,
-        RoomRedactionEvent,
-    ]
-
-    def __init__(self, hs):
-        self._event_list = {}  # dict of TYPE to event class
-        for event_class in EventFactory._event_classes:
-            self._event_list[event_class.TYPE] = event_class
-
-        self.clock = hs.get_clock()
-        self.hs = hs
-
-        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(local_part, self.hs)
-
-        return e_id.to_string()
-
-    def create_event(self, etype=None, **kwargs):
-        kwargs["type"] = etype
-        if "event_id" not in kwargs:
-            kwargs["event_id"] = self.create_event_id()
-            kwargs["origin"] = self.hs.hostname
-        else:
-            ev_id = self.hs.parse_eventid(kwargs["event_id"])
-            kwargs["origin"] = ev_id.domain
-
-        if "origin_server_ts" not in kwargs:
-            kwargs["origin_server_ts"] = int(self.clock.time_msec())
-
-        # The "age" key is a delta timestamp that should be converted into an
-        # absolute timestamp the minute we see it.
-        if "age" in kwargs:
-            kwargs["age_ts"] = int(self.clock.time_msec()) - int(kwargs["age"])
-            del kwargs["age"]
-        elif "age_ts" not in kwargs:
-            kwargs["age_ts"] = int(self.clock.time_msec())
-
-        if etype in self._event_list:
-            handler = self._event_list[etype]
-        else:
-            handler = GenericEvent
-
-        return handler(**kwargs)
diff --git a/synapse/api/events/room.py b/synapse/api/events/room.py
deleted file mode 100644
index 8c4ac45d02..0000000000
--- a/synapse/api/events/room.py
+++ /dev/null
@@ -1,170 +0,0 @@
-# -*- 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 synapse.api.constants import Feedback, Membership
-from synapse.api.errors import SynapseError
-from . import SynapseEvent, SynapseStateEvent
-
-
-class GenericEvent(SynapseEvent):
-    def get_content_template(self):
-        return {}
-
-
-class RoomTopicEvent(SynapseEvent):
-    TYPE = "m.room.topic"
-
-    internal_keys = SynapseEvent.internal_keys + [
-        "topic",
-    ]
-
-    def __init__(self, **kwargs):
-        kwargs["state_key"] = ""
-        if "topic" in kwargs["content"]:
-            kwargs["topic"] = kwargs["content"]["topic"]
-        super(RoomTopicEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {"topic": u"string"}
-
-
-class RoomNameEvent(SynapseEvent):
-    TYPE = "m.room.name"
-
-    internal_keys = SynapseEvent.internal_keys + [
-        "name",
-    ]
-
-    def __init__(self, **kwargs):
-        kwargs["state_key"] = ""
-        if "name" in kwargs["content"]:
-            kwargs["name"] = kwargs["content"]["name"]
-        super(RoomNameEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {"name": u"string"}
-
-
-class RoomMemberEvent(SynapseEvent):
-    TYPE = "m.room.member"
-
-    valid_keys = SynapseEvent.valid_keys + [
-        # target is the state_key
-        "membership",  # action
-    ]
-
-    def __init__(self, **kwargs):
-        if "membership" not in kwargs:
-            kwargs["membership"] = kwargs.get("content", {}).get("membership")
-        if not kwargs["membership"] in Membership.LIST:
-            raise SynapseError(400, "Bad membership value.")
-        super(RoomMemberEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {"membership": u"string"}
-
-
-class MessageEvent(SynapseEvent):
-    TYPE = "m.room.message"
-
-    valid_keys = SynapseEvent.valid_keys + [
-        "msg_id",  # unique per room + user combo
-    ]
-
-    def __init__(self, **kwargs):
-        super(MessageEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {"msgtype": u"string"}
-
-
-class FeedbackEvent(SynapseEvent):
-    TYPE = "m.room.message.feedback"
-
-    valid_keys = SynapseEvent.valid_keys
-
-    def __init__(self, **kwargs):
-        super(FeedbackEvent, self).__init__(**kwargs)
-        if not kwargs["content"]["type"] in Feedback.LIST:
-            raise SynapseError(400, "Bad feedback value.")
-
-    def get_content_template(self):
-        return {
-            "type": u"string",
-            "target_event_id": u"string"
-        }
-
-
-class InviteJoinEvent(SynapseEvent):
-    TYPE = "m.room.invite_join"
-
-    valid_keys = SynapseEvent.valid_keys + [
-        # target_user_id is the state_key
-        "target_host",
-    ]
-
-    def __init__(self, **kwargs):
-        super(InviteJoinEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomConfigEvent(SynapseEvent):
-    TYPE = "m.room.config"
-
-    def __init__(self, **kwargs):
-        kwargs["state_key"] = ""
-        super(RoomConfigEvent, self).__init__(**kwargs)
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomCreateEvent(SynapseStateEvent):
-    TYPE = "m.room.create"
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomJoinRulesEvent(SynapseStateEvent):
-    TYPE = "m.room.join_rules"
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomPowerLevelsEvent(SynapseStateEvent):
-    TYPE = "m.room.power_levels"
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomAliasesEvent(SynapseStateEvent):
-    TYPE = "m.room.aliases"
-
-    def get_content_template(self):
-        return {}
-
-
-class RoomRedactionEvent(SynapseEvent):
-    TYPE = "m.room.redaction"
-
-    valid_keys = SynapseEvent.valid_keys + ["redacts"]
-
-    def get_content_template(self):
-        return {}
diff --git a/synapse/api/events/utils.py b/synapse/api/events/utils.py
deleted file mode 100644
index d6019d56eb..0000000000
--- a/synapse/api/events/utils.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- 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 .room import (
-    RoomMemberEvent, RoomJoinRulesEvent, RoomPowerLevelsEvent,
-    RoomAliasesEvent, RoomCreateEvent,
-)
-
-
-def prune_event(event):
-    """ Returns a pruned version of the given event, which removes all keys we
-    don't know about or think could potentially be dodgy.
-
-    This is used when we "redact" an event. We want to remove all fields that
-    the user has specified, but we do want to keep necessary information like
-    type, state_key etc.
-    """
-    event_type = event.type
-
-    allowed_keys = [
-        "event_id",
-        "user_id",
-        "room_id",
-        "hashes",
-        "signatures",
-        "content",
-        "type",
-        "state_key",
-        "depth",
-        "prev_events",
-        "prev_state",
-        "auth_events",
-        "origin",
-        "origin_server_ts",
-    ]
-
-    new_content = {}
-
-    def add_fields(*fields):
-        for field in fields:
-            if field in event.content:
-                new_content[field] = event.content[field]
-
-    if event_type == RoomMemberEvent.TYPE:
-        add_fields("membership")
-    elif event_type == RoomCreateEvent.TYPE:
-        add_fields("creator")
-    elif event_type == RoomJoinRulesEvent.TYPE:
-        add_fields("join_rule")
-    elif event_type == RoomPowerLevelsEvent.TYPE:
-        add_fields(
-            "users",
-            "users_default",
-            "events",
-            "events_default",
-            "events_default",
-            "state_default",
-            "ban",
-            "kick",
-            "redact",
-        )
-    elif event_type == RoomAliasesEvent.TYPE:
-        add_fields("aliases")
-
-    allowed_fields = {
-        k: v
-        for k, v in event.get_full_dict().items()
-        if k in allowed_keys
-    }
-
-    allowed_fields["content"] = new_content
-
-    return type(event)(**allowed_fields)
diff --git a/synapse/api/events/validator.py b/synapse/api/events/validator.py
deleted file mode 100644
index 067215f6ef..0000000000
--- a/synapse/api/events/validator.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- 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 synapse.api.errors import SynapseError, Codes
-
-
-class EventValidator(object):
-    def __init__(self, hs):
-        pass
-
-    def validate(self, event):
-        """Checks the given JSON content abides by the rules of the template.
-
-        Args:
-            content : A JSON object to check.
-            raises: True to raise a SynapseError if the check fails.
-        Returns:
-            True if the content passes the template. Returns False if the check
-            fails and raises=False.
-        Raises:
-            SynapseError if the check fails and raises=True.
-        """
-        # recursively call to inspect each layer
-        err_msg = self._check_json_template(
-            event.content,
-            event.get_content_template()
-        )
-        if err_msg:
-            raise SynapseError(400, err_msg, Codes.BAD_JSON)
-        else:
-            return True
-
-    def _check_json_template(self, content, template):
-        """Check content and template matches.
-
-        If the template is a dict, each key in the dict will be validated with
-        the content, else it will just compare the types of content and
-        template. This basic type check is required because this function will
-        be recursively called and could be called with just strs or ints.
-
-        Args:
-            content: The content to validate.
-            template: The validation template.
-        Returns:
-            str: An error message if the validation fails, else None.
-        """
-        if type(content) != type(template):
-            return "Mismatched types: %s" % template
-
-        if type(template) == dict:
-            for key in template:
-                if key not in content:
-                    return "Missing %s key" % key
-
-                if type(content[key]) != type(template[key]):
-                    return "Key %s is of the wrong type (got %s, want %s)" % (
-                        key, type(content[key]), type(template[key]))
-
-                if type(content[key]) == dict:
-                    # we must go deeper
-                    msg = self._check_json_template(
-                        content[key],
-                        template[key]
-                    )
-                    if msg:
-                        return msg
-                elif type(content[key]) == list:
-                    # make sure each item type in content matches the template
-                    for entry in content[key]:
-                        msg = self._check_json_template(
-                            entry,
-                            template[key][0]
-                        )
-                        if msg:
-                            return msg
diff --git a/synapse/api/urls.py b/synapse/api/urls.py
index 6dc19305b7..d7625127f8 100644
--- a/synapse/api/urls.py
+++ b/synapse/api/urls.py
@@ -20,3 +20,4 @@ FEDERATION_PREFIX = "/_matrix/federation/v1"
 WEB_CLIENT_PREFIX = "/_matrix/client"
 CONTENT_REPO_PREFIX = "/_matrix/content"
 SERVER_KEY_PREFIX = "/_matrix/key/v1"
+MEDIA_PREFIX = "/_matrix/media/v1"