1 | #include "mtx/responses/sync.hpp" |
2 | #include "mtx/events/collections.hpp" |
3 | #include "mtx/log.hpp" |
4 | #include "mtx/responses/common.hpp" |
5 | |
6 | #include <nlohmann/json.hpp> |
7 | |
8 | #include <algorithm> |
9 | #include <variant> |
10 | |
11 | using json = nlohmann::json; |
12 | |
13 | namespace mtx { |
14 | namespace responses { |
15 | |
16 | void |
17 | from_json(const json &obj, AccountData &account_data) |
18 | { |
19 | if (auto it = obj.find(key: "events" ); it != obj.end() && it->is_array()) |
20 | utils::parse_room_account_data_events(events: *it, container&: account_data.events); |
21 | } |
22 | |
23 | void |
24 | from_json(const json &obj, State &state) |
25 | { |
26 | if (auto it = obj.find(key: "events" ); it != obj.end() && it->is_array()) |
27 | utils::parse_state_events(events: *it, container&: state.events); |
28 | } |
29 | |
30 | void |
31 | from_json(const json &obj, Timeline &timeline) |
32 | { |
33 | timeline.prev_batch = obj.value(key: "prev_batch" , default_value: std::string{}); |
34 | timeline.limited = obj.value(key: "limited" , default_value: false); |
35 | |
36 | utils::parse_timeline_events(events: obj.at(key: "events" ), container&: timeline.events); |
37 | } |
38 | |
39 | void |
40 | from_json(const json &obj, UnreadNotifications ¬ifications) |
41 | { |
42 | if (auto it = obj.find(key: "highlight_count" ); it != obj.end()) |
43 | notifications.highlight_count = it->get<uint64_t>(); |
44 | |
45 | if (auto it = obj.find(key: "notification_count" ); it != obj.end()) |
46 | notifications.notification_count = it->get<uint64_t>(); |
47 | } |
48 | |
49 | void |
50 | from_json(const json &obj, Ephemeral &ephemeral) |
51 | { |
52 | if (auto it = obj.find(key: "events" ); it != obj.end() && it->is_array()) |
53 | utils::parse_ephemeral_events(events: *it, container&: ephemeral.events); |
54 | } |
55 | |
56 | void |
57 | from_json(const json &obj, JoinedRoom &room) |
58 | { |
59 | if (auto it = obj.find(key: "state" ); it != obj.end()) |
60 | room.state = it->get<State>(); |
61 | |
62 | if (auto it = obj.find(key: "timeline" ); it != obj.end()) |
63 | room.timeline = it->get<Timeline>(); |
64 | |
65 | if (auto it = obj.find(key: "unread_notifications" ); it != obj.end()) |
66 | room.unread_notifications = it->get<UnreadNotifications>(); |
67 | |
68 | if (auto it = obj.find(key: "ephemeral" ); it != obj.end()) |
69 | room.ephemeral = it->get<Ephemeral>(); |
70 | |
71 | if (auto it = obj.find(key: "account_data" ); it != obj.end()) |
72 | room.account_data = it->get<AccountData>(); |
73 | } |
74 | |
75 | void |
76 | from_json(const json &obj, LeftRoom &room) |
77 | { |
78 | if (auto it = obj.find(key: "state" ); it != obj.end()) |
79 | room.state = it->get<State>(); |
80 | |
81 | if (auto it = obj.find(key: "timeline" ); it != obj.end()) |
82 | room.timeline = it->get<Timeline>(); |
83 | } |
84 | |
85 | std::string |
86 | InvitedRoom::name() const |
87 | { |
88 | using Name = mtx::events::StrippedEvent<mtx::events::state::Name>; |
89 | using Member = mtx::events::StrippedEvent<mtx::events::state::Member>; |
90 | |
91 | std::string room_name; |
92 | std::string member_name; |
93 | |
94 | for (const auto &event : invite_state) { |
95 | if (auto name = std::get_if<Name>(ptr: &event); name != nullptr) { |
96 | room_name = name->content.name; |
97 | } else if (auto avatar = std::get_if<Member>(ptr: &event); avatar != nullptr) { |
98 | if (member_name.empty()) |
99 | member_name = avatar->content.display_name; |
100 | } |
101 | } |
102 | |
103 | if (room_name.empty()) |
104 | return member_name; |
105 | |
106 | return room_name; |
107 | } |
108 | |
109 | std::string |
110 | InvitedRoom::avatar() const |
111 | { |
112 | using Avatar = mtx::events::StrippedEvent<mtx::events::state::Avatar>; |
113 | using Member = mtx::events::StrippedEvent<mtx::events::state::Member>; |
114 | |
115 | std::string room_avatar; |
116 | std::string member_avatar; |
117 | |
118 | for (const auto &event : invite_state) { |
119 | if (auto avatar = std::get_if<Avatar>(ptr: &event); avatar != nullptr) { |
120 | room_avatar = avatar->content.url; |
121 | } else if (auto member = std::get_if<Member>(ptr: &event); member != nullptr) { |
122 | // Pick the first avatar. |
123 | if (member_avatar.empty()) |
124 | member_avatar = member->content.avatar_url; |
125 | } |
126 | } |
127 | |
128 | if (room_avatar.empty()) |
129 | return member_avatar; |
130 | |
131 | return room_avatar; |
132 | } |
133 | |
134 | void |
135 | from_json(const json &obj, InvitedRoom &room) |
136 | { |
137 | if (auto state = obj.find(key: "invite_state" ); state != obj.end()) |
138 | if (auto events = state->find(key: "events" ); events != state->end()) |
139 | utils::parse_stripped_events(events: *events, container&: room.invite_state); |
140 | } |
141 | |
142 | void |
143 | from_json(const json &obj, KnockedRoom &room) |
144 | { |
145 | if (auto state = obj.find(key: "knock_state" ); state != obj.end()) |
146 | if (auto events = state->find(key: "events" ); events != state->end()) |
147 | utils::parse_stripped_events(events: *events, container&: room.knock_state); |
148 | } |
149 | |
150 | void |
151 | from_json(const json &obj, Rooms &rooms) |
152 | { |
153 | if (auto entries = obj.find(key: "join" ); entries != obj.end()) { |
154 | for (const auto &r : entries->items()) { |
155 | if (r.key().size() < 256) { |
156 | rooms.join.emplace_hint(pos: rooms.join.end(), args: r.key(), args: r.value().get<JoinedRoom>()); |
157 | } else { |
158 | mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes." ); |
159 | } |
160 | } |
161 | } |
162 | |
163 | if (auto entries = obj.find(key: "leave" ); entries != obj.end()) { |
164 | for (const auto &r : entries->items()) { |
165 | if (r.key().size() < 256) { |
166 | rooms.leave.emplace_hint(pos: rooms.leave.end(), args: r.key(), args: r.value().get<LeftRoom>()); |
167 | } else { |
168 | mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes." ); |
169 | } |
170 | } |
171 | } |
172 | |
173 | if (auto entries = obj.find(key: "invite" ); entries != obj.end()) { |
174 | for (const auto &r : entries->items()) { |
175 | if (r.key().size() < 256) { |
176 | rooms.invite.emplace_hint( |
177 | pos: rooms.invite.end(), args: r.key(), args: r.value().get<InvitedRoom>()); |
178 | } else { |
179 | mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes." ); |
180 | } |
181 | } |
182 | } |
183 | |
184 | if (auto entries = obj.find(key: "knock" ); entries != obj.end()) { |
185 | for (const auto &r : entries->items()) { |
186 | if (r.key().size() < 256) { |
187 | rooms.knock.emplace_hint(pos: rooms.knock.end(), args: r.key(), args: r.value().get<KnockedRoom>()); |
188 | } else { |
189 | mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes." ); |
190 | } |
191 | } |
192 | } |
193 | } |
194 | |
195 | void |
196 | from_json(const json &obj, DeviceLists &device_lists) |
197 | { |
198 | if (obj.count(key: "changed" ) != 0) { |
199 | device_lists.changed = obj.at(key: "changed" ).get<std::vector<std::string>>(); |
200 | |
201 | std::erase_if(cont&: device_lists.changed, pred: [](const std::string &user) { |
202 | if (user.size() > 255) { |
203 | mtx::utils::log::log()->warn(msg: "Invalid userid in device list changed." ); |
204 | return true; |
205 | } else |
206 | return false; |
207 | }); |
208 | } |
209 | |
210 | if (obj.count(key: "left" ) != 0) { |
211 | device_lists.left = obj.at(key: "left" ).get<std::vector<std::string>>(); |
212 | |
213 | std::erase_if(cont&: device_lists.left, pred: [](const std::string &user) { |
214 | if (user.size() > 255) { |
215 | mtx::utils::log::log()->warn(msg: "Invalid userid in device list left." ); |
216 | return true; |
217 | } else |
218 | return false; |
219 | }); |
220 | } |
221 | } |
222 | |
223 | void |
224 | from_json(const json &obj, ToDevice &to_device) |
225 | { |
226 | if (obj.count(key: "events" ) != 0) |
227 | utils::parse_device_events(events: obj.at(key: "events" ), container&: to_device.events); |
228 | } |
229 | |
230 | void |
231 | from_json(const json &obj, Sync &response) |
232 | { |
233 | if (auto it = obj.find(key: "rooms" ); it != obj.end()) |
234 | response.rooms = it->get<Rooms>(); |
235 | |
236 | if (auto it = obj.find(key: "device_lists" ); it != obj.end()) |
237 | response.device_lists = it->get<DeviceLists>(); |
238 | |
239 | if (auto it = obj.find(key: "to_device" ); it != obj.end()) |
240 | response.to_device = it->get<ToDevice>(); |
241 | |
242 | if (auto it = obj.find(key: "device_one_time_keys_count" ); it != obj.end()) |
243 | response.device_one_time_keys_count = it->get<std::map<std::string, uint16_t>>(); |
244 | |
245 | if (auto fallback_keys = obj.find(key: "device_unused_fallback_key_types" ); |
246 | fallback_keys != obj.end() && fallback_keys->is_array()) |
247 | response.device_unused_fallback_key_types = fallback_keys->get<std::vector<std::string>>(); |
248 | |
249 | if (obj.count(key: "presence" ) != 0 && obj.at(key: "presence" ).contains(key: "events" )) { |
250 | const auto &events = obj.at(key: "presence" ).at(key: "events" ); |
251 | response.presence.reserve(n: events.size()); |
252 | for (const auto &e : events) { |
253 | try { |
254 | response.presence.push_back( |
255 | x: e.get<mtx::events::Event<mtx::events::presence::Presence>>()); |
256 | } catch (std::exception &ex) { |
257 | mtx::utils::log::log()->warn( |
258 | fmt: "Error parsing presence event: {}, {}" , args: ex.what(), args: e.dump(indent: 2)); |
259 | } |
260 | } |
261 | } |
262 | |
263 | if (auto it = obj.find(key: "account_data" ); it != obj.end()) |
264 | response.account_data = it->get<AccountData>(); |
265 | |
266 | response.next_batch = obj.at(key: "next_batch" ).get<std::string>(); |
267 | } |
268 | } |
269 | } |
270 | |