__init__.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from synapse.util.caches import intern_dict
  16. from synapse.util.frozenutils import freeze
  17. # Whether we should use frozen_dict in FrozenEvent. Using frozen_dicts prevents
  18. # bugs where we accidentally share e.g. signature dicts. However, converting
  19. # a dict to frozen_dicts is expensive.
  20. USE_FROZEN_DICTS = True
  21. class _EventInternalMetadata(object):
  22. def __init__(self, internal_metadata_dict):
  23. self.__dict__ = dict(internal_metadata_dict)
  24. def get_dict(self):
  25. return dict(self.__dict__)
  26. def is_outlier(self):
  27. return getattr(self, "outlier", False)
  28. def is_invite_from_remote(self):
  29. return getattr(self, "invite_from_remote", False)
  30. def get_send_on_behalf_of(self):
  31. """Whether this server should send the event on behalf of another server.
  32. This is used by the federation "send_join" API to forward the initial join
  33. event for a server in the room.
  34. returns a str with the name of the server this event is sent on behalf of.
  35. """
  36. return getattr(self, "send_on_behalf_of", None)
  37. def _event_dict_property(key):
  38. # We want to be able to use hasattr with the event dict properties.
  39. # However, (on python3) hasattr expects AttributeError to be raised. Hence,
  40. # we need to transform the KeyError into an AttributeError
  41. def getter(self):
  42. try:
  43. return self._event_dict[key]
  44. except KeyError:
  45. raise AttributeError(key)
  46. def setter(self, v):
  47. try:
  48. self._event_dict[key] = v
  49. except KeyError:
  50. raise AttributeError(key)
  51. def delete(self):
  52. try:
  53. del self._event_dict[key]
  54. except KeyError:
  55. raise AttributeError(key)
  56. return property(
  57. getter,
  58. setter,
  59. delete,
  60. )
  61. class EventBase(object):
  62. def __init__(self, event_dict, signatures={}, unsigned={},
  63. internal_metadata_dict={}, rejected_reason=None):
  64. self.signatures = signatures
  65. self.unsigned = unsigned
  66. self.rejected_reason = rejected_reason
  67. self._event_dict = event_dict
  68. self.internal_metadata = _EventInternalMetadata(
  69. internal_metadata_dict
  70. )
  71. auth_events = _event_dict_property("auth_events")
  72. depth = _event_dict_property("depth")
  73. content = _event_dict_property("content")
  74. hashes = _event_dict_property("hashes")
  75. origin = _event_dict_property("origin")
  76. origin_server_ts = _event_dict_property("origin_server_ts")
  77. prev_events = _event_dict_property("prev_events")
  78. prev_state = _event_dict_property("prev_state")
  79. redacts = _event_dict_property("redacts")
  80. room_id = _event_dict_property("room_id")
  81. sender = _event_dict_property("sender")
  82. user_id = _event_dict_property("sender")
  83. @property
  84. def membership(self):
  85. return self.content["membership"]
  86. def is_state(self):
  87. return hasattr(self, "state_key") and self.state_key is not None
  88. def get_dict(self):
  89. d = dict(self._event_dict)
  90. d.update({
  91. "signatures": self.signatures,
  92. "unsigned": dict(self.unsigned),
  93. })
  94. return d
  95. def get(self, key, default=None):
  96. return self._event_dict.get(key, default)
  97. def get_internal_metadata_dict(self):
  98. return self.internal_metadata.get_dict()
  99. def get_pdu_json(self, time_now=None):
  100. pdu_json = self.get_dict()
  101. if time_now is not None and "age_ts" in pdu_json["unsigned"]:
  102. age = time_now - pdu_json["unsigned"]["age_ts"]
  103. pdu_json.setdefault("unsigned", {})["age"] = int(age)
  104. del pdu_json["unsigned"]["age_ts"]
  105. # This may be a frozen event
  106. pdu_json["unsigned"].pop("redacted_because", None)
  107. return pdu_json
  108. def __set__(self, instance, value):
  109. raise AttributeError("Unrecognized attribute %s" % (instance,))
  110. def __getitem__(self, field):
  111. return self._event_dict[field]
  112. def __contains__(self, field):
  113. return field in self._event_dict
  114. def items(self):
  115. return list(self._event_dict.items())
  116. class FrozenEvent(EventBase):
  117. def __init__(self, event_dict, internal_metadata_dict={}, rejected_reason=None):
  118. event_dict = dict(event_dict)
  119. # Signatures is a dict of dicts, and this is faster than doing a
  120. # copy.deepcopy
  121. signatures = {
  122. name: {sig_id: sig for sig_id, sig in sigs.items()}
  123. for name, sigs in event_dict.pop("signatures", {}).items()
  124. }
  125. unsigned = dict(event_dict.pop("unsigned", {}))
  126. # We intern these strings because they turn up a lot (especially when
  127. # caching).
  128. event_dict = intern_dict(event_dict)
  129. if USE_FROZEN_DICTS:
  130. frozen_dict = freeze(event_dict)
  131. else:
  132. frozen_dict = event_dict
  133. self.event_id = event_dict["event_id"]
  134. self.type = event_dict["type"]
  135. if "state_key" in event_dict:
  136. self.state_key = event_dict["state_key"]
  137. super(FrozenEvent, self).__init__(
  138. frozen_dict,
  139. signatures=signatures,
  140. unsigned=unsigned,
  141. internal_metadata_dict=internal_metadata_dict,
  142. rejected_reason=rejected_reason,
  143. )
  144. @staticmethod
  145. def from_event(event):
  146. e = FrozenEvent(
  147. event.get_pdu_json()
  148. )
  149. e.internal_metadata = event.internal_metadata
  150. return e
  151. def __str__(self):
  152. return self.__repr__()
  153. def __repr__(self):
  154. return "<FrozenEvent event_id='%s', type='%s', state_key='%s'>" % (
  155. self.get("event_id", None),
  156. self.get("type", None),
  157. self.get("state_key", None),
  158. )