aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-11-18 23:32:08 +0100
committerdec05eba <dec05eba@protonmail.com>2022-11-18 23:32:12 +0100
commitde45f6d8d7d777244006a7998ec971157e51296e (patch)
tree93ab54b196bda4af7d7fe16d74fb25f08c37a931 /src
parentc56cb5d25388d938fce485ff02b067a2fd70e096 (diff)
Readd meme gpg encryption in matrix, this time asynchronous decryption
of only visible items
Diffstat (limited to 'src')
-rw-r--r--src/Body.cpp2
-rw-r--r--src/Config.cpp1
-rw-r--r--src/DownloadUtils.cpp8
-rw-r--r--src/FileAnalyzer.cpp6
-rw-r--r--src/Program.cpp8
-rw-r--r--src/QuickMedia.cpp73
-rw-r--r--src/ResourceLoader.cpp8
-rw-r--r--src/StringUtils.cpp12
-rw-r--r--src/plugins/Matrix.cpp218
9 files changed, 282 insertions, 54 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index b21012d..fcf932c 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -1553,6 +1553,7 @@ namespace QuickMedia {
if(item->extra) {
Widgets widgets;
+ widgets.body_item = item.get();
if(thumbnail_drawn) {
ThumbnailWidget thumbnail;
thumbnail.position = image.get_position();
@@ -1624,6 +1625,7 @@ namespace QuickMedia {
if(item->extra) {
Widgets widgets;
+ widgets.body_item = item.get();
if(thumbnail_drawn) {
ThumbnailWidget thumbnail;
thumbnail.position = image.get_position();
diff --git a/src/Config.cpp b/src/Config.cpp
index 6a73dcf..570504f 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -226,6 +226,7 @@ namespace QuickMedia {
config->matrix.known_homeservers.push_back(known_homeserver.asString());
}
}
+ get_json_value(matrix_json, "gpg_user_id", config->matrix.gpg_user_id);
}
if(!has_known_matrix_homeservers_config)
diff --git a/src/DownloadUtils.cpp b/src/DownloadUtils.cpp
index 3fb8b19..913857b 100644
--- a/src/DownloadUtils.cpp
+++ b/src/DownloadUtils.cpp
@@ -19,14 +19,6 @@ namespace QuickMedia {
int64_t total_downloaded_size = 0;
};
- static int accumulate_string(char *data, int size, void *userdata) {
- std::string *str = (std::string*)userdata;
- if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable
- return 1;
- str->append(data, size);
- return 0;
- }
-
static bool http_is_redirect(const char *header, size_t size) {
const void *end_of_first_line_p = memmem(header, size, "\r\n", 2);
if(!end_of_first_line_p)
diff --git a/src/FileAnalyzer.cpp b/src/FileAnalyzer.cpp
index b02d0c2..65c33b5 100644
--- a/src/FileAnalyzer.cpp
+++ b/src/FileAnalyzer.cpp
@@ -139,12 +139,6 @@ namespace QuickMedia {
}
- static int accumulate_string(char *data, int size, void *userdata) {
- std::string *str = (std::string*)userdata;
- str->append(data, size);
- return 0;
- }
-
bool video_get_middle_frame(const FileAnalyzer &file, const char *destination_path, int width, int height) {
Path destination_path_tmp = destination_path;
destination_path_tmp.append(".tmp.jpg"); // TODO: .png, but the below code also needs to be changed for that
diff --git a/src/Program.cpp b/src/Program.cpp
index ee189f7..8b3cc49 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -13,6 +13,14 @@
#define READ_END 0
#define WRITE_END 1
+int accumulate_string(char *data, int size, void *userdata) {
+ std::string *str = (std::string*)userdata;
+ if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable
+ return 1;
+ str->append(data, size);
+ return 0;
+}
+
struct ReadWriteProgram {
pid_t pid = -1;
int read_fd = -1;
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 7e37214..074f3e9 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -5311,6 +5311,11 @@ namespace QuickMedia {
body_item->embedded_item = BodyItem::create("");
*body_item->embedded_item = *related_body_item;
+ if(related_body_item->extra) {
+ MatrixChatBodyItemData *other_item_data = static_cast<MatrixChatBodyItemData*>(related_body_item->extra.get());
+ if(other_item_data->decrypt_state != MatrixChatBodyItemData::DecryptState::DECRYPTED)
+ body_item->embedded_item->extra = std::make_shared<MatrixChatBodyItemData>(other_item_data->matrix, other_item_data->text_to_decrypt);
+ }
body_item->embedded_item->embedded_item = nullptr;
body_item->embedded_item->reactions.clear();
if(message->user->user_id != my_user_id && ((related_body_item->userdata && static_cast<Message*>(related_body_item->userdata)->user.get() == me) || message_contains_user_mention(body_item, my_display_name, my_user_id)))
@@ -5325,11 +5330,23 @@ namespace QuickMedia {
return load_cached_related_embedded_item(body_item, message, me.get(), current_room->get_user_display_name(me), me->user_id, message_body_items);
}
+ static void matrix_body_set_text_decrypt_if_needed(Matrix *matrix, BodyItem *body_item, std::string text) {
+ // TODO: Check if gpg is installed
+ if(!get_config().matrix.gpg_user_id.empty() && text.find("-----BEGIN PGP MESSAGE-----") != std::string::npos && text.find("-----END PGP MESSAGE-----") != std::string::npos) {
+ body_item->extra = std::make_shared<MatrixChatBodyItemData>(matrix, std::move(text));
+ body_item->set_description("🔒 Decrypting message...");
+ } else {
+ body_item->set_description(std::move(text));
+ }
+ }
+
static std::shared_ptr<BodyItem> message_to_body_item(Matrix *matrix, RoomData *room, Message *message, const std::string &my_display_name, const std::string &my_user_id) {
Message *latest_message = get_latest_message_in_edit_chain(message);
+
auto body_item = BodyItem::create("");
body_item->set_author(extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH));
- body_item->set_description(strip(message_to_qm_text(matrix, latest_message)));
+ //body_item->set_description(matrix_decrypt_gpg_message_if_needed(strip(message_to_qm_text(matrix, latest_message))));
+ matrix_body_set_text_decrypt_if_needed(matrix, body_item.get(), strip(message_to_qm_text(matrix, latest_message)));
body_item->set_timestamp(message->timestamp);
if(!message->thumbnail_url.empty()) {
body_item->thumbnail_url = message->thumbnail_url;
@@ -5631,7 +5648,8 @@ namespace QuickMedia {
if(message->timestamp > edited_message_ref->timestamp) {
std::string qm_formatted_text = message_to_qm_text(matrix, message.get());
- body_item->set_description(std::move(qm_formatted_text));
+ //body_item->set_description(matrix_decrypt_gpg_message_if_needed(std::move(qm_formatted_text)));
+ matrix_body_set_text_decrypt_if_needed(matrix, body_item.get(), std::move(qm_formatted_text));
if(message->user != me && message_contains_user_mention(matrix, message.get(), my_display_name, me->user_id))
body_item->set_description_color(get_theme().attention_alert_text_color, true);
else
@@ -5674,7 +5692,8 @@ namespace QuickMedia {
if(message->timestamp > edited_message_ref->timestamp) {
std::string qm_formatted_text = formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), true);
- body_item->set_description(std::move(qm_formatted_text));
+ //body_item->set_description(matrix_decrypt_gpg_message_if_needed(std::move(qm_formatted_text)));
+ matrix_body_set_text_decrypt_if_needed(matrix, body_item.get(), std::move(qm_formatted_text));
if(message->user != me && message_contains_user_mention(matrix, message.get(), my_display_name, me->user_id))
body_item->set_description_color(get_theme().attention_alert_text_color, true);
else
@@ -6028,7 +6047,8 @@ namespace QuickMedia {
"/leave: Leave the current room.\n"
"/me [text]: Send a message of type \"m.emote\".\n"
"/react [text]: React to the selected message (also works if you are replying to a message).\n"
- "/id: Show the room id.";
+ "/id: Show the room id.\n"
+ "/encrypt [text]: Send a message encrypted with gpg. gpg needs to be installed to do this. Uses the gpg key specified by the user id in your config variable \"matrix.gpg_user_id\".";
message->timestamp = time(nullptr) * 1000; // TODO: What if the user has broken local time?
matrix->append_system_message(current_room, std::move(message));
@@ -6052,7 +6072,7 @@ namespace QuickMedia {
} else if(strncmp(text.c_str(), "/react ", 7) == 0) {
msgtype = "m.reaction";
text.erase(text.begin(), text.begin() + 7);
- } else {
+ } else if(strncmp(text.c_str(), "/encrypt ", 9) != 0) {
show_notification("QuickMedia", "Error: invalid command: " + text + ", type /help to see a list of valid commands.", Urgency::NORMAL);
return false;
}
@@ -6061,7 +6081,27 @@ namespace QuickMedia {
msgtype = "m.reaction";
text.erase(text.begin(), text.begin() + 7);
}
- }
+ }
+
+ if((chat_state == ChatState::TYPING_MESSAGE || chat_state == ChatState::EDITING || chat_state == ChatState::REPLYING) && msgtype.empty() && text[0] == '/' && strncmp(text.c_str(), "/encrypt ", 9) == 0) {
+ text.erase(text.begin(), text.begin() + 9);
+ if(!is_program_executable_by_name("gpg")) {
+ show_notification("QuickMedia", "GPG needs to be installed to use the /encrypt command", Urgency::CRITICAL);
+ return false;
+ }
+
+ if(get_config().matrix.gpg_user_id.empty()) {
+ show_notification("QuickMedia", "The config variable matrix.gpg_user_id needs to be set to use the /encrypt command", Urgency::CRITICAL);
+ return false;
+ }
+
+ std::string encrypted_string;
+ if(!matrix_gpg_encrypt_for_each_user_in_room(matrix, current_room, get_config().matrix.gpg_user_id, text, encrypted_string)) {
+ show_notification("QuickMedia", "Failed to encrypt message with gpg. Make sure you used the correct gpg user id in the config variable matrix.gpg_user_id", Urgency::CRITICAL);
+ return false;
+ }
+ text = std::move(encrypted_string);
+ }
auto message = std::make_shared<Message>();
message->body_is_formatted = true;
@@ -6178,7 +6218,8 @@ namespace QuickMedia {
std::string qm_formatted_text = formatted_text_to_qm_text(matrix, formatted_text.c_str(), formatted_text.size(), true);
auto body_item_shared_ptr = tabs[MESSAGES_TAB_INDEX].body->get_item_by_index(body_item_index);
- body_item_shared_ptr->set_description(std::move(qm_formatted_text));
+ //body_item_shared_ptr->set_description(matrix_decrypt_gpg_message_if_needed(std::move(qm_formatted_text)));
+ matrix_body_set_text_decrypt_if_needed(matrix, body_item_shared_ptr.get(), std::move(qm_formatted_text));
body_item_shared_ptr->set_description_color(get_theme().provisional_message_color);
//auto edit_body_item = message_to_body_item(matrix, current_room, message.get(), current_room->get_user_avatar_url(me), me->user_id);
@@ -6272,6 +6313,11 @@ namespace QuickMedia {
auto related_body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size(), event_data->event_id);
if(related_body_item) {
*body_item = *related_body_item;
+ if(related_body_item->extra) {
+ MatrixChatBodyItemData *other_item_data = static_cast<MatrixChatBodyItemData*>(related_body_item->extra.get());
+ if(other_item_data->decrypt_state != MatrixChatBodyItemData::DecryptState::DECRYPTED)
+ body_item->extra = std::make_shared<MatrixChatBodyItemData>(other_item_data->matrix, other_item_data->text_to_decrypt);
+ }
body_item->reactions.clear();
if(message_contains_user_mention(related_body_item.get(), current_room->get_user_display_name(me), me->user_id))
body_item->set_description_color(get_theme().attention_alert_text_color, true);
@@ -7616,8 +7662,9 @@ namespace QuickMedia {
break;
}
- if(last_timeline_message != -1) {
- current_room->body_item->set_description(current_room->latest_message);
+ if(last_timeline_message != -1 && (!current_room->body_item->extra || static_cast<MatrixChatBodyItemData*>(current_room->body_item->extra.get())->decrypt_state == MatrixChatBodyItemData::DecryptState::DECRYPTED)) {
+ if(current_room->offset_to_latest_message_text < (int)current_room->body_item->get_description().size())
+ current_room->body_item->set_description(current_room->body_item->get_description().substr(current_room->offset_to_latest_message_text));
current_room->body_item->set_description_color(get_theme().faded_text_color);
// TODO: Show a line like nheko instead for unread messages, or something else
current_room->body_item->set_title_color(get_theme().text_color);
@@ -7876,14 +7923,6 @@ namespace QuickMedia {
}
}
- static int accumulate_string(char *data, int size, void *userdata) {
- std::string *str = (std::string*)userdata;
- if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable
- return 1;
- str->append(data, size);
- return 0;
- }
-
void Program::download_page(std::string url, std::string download_filename, bool no_dialog) {
window.set_title(("QuickMedia - Select where you want to save " + std::string(url)).c_str());
diff --git a/src/ResourceLoader.cpp b/src/ResourceLoader.cpp
index 86687d2..a120096 100644
--- a/src/ResourceLoader.cpp
+++ b/src/ResourceLoader.cpp
@@ -26,14 +26,6 @@ namespace QuickMedia {
return resource_root.c_str();
}
- static int accumulate_string(char *data, int size, void *userdata) {
- std::string *str = (std::string*)userdata;
- if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable
- return 1;
- str->append(data, size);
- return 0;
- }
-
// If absolute, use the path; otherwise use fc-match to find the font
static bool find_font(const std::string &font_name, std::string &font_filepath_result) {
if(get_config().font.latin.find('/') != std::string::npos) {
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index bc59730..564f307 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -3,7 +3,7 @@
namespace QuickMedia {
template <typename T>
- static void string_split_t(const std::string &str, const T &delimiter, StringSplitCallback callback_func, bool include_empty) {
+ static void string_split_t(const std::string_view str, const T &delimiter, StringSplitCallback callback_func, bool include_empty) {
size_t index = 0;
while(index < str.size()) {
size_t new_index = str.find(delimiter, index);
@@ -23,10 +23,18 @@ namespace QuickMedia {
}
void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty) {
- string_split_t(str, delimiter, callback_func, include_empty);
+ string_split_t(std::string_view(str), delimiter, callback_func, include_empty);
}
void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func, bool include_empty) {
+ string_split_t(std::string_view(str), delimiter, callback_func, include_empty);
+ }
+
+ void string_split_view(const std::string_view str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty) {
+ string_split_t(str, delimiter, callback_func, include_empty);
+ }
+
+ void string_split_view(const std::string_view str, char delimiter, StringSplitCallback callback_func, bool include_empty) {
string_split_t(str, delimiter, callback_func, include_empty);
}
diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp
index 973571e..953543c 100644
--- a/src/plugins/Matrix.cpp
+++ b/src/plugins/Matrix.cpp
@@ -41,6 +41,145 @@ namespace QuickMedia {
static const char* ADDITIONAL_MESSAGES_FILTER = "{\"presence\":{\"types\":[\"\"]},\"account_data\":{\"limit\":0,\"types\":[\"\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\",\"m.room.canonical_alias\",\"m.space.child\"],\"lazy_load_members\":true},\"timeline\":{\"limit\":20,\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true}}}";
static const char* CONTINUE_FILTER = "{\"presence\":{\"limit\":0,\"types\":[\"\"]},\"account_data\":{\"types\":[\"qm.emoji\",\"m.direct\"]},\"room\":{\"state\":{\"not_types\":[\"m.room.related_groups\",\"m.room.power_levels\",\"m.room.join_rules\",\"m.room.history_visibility\",\"m.room.canonical_alias\",\"m.space.child\"],\"lazy_load_members\":true},\"timeline\":{\"lazy_load_members\":true},\"ephemeral\":{\"limit\":0,\"types\":[\"\"],\"lazy_load_members\":true},\"account_data\":{\"types\":[\"m.fully_read\",\"m.tag\",\"qm.last_read_message_timestamp\"],\"lazy_load_members\":true}}}";
+ static bool is_gpg_installed = false;
+ static bool gpg_installed_checked = false;
+
+ static std::string matrix_decrypt_gpg_message_if_needed(std::string message, bool &success) {
+ success = false;
+
+ if(!gpg_installed_checked) {
+ gpg_installed_checked = true;
+ is_gpg_installed = is_program_executable_by_name("gpg");
+ }
+
+ std::string result;
+ if(!is_gpg_installed || get_config().matrix.gpg_user_id.empty()) {
+ result = std::move(message);
+ } else {
+ size_t pgp_begin_index;
+ size_t pgp_end_index;
+ if((pgp_begin_index = message.find("-----BEGIN PGP MESSAGE-----")) != std::string::npos && (pgp_end_index = message.find("-----END PGP MESSAGE-----", pgp_begin_index + 27)) != std::string::npos) {
+ std::string decrypted_string;
+ const char *args[] = { "gpg", "-d", nullptr };
+ const std::string_view pgp_message(message.data() + pgp_begin_index, (pgp_end_index + 25) - pgp_begin_index);
+ if(exec_program_write_stdin(args, pgp_message.data(), pgp_message.size(), accumulate_string, &decrypted_string) != 0) {
+ result = "🔒 Failed to decrypt message:\n" + std::move(message);
+ } else {
+ decrypted_string.insert(0, "🔒 ", strlen("🔒 "));
+ result = std::move(message);
+ result.replace(result.begin() + pgp_begin_index, result.begin() + pgp_begin_index + pgp_message.size(), std::move(decrypted_string));
+ success = true;
+ }
+ } else {
+ result = std::move(message);
+ }
+ }
+ return result;
+ }
+
+ enum class GpgLineType {
+ UID,
+ FPR,
+ UNKNOWN
+ };
+
+ static std::string user_id_to_email_format(const std::string_view user_id) {
+ if(user_id.empty() || user_id[0] != '@')
+ return "";
+
+ size_t colon_index = user_id.find(':');
+ if(colon_index == std::string::npos)
+ return "";
+
+ return std::string(user_id.substr(1, colon_index - 1)) + "@" + std::string(user_id.substr(colon_index + 1));
+ }
+
+ static std::string_view gpg_display_name_extract_email(const std::string_view display_name) {
+ if(display_name.empty() || display_name.back() != '>')
+ return std::string_view();
+
+ size_t email_start_index = display_name.rfind('<');
+ if(email_start_index == std::string::npos)
+ return std::string_view();
+
+ email_start_index += 1;
+ return display_name.substr(email_start_index, display_name.size() - 1 - email_start_index);
+ }
+
+ static bool for_each_user_with_matching_gpg_key(const std::vector<std::shared_ptr<UserInfo>> &users, std::function<void(std::string_view pub_key)> callback) {
+ std::string output;
+ const char *args[] = { "gpg", "--list-keys", "--with-colons", nullptr };
+ if(exec_program(args, accumulate_string, &output) != 0)
+ return false;
+
+ std::unordered_set<std::string> users_by_email;
+ for(auto &user : users) {
+ std::string user_email = user_id_to_email_format(user->user_id);
+ if(!user_email.empty())
+ users_by_email.insert(std::move(user_email));
+ }
+
+ std::string_view fpr;
+ string_split(output, '\n', [&](const char *str, size_t size) {
+ GpgLineType line_type = GpgLineType::UNKNOWN;
+ int column = 0;
+
+ string_split_view(std::string_view(str, size), ':', [&](const char *str, size_t size) {
+ std::string_view section(str, size);
+ if(column == 0) {
+ if(section == "uid")
+ line_type = GpgLineType::UID;
+ else if(section == "fpr")
+ line_type = GpgLineType::FPR;
+ } else if(column == 9) {
+ if(line_type == GpgLineType::UID) {
+ // Assumes that each uid is preceeded with fpr
+ const std::string user_email = std::string(gpg_display_name_extract_email(section));
+ if(!user_email.empty() && users_by_email.find(user_email) != users_by_email.end()) {
+ fprintf(stderr, "found public key %.*s for user %s\n", (int)fpr.size(), fpr.data(), user_email.c_str());
+ callback(fpr);
+ }
+ } else if(line_type == GpgLineType::FPR) {
+ fpr = section;
+ }
+ }
+
+ ++column;
+ return true;
+ });
+
+ return true;
+ });
+
+ return true;
+ }
+
+ bool matrix_gpg_encrypt_for_each_user_in_room(Matrix *matrix, RoomData *room, const std::string &my_gpg_user_id, const std::string &str, std::string &encrypted_str) {
+ auto me = matrix->get_me(room);
+ if(!me) {
+ fprintf(stderr, "Error: matrix->get_me failed\n");
+ return false;
+ }
+
+ std::vector<std::string> user_public_keys;
+ const bool found_users = for_each_user_with_matching_gpg_key(room->get_users_excluding_me(me->user_id), [&](std::string_view pub_key) {
+ user_public_keys.emplace_back(pub_key);
+ });
+
+ if(!found_users) {
+ fprintf(stderr, "Error: gpg --list-keys failed\n");
+ return false;
+ }
+
+ std::vector<const char*> args = { "gpg", "-e", "-r", my_gpg_user_id.c_str(), "-u", my_gpg_user_id.c_str(), "--armor", "--trust-model", "always", "--comment", "This message requires you to use QuickMedia to view it and the user that sent it needs to have your key imported in gpg" };
+ for(const std::string &pub_key : user_public_keys) {
+ args.push_back("-r");
+ args.push_back(pub_key.c_str());
+ }
+ args.push_back(nullptr);
+ return exec_program_write_stdin(args.data(), str.c_str(), str.size(), accumulate_string, &encrypted_str) == 0;
+ }
+
static std::string capitalize(const std::string &str) {
if(str.size() >= 1)
return to_upper(str[0]) + str.substr(1);
@@ -163,6 +302,35 @@ namespace QuickMedia {
}
}
+ MatrixChatBodyItemData::~MatrixChatBodyItemData() {
+ if(decrypt_job)
+ decrypt_job->cancel = true;
+ }
+
+ void MatrixChatBodyItemData::draw_overlay(mgl::Window&, const Widgets &widgets) {
+ switch(decrypt_state) {
+ case DecryptState::NOT_DECRYPTED: {
+ decrypt_state = DecryptState::DECRYPTING;
+ decrypt_job = std::make_shared<MatrixChatBodyDecryptJob>();
+ decrypt_job->text = text_to_decrypt;
+ decrypt_job->decrypt_state = MatrixChatBodyDecryptJob::DecryptState::DECRYPTING;
+ matrix->async_decrypt_message(decrypt_job);
+ break;
+ }
+ case DecryptState::DECRYPTING: {
+ if(decrypt_job->decrypt_state == MatrixChatBodyDecryptJob::DecryptState::DECRYPTED || decrypt_job->decrypt_state == MatrixChatBodyDecryptJob::DecryptState::FAILED_TO_DECRYPT) {
+ decrypt_state = DecryptState::DECRYPTED;
+ widgets.body_item->set_description(std::move(decrypt_job->text));
+ decrypt_job.reset();
+ }
+ break;
+ }
+ case DecryptState::DECRYPTED: {
+ break;
+ }
+ }
+ }
+
bool TimestampedDisplayData::set_data_if_newer(std::string new_data, time_t new_timestamp) {
if(new_timestamp == 0) {
data = std::move(new_data);
@@ -307,6 +475,7 @@ namespace QuickMedia {
std::vector<std::shared_ptr<UserInfo>> RoomData::get_users_excluding_me(const std::string &my_user_id) {
std::lock_guard<std::mutex> lock(user_mutex);
std::vector<std::shared_ptr<UserInfo>> users_excluding_me;
+ // TODO: Optimize
for(auto &[user_id, user] : user_info_by_user_id) {
if(user->user_id != my_user_id) {
users_excluding_me.push_back(user);
@@ -687,6 +856,10 @@ namespace QuickMedia {
return body;
}
+ static bool should_message_by_decrypted(const std::string &text) {
+ return !get_config().matrix.gpg_user_id.empty() && text.find("-----BEGIN PGP MESSAGE-----") != std::string::npos && text.find("-----END PGP MESSAGE-----") != std::string::npos;
+ }
+
void MatrixQuickMedia::update_room_description(RoomData *room, const Messages &new_messages, bool is_initial_sync, bool sync_is_cache) {
time_t read_marker_message_timestamp = 0;
std::shared_ptr<UserInfo> me = matrix->get_me(room);
@@ -756,11 +929,13 @@ namespace QuickMedia {
if(!unread_mentions && set_room_as_unread)
room_desc += "Unread: ";
- room->latest_message = extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_unread_message, custom_emoji_max_size);
- room_desc += room->latest_message;
+ room->offset_to_latest_message_text = room_desc.size();
+ room_desc += extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_unread_message, custom_emoji_max_size);
room->body_item->set_description(std::move(room_desc));
room->body_item->set_description_max_lines(3);
+ if(should_message_by_decrypted(room->body_item->get_description()))
+ room->body_item->extra = std::make_shared<MatrixChatBodyItemData>(matrix, room->body_item->get_description());
if(set_room_as_unread)
room->body_item->set_title_color(get_theme().attention_alert_text_color, true);
room->last_message_read = false;
@@ -768,10 +943,12 @@ namespace QuickMedia {
rooms_page->move_room_to_top(room);
room_tags_page->move_room_to_top(room);
} else if(last_new_message) {
+ room->offset_to_latest_message_text = 0;
room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_new_message.get(), custom_emoji_max_size));
room->body_item->set_description_color(get_theme().faded_text_color);
room->body_item->set_description_max_lines(3);
- room->latest_message = room->body_item->get_description();
+ if(should_message_by_decrypted(room->body_item->get_description()))
+ room->body_item->extra = std::make_shared<MatrixChatBodyItemData>(matrix, room->body_item->get_description());
rooms_page->move_room_to_top(room);
room_tags_page->move_room_to_top(room);
@@ -1763,6 +1940,10 @@ namespace QuickMedia {
custom_emoji_by_key.clear();
silenced_invites.clear();
qm_read_markers_by_room_cache.clear();
+ if(decrypt_thread.joinable()) {
+ decrypt_task.close();
+ decrypt_thread.join();
+ }
}
bool Matrix::is_initial_sync_finished() {
@@ -2674,14 +2855,6 @@ namespace QuickMedia {
mgl::vec2i image_max_size;
};
- static int accumulate_string(char *data, int size, void *userdata) {
- std::string *str = (std::string*)userdata;
- if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable
- return 1;
- str->append(data, size);
- return 0;
- }
-
// TODO: Full proper parsing with tag depth
static int formattext_text_parser_callback(HtmlParser *html_parser, HtmlParseType parse_type, void *userdata) {
FormattedTextParseUserdata &parse_userdata = *(FormattedTextParseUserdata*)userdata;
@@ -3882,7 +4055,7 @@ namespace QuickMedia {
if(is_inside_code_block)
formatted_body += '\n';
else
- formatted_body += "<br/>";
+ formatted_body += "<br>";
}
std::string line_str(str, size);
@@ -3924,6 +4097,26 @@ namespace QuickMedia {
}
+ void Matrix::async_decrypt_message(std::shared_ptr<MatrixChatBodyDecryptJob> decrypt_job) {
+ if(!decrypt_thread.joinable()) {
+ decrypt_thread = std::thread([this]() {
+ while(decrypt_task.is_running()) {
+ auto decrypt_job = decrypt_task.pop_wait();
+ if(!decrypt_job)
+ return;
+
+ if(decrypt_job.value()->cancel)
+ continue;
+
+ bool success = false;
+ decrypt_job.value()->text = matrix_decrypt_gpg_message_if_needed(std::move(decrypt_job.value()->text), success);
+ decrypt_job.value()->decrypt_state = success ? MatrixChatBodyDecryptJob::DecryptState::DECRYPTED : MatrixChatBodyDecryptJob::DecryptState::FAILED_TO_DECRYPT;
+ }
+ });
+ }
+ decrypt_task.push(std::move(decrypt_job));
+ }
+
PluginResult Matrix::post_message(RoomData *room, const std::string &body, std::string &event_id_response, const std::optional<UploadInfo> &file_info, const std::optional<UploadInfo> &thumbnail_info, const std::string &msgtype, const std::string &custom_transaction_id) {
std::string transaction_id = custom_transaction_id;
if(transaction_id.empty())
@@ -5855,7 +6048,6 @@ namespace QuickMedia {
}
void Matrix::update() {
- mgl::Clock timer;
std::optional<std::function<void()>> task;
while((task = ui_thread_tasks.pop_if_available()) != std::nullopt) {
task.value()();