1#include "events.hpp"
2
3#include <nlohmann/json.hpp>
4
5#include "mtx/events/encrypted.hpp"
6#include "mtx/events/unknown.hpp"
7
8namespace mtx::events {
9namespace detail {
10
11template<typename, typename = void>
12struct can_edit : std::false_type
13{};
14
15template<typename Content>
16struct can_edit<Content, std::void_t<decltype(Content::relations)>>
17 : std::is_same<decltype(Content::relations), mtx::common::Relations>
18{};
19}
20
21template<class Content>
22[[gnu::used, gnu::retain]] void
23to_json(nlohmann::json &obj, const Event<Content> &event)
24{
25 obj["content"] = event.content;
26 obj["sender"] = event.sender;
27 if constexpr (std::is_same_v<Unknown, Content>)
28 obj["type"] = event.content.type;
29 else
30 obj["type"] = ::mtx::events::to_string(type: event.type);
31}
32
33template<class Content>
34[[gnu::used, gnu::retain]] void
35from_json(const nlohmann::json &obj, Event<Content> &event)
36{
37 if (!std::is_same_v<Content, mtx::events::msg::Encrypted> &&
38 obj.at(key: "content").contains(key: "m.new_content")) {
39 auto new_content = obj.at(key: "content").at(key: "m.new_content");
40
41 if (obj.at(key: "content").contains(key: "m.relates_to"))
42 new_content["m.relates_to"] = obj.at(key: "content").at(key: "m.relates_to");
43 if (obj.at(key: "content").at(key: "m.new_content").contains(key: "m.relates_to"))
44 new_content["m.new_content"]["m.relates_to"] =
45 obj.at(key: "content").at(key: "m.new_content").at(key: "m.relates_to");
46 if (obj.at(key: "content").contains(key: "im.nheko.relations.v1.relations"))
47 new_content["im.nheko.relations.v1.relations"] =
48 obj.at(key: "content").at(key: "im.nheko.relations.v1.relations");
49
50 event.content = new_content.get<Content>();
51 } else if (obj.at(key: "content").is_object()) {
52 event.content = obj.at(key: "content").get<Content>();
53 } else {
54 event.content = {};
55 }
56
57 auto type = obj.at(key: "type").get<std::string>();
58 if (type.size() > 255) {
59 throw std::out_of_range("Type exceeds 255 bytes");
60 }
61 event.type = getEventType(type);
62
63 event.sender = obj.value(key: "sender", default_value: "");
64
65 if (event.sender.size() > 255) {
66 throw std::out_of_range("Sender exceeds 255 bytes");
67 }
68
69 if constexpr (std::is_same_v<Unknown, Content>)
70 event.content.type = obj.at(key: "type").get<std::string>();
71}
72
73template<class Content>
74[[gnu::used, gnu::retain]] void
75from_json(const nlohmann::json &obj, DeviceEvent<Content> &event)
76{
77 Event<Content> base_event = event;
78 from_json(obj, base_event);
79 event.content = base_event.content;
80 event.type = base_event.type;
81 event.sender = obj.at(key: "sender").get<std::string>();
82}
83
84template<class Content>
85[[gnu::used, gnu::retain]] void
86to_json(nlohmann::json &obj, const DeviceEvent<Content> &event)
87{
88 Event<Content> base_event = event;
89 to_json(obj, base_event);
90
91 obj["sender"] = event.sender;
92}
93
94void
95from_json(const nlohmann::json &obj, UnsignedData &data)
96{
97 if (obj.find(key: "age") != obj.end())
98 data.age = obj.at(key: "age").get<uint64_t>();
99
100 if (obj.find(key: "transaction_id") != obj.end())
101 data.transaction_id = obj.at(key: "transaction_id").get<std::string>();
102
103 if (obj.find(key: "prev_sender") != obj.end())
104 data.prev_sender = obj.at(key: "prev_sender").get<std::string>();
105
106 if (obj.find(key: "replaces_state") != obj.end())
107 data.replaces_state = obj.at(key: "replaces_state").get<std::string>();
108
109 if (obj.find(key: "redacted_by") != obj.end())
110 data.redacted_by = obj.at(key: "redacted_by").get<std::string>();
111
112 if (obj.find(key: "redacted_because") != obj.end())
113 data.redacted_because =
114 obj.at(key: "redacted_because").get<Event<mtx::events::msg::Redaction>>();
115}
116
117void
118to_json(nlohmann::json &obj, const UnsignedData &event)
119{
120 if (!event.prev_sender.empty())
121 obj["prev_sender"] = event.prev_sender;
122
123 if (!event.transaction_id.empty())
124 obj["transaction_id"] = event.transaction_id;
125
126 if (!event.replaces_state.empty())
127 obj["replaces_state"] = event.replaces_state;
128
129 if (event.age != 0)
130 obj["age"] = event.age;
131
132 if (!event.redacted_by.empty())
133 obj["redacted_by"] = event.redacted_by;
134
135 if (event.redacted_because)
136 obj["redacted_because"] = *event.redacted_because;
137}
138
139template<class Content>
140[[gnu::used, gnu::retain]] void
141from_json(const nlohmann::json &obj, StrippedEvent<Content> &event)
142{
143 Event<Content> &base = event;
144 from_json(obj, base);
145
146 event.state_key = obj.at(key: "state_key").get<std::string>();
147
148 if (event.state_key.size() > 255) {
149 throw std::out_of_range("State key exceeds 255 bytes");
150 }
151}
152
153template<class Content>
154[[gnu::used, gnu::retain]] void
155to_json(nlohmann::json &obj, const StrippedEvent<Content> &event)
156{
157 Event<Content> base_event = event;
158 to_json(obj, base_event);
159
160 obj["state_key"] = event.state_key;
161}
162
163template<class Content>
164[[gnu::used, gnu::retain]] void
165from_json(const nlohmann::json &obj, RoomEvent<Content> &event)
166{
167 Event<Content> &base = event;
168 from_json(obj, base);
169
170 event.event_id = obj.at(key: "event_id").get<std::string>();
171
172 if (event.event_id.size() > 255) {
173 throw std::out_of_range("Event id exceeds 255 bytes");
174 }
175
176 event.origin_server_ts = obj.at(key: "origin_server_ts").get<uint64_t>();
177
178 // SPEC_BUG: Not present in the state array returned by /sync.
179 if (obj.find(key: "room_id") != obj.end())
180 event.room_id = obj.at(key: "room_id").get<std::string>();
181
182 if (event.room_id.size() > 255) {
183 throw std::out_of_range("Room id exceeds 255 bytes");
184 }
185
186 if (obj.find(key: "unsigned") != obj.end())
187 event.unsigned_data = obj.at(key: "unsigned").get<UnsignedData>();
188}
189
190template<class Content>
191[[gnu::used, gnu::retain]] void
192to_json(nlohmann::json &obj, const RoomEvent<Content> &event)
193{
194 Event<Content> base_event = event;
195 to_json(obj, base_event);
196
197 if (!event.room_id.empty())
198 obj["room_id"] = event.room_id;
199
200 obj["event_id"] = event.event_id;
201 obj["unsigned"] = event.unsigned_data;
202 obj["origin_server_ts"] = event.origin_server_ts;
203}
204
205template<class Content>
206[[gnu::used, gnu::retain]] void
207to_json(nlohmann::json &obj, const StateEvent<Content> &event)
208{
209 RoomEvent<Content> base_event = event;
210 to_json(obj, base_event);
211
212 obj["state_key"] = event.state_key;
213}
214
215template<class Content>
216[[gnu::used, gnu::retain]] void
217from_json(const nlohmann::json &obj, StateEvent<Content> &event)
218{
219 RoomEvent<Content> &base = event;
220 from_json(obj, base);
221
222 event.state_key = obj.at(key: "state_key").get<std::string>();
223
224 if (event.state_key.size() > 255) {
225 throw std::out_of_range("State key exceeds 255 bytes");
226 }
227}
228
229template<class Content>
230[[gnu::used, gnu::retain]] void
231to_json(nlohmann::json &obj, const RedactionEvent<Content> &event)
232{
233 RoomEvent<Content> base_event = event;
234 to_json(obj, base_event);
235
236 obj["redacts"] = event.redacts;
237}
238
239template<class Content>
240[[gnu::used, gnu::retain]] void
241from_json(const nlohmann::json &obj, RedactionEvent<Content> &event)
242{
243 RoomEvent<Content> &base = event;
244 from_json(obj, base);
245
246 event.redacts = obj.at(key: "redacts").get<std::string>();
247}
248
249template<class Content>
250[[gnu::used, gnu::retain]] void
251to_json(nlohmann::json &obj, const EncryptedEvent<Content> &event)
252{
253 RoomEvent<Content> base_event = event;
254 to_json(obj, base_event);
255}
256
257template<class Content>
258[[gnu::used, gnu::retain]] void
259from_json(const nlohmann::json &obj, EncryptedEvent<Content> &event)
260{
261 RoomEvent<Content> &base = event;
262 from_json(obj, base);
263}
264
265template<class Content>
266[[gnu::used, gnu::retain]] void
267to_json(nlohmann::json &obj, const EphemeralEvent<Content> &event)
268{
269 obj["content"] = event.content;
270 if constexpr (std::is_same_v<Unknown, Content>)
271 obj["type"] = event.content.type;
272 else
273 obj["type"] = ::mtx::events::to_string(type: event.type);
274
275 if (!event.room_id.empty())
276 obj["room_id"] = event.room_id;
277}
278
279template<class Content>
280[[gnu::used, gnu::retain]] void
281from_json(const nlohmann::json &obj, EphemeralEvent<Content> &event)
282{
283 event.content = obj.at(key: "content").get<Content>();
284 event.type = getEventType(type: obj.at(key: "type").get<std::string>());
285 if constexpr (std::is_same_v<Unknown, Content>)
286 event.content.type = obj.at(key: "type").get<std::string>();
287
288 if (obj.contains(key: "room_id"))
289 event.room_id = obj.at(key: "room_id").get<std::string>();
290
291 if (event.room_id.size() > 255) {
292 throw std::out_of_range("Room id exceeds 255 bytes");
293 }
294}
295
296}
297