# -*- coding: utf-8 -*- # Copyright 2014-2016 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.frozenutils import freeze from synapse.util.caches import intern_dict # Whether we should use frozen_dict in FrozenEvent. Using frozen_dicts prevents # bugs where we accidentally share e.g. signature dicts. However, converting # a dict to frozen_dicts is expensive. USE_FROZEN_DICTS = True class _EventInternalMetadata(object): def __init__(self, internal_metadata_dict): self.__dict__ = dict(internal_metadata_dict) def get_dict(self): return dict(self.__dict__) def is_outlier(self): return getattr(self, "outlier", False) def is_invite_from_remote(self): return getattr(self, "invite_from_remote", False) def get_send_on_behalf_of(self): """Whether this server should send the event on behalf of another server. This is used by the federation "send_join" API to forward the initial join event for a server in the room. returns a str with the name of the server this event is sent on behalf of. """ return getattr(self, "send_on_behalf_of", None) def _event_dict_property(key): def getter(self): return self._event_dict[key] def setter(self, v): self._event_dict[key] = v def delete(self): del self._event_dict[key] return property( getter, setter, delete, ) class EventBase(object): def __init__(self, event_dict, signatures={}, unsigned={}, internal_metadata_dict={}, rejected_reason=None): self.signatures = signatures self.unsigned = unsigned self.rejected_reason = rejected_reason self._event_dict = event_dict self.internal_metadata = _EventInternalMetadata( internal_metadata_dict ) auth_events = _event_dict_property("auth_events") depth = _event_dict_property("depth") content = _event_dict_property("content") hashes = _event_dict_property("hashes") origin = _event_dict_property("origin") origin_server_ts = _event_dict_property("origin_server_ts") prev_events = _event_dict_property("prev_events") prev_state = _event_dict_property("prev_state") redacts = _event_dict_property("redacts") room_id = _event_dict_property("room_id") sender = _event_dict_property("sender") user_id = _event_dict_property("sender") @property def membership(self): return self.content["membership"] def is_state(self): return hasattr(self, "state_key") and self.state_key is not None def get_dict(self): d = dict(self._event_dict) d.update({ "signatures": self.signatures, "unsigned": dict(self.unsigned), }) return d def get(self, key, default=None): return self._event_dict.get(key, default) 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"] # This may be a frozen event pdu_json["unsigned"].pop("redacted_because", None) return pdu_json def __set__(self, instance, value): raise AttributeError("Unrecognized attribute %s" % (instance,)) def __getitem__(self, field): return self._event_dict[field] def __contains__(self, field): return field in self._event_dict def items(self): return self._event_dict.items() class FrozenEvent(EventBase): def __init__(self, event_dict, internal_metadata_dict={}, rejected_reason=None): event_dict = dict(event_dict) # Signatures is a dict of dicts, and this is faster than doing a # copy.deepcopy signatures = { name: {sig_id: sig for sig_id, sig in sigs.items()} for name, sigs in event_dict.pop("signatures", {}).items() } unsigned = dict(event_dict.pop("unsigned", {})) # We intern these strings because they turn up a lot (especially when # caching). event_dict = intern_dict(event_dict) if USE_FROZEN_DICTS: frozen_dict = freeze(event_dict) else: frozen_dict = event_dict self.event_id = event_dict["event_id"] self.type = event_dict["type"] if "state_key" in event_dict: self.state_key = event_dict["state_key"] super(FrozenEvent, self).__init__( frozen_dict, signatures=signatures, unsigned=unsigned, internal_metadata_dict=internal_metadata_dict, rejected_reason=rejected_reason, ) @staticmethod def from_event(event): e = FrozenEvent( event.get_pdu_json() ) e.internal_metadata = event.internal_metadata return e def __str__(self): return self.__repr__() def __repr__(self): return "" % ( self.get("event_id", None), self.get("type", None), self.get("state_key", None), )