From 2a973ff9402dab9d6c751a146a9f83617d0e5211 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 23 Oct 2020 10:25:08 +0200 Subject: Matrix: start on room tags, fix thread race condition on accessing room variables (name, avatar url, prev batch) --- src/Body.cpp | 1 + src/QuickMedia.cpp | 12 ++--- src/plugins/Matrix.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 128 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/Body.cpp b/src/Body.cpp index 3fd2324..0af6407 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -835,6 +835,7 @@ namespace QuickMedia { return spacing_y; } + // TODO: Support utf-8 case insensitive find //static bool Body::string_find_case_insensitive(const std::string &str, const std::string &substr) { auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(), diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 48d3689..33ebb06 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1012,7 +1012,7 @@ namespace QuickMedia { tab.body->clear_cache(); } - if(new_tabs.size() == 1 && new_tabs[0].page->is_manga_images_page()) { + if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::MANGA_IMAGES) { select_episode(selected_item, false); Body *chapters_body = tabs[selected_tab].body.get(); chapters_body->filter_search_fuzzy(""); // Needed (or not really) to go to the next chapter when reaching the last page of a chapter @@ -1048,11 +1048,11 @@ namespace QuickMedia { } window.setKeyRepeatEnabled(true); redraw = true; - } else if(new_tabs.size() == 1 && new_tabs[0].page->is_image_board_thread_page()) { + } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::IMAGE_BOARD_THREAD) { current_page = PageType::IMAGE_BOARD_THREAD; image_board_thread_page(static_cast(new_tabs[0].page.get()), new_tabs[0].body.get()); redraw = true; - } else if(new_tabs.size() == 1 && new_tabs[0].page->is_video_page()) { + } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::VIDEO) { current_page = PageType::VIDEO_CONTENT; video_content_page(new_tabs[0].page.get(), selected_item->url, selected_item->get_title()); redraw = true; @@ -3049,7 +3049,7 @@ namespace QuickMedia { room->has_unread_mention = true; // TODO: What if the message or username begins with "-"? also make the notification image be the avatar of the user if(!is_window_focused || room != current_room || is_first_sync || selected_tab == ROOMS_TAB_INDEX) - show_notification("QuickMedia matrix - " + matrix->message_get_author_displayname(message.get()) + " (" + room->name + ")", message->body); + show_notification("QuickMedia matrix - " + matrix->message_get_author_displayname(message.get()) + " (" + room->get_name() + ")", message->body); } } } @@ -3537,12 +3537,12 @@ namespace QuickMedia { for(size_t i = 0; i < rooms.size(); ++i) { auto &room = rooms[i]; - std::string room_name = room->name; + std::string room_name = room->get_name(); if(room_name.empty()) room_name = room->id; auto body_item = BodyItem::create(std::move(room_name)); - body_item->thumbnail_url = room->avatar_url; + body_item->thumbnail_url = room->get_avatar_url(); body_item->userdata = room; // Note: this has to be valid as long as the room list is valid! body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; body_item->thumbnail_size = sf::Vector2i(32, 32); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 9a2f70b..99d6bed 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -115,6 +115,51 @@ namespace QuickMedia { return pinned_events; } + bool RoomData::has_prev_batch() { + std::lock_guard lock(room_mutex); + return !prev_batch.empty(); + } + + void RoomData::set_prev_batch(const std::string &new_prev_batch) { + std::lock_guard lock(room_mutex); + prev_batch = new_prev_batch; + } + + std::string RoomData::get_prev_batch() { + std::lock_guard lock(room_mutex); + return prev_batch; + } + + bool RoomData::has_name() { + std::lock_guard lock(room_mutex); + return !name.empty(); + } + + void RoomData::set_name(const std::string &new_name) { + std::lock_guard lock(room_mutex); + name = new_name; + } + + std::string RoomData::get_name() { + std::lock_guard lock(room_mutex); + return name; + } + + bool RoomData::has_avatar_url() { + std::lock_guard lock(room_mutex); + return !avatar_url.empty(); + } + + void RoomData::set_avatar_url(const std::string &new_avatar_url) { + std::lock_guard lock(room_mutex); + avatar_url = new_avatar_url; + } + + std::string RoomData::get_avatar_url() { + std::lock_guard lock(room_mutex); + return avatar_url; + } + PluginResult Matrix::sync(RoomSyncData &room_sync_data) { std::vector additional_args = { { "-H", "Authorization: Bearer " + access_token }, @@ -227,11 +272,11 @@ namespace QuickMedia { const rapidjson::Value &timeline_json = GetMember(it.value, "timeline"); if(timeline_json.IsObject()) { - if(room->prev_batch.empty()) { + if(!room->has_prev_batch()) { // This may be non-existent if this is the first event in the room const rapidjson::Value &prev_batch_json = GetMember(timeline_json, "prev_batch"); if(prev_batch_json.IsString()) - room->prev_batch = prev_batch_json.GetString(); + room->set_prev_batch(prev_batch_json.GetString()); } // TODO: Use /_matrix/client/r0/notifications ? or remove this and always look for displayname/user_id in messages @@ -258,6 +303,12 @@ namespace QuickMedia { events_add_user_read_markers(events_json, room); } } + + const rapidjson::Value &account_data_json = GetMember(it.value, "account_data"); + if(account_data_json.IsObject()) { + const rapidjson::Value &events_json = GetMember(account_data_json, "events"); + events_add_room_to_tags(events_json, room); + } } return PluginResult::OK; @@ -709,11 +760,14 @@ namespace QuickMedia { if(!name_json.IsString()) continue; - room_data->name = name_json.GetString(); + room_data->set_name(name_json.GetString()); } + bool has_room_name = room_data->has_name(); + bool has_room_avatar_url = room_data->has_avatar_url(); + std::vector> users_excluding_me; - if(room_data->name.empty() || room_data->avatar_url.empty()) + if(!has_room_name || !has_room_avatar_url) users_excluding_me = room_data->get_users_excluding_me(user_id); // TODO: What about thread safety with user_id? its reset in /logout for(const rapidjson::Value &event_item_json : events_json.GetArray()) { @@ -732,18 +786,21 @@ namespace QuickMedia { if(!creator_json.IsString()) continue; - if(room_data->name.empty()) - room_data->name = combine_user_display_names_for_room_name(users_excluding_me, creator_json.GetString()); + if(!has_room_name) { + room_data->set_name(combine_user_display_names_for_room_name(users_excluding_me, creator_json.GetString())); + has_room_name = true; + } - if(room_data->avatar_url.empty()) { + if(!has_room_avatar_url) { if(users_excluding_me.empty()) { auto user = room_data->get_user_by_id(creator_json.GetString()); if(user) - room_data->avatar_url = user->avatar_url; + room_data->set_avatar_url(user->avatar_url); } else { // TODO: If there are multiple users, then we want to use some other type of avatar, not the first users avatar - room_data->avatar_url = users_excluding_me.front()->avatar_url; + room_data->set_avatar_url(users_excluding_me.front()->avatar_url); } + has_room_avatar_url = true; } } @@ -764,7 +821,7 @@ namespace QuickMedia { continue; std::string url_json_str = url_json.GetString() + 6; - room_data->avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + std::move(url_json_str) + "?width=32&height=32&method=crop"; + room_data->set_avatar_url(homeserver + "/_matrix/media/r0/thumbnail/" + std::move(url_json_str) + "?width=32&height=32&method=crop"); } } @@ -801,8 +858,54 @@ namespace QuickMedia { room_data->append_pinned_events(std::move(pinned_events)); } + // TODO: According to spec: "Any tag in the tld.name.* form but not matching the namespace of the current client should be ignored", + // should we follow this? + static const char* tag_get_name(const char *name, size_t size) { + if(size >= 2 && (memcmp(name, "m.", 2) == 0 || memcmp(name, "u.", 2) == 0)) + return name + 2; + else if(size >= 9 && memcmp(name, "tld.name.", 9) == 0) + return name + 9; + else + return name; + } + + void Matrix::events_add_room_to_tags(const rapidjson::Value &events_json, RoomData *room_data) { + if(!events_json.IsArray()) + return; + + std::vector pinned_events; + for(const rapidjson::Value &event_item_json : events_json.GetArray()) { + if(!event_item_json.IsObject()) + continue; + + const rapidjson::Value &type_json = GetMember(event_item_json, "type"); + if(!type_json.IsString() || strcmp(type_json.GetString(), "m.tag") != 0) + continue; + + const rapidjson::Value &content_json = GetMember(event_item_json, "content"); + if(!content_json.IsObject()) + continue; + + const rapidjson::Value &tags_json = GetMember(content_json, "tags"); + if(!tags_json.IsObject()) + continue; + + for(auto const &tag_json : tags_json.GetObject()) { + if(!tag_json.name.IsString() || !tag_json.value.IsObject()) + continue; + + const char *tag_name = tag_get_name(tag_json.name.GetString(), tag_json.name.GetStringLength()); + if(!tag_name) + continue; + + // TODO: Support tag order + rooms_by_tag_name[tag_name].push_back(room_data->index); + } + } + } + PluginResult Matrix::get_previous_room_messages(RoomData *room_data) { - std::string from = room_data->prev_batch; + std::string from = room_data->get_prev_batch(); if(from.empty()) { fprintf(stderr, "Info: missing previous batch for room: %s, using /sync next batch\n", room_data->id.c_str()); from = next_batch; @@ -835,9 +938,9 @@ namespace QuickMedia { if(!json_root.IsObject()) return PluginResult::ERR; - const rapidjson::Value &state_json = GetMember(json_root, "state"); - events_add_user_info(state_json, room_data); - events_set_room_name(state_json, room_data); + //const rapidjson::Value &state_json = GetMember(json_root, "state"); + //events_add_user_info(state_json, room_data); + //events_set_room_name(state_json, room_data); const rapidjson::Value &chunk_json = GetMember(json_root, "chunk"); events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr, false); @@ -848,7 +951,7 @@ namespace QuickMedia { return PluginResult::OK; } - room_data->prev_batch = end_json.GetString(); + room_data->set_prev_batch(end_json.GetString()); return PluginResult::OK; } @@ -1478,6 +1581,7 @@ namespace QuickMedia { rooms.clear(); room_list_read_index = 0; room_data_by_id.clear(); + rooms_by_tag_name.clear(); user_id.clear(); username.clear(); access_token.clear(); @@ -1722,7 +1826,8 @@ namespace QuickMedia { void Matrix::add_room(std::unique_ptr room) { std::lock_guard lock(room_data_mutex); - room_data_by_id.insert(std::make_pair(room->id, rooms.size())); + room->index = rooms.size(); + room_data_by_id.insert(std::make_pair(room->id, room->index)); rooms.push_back(std::move(room)); } -- cgit v1.2.3