| 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 | |