1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-06-16 21:35:24 +00:00

Change DB stucture, improve LMC behaviour

**Please, backup your DB before performing any testing.**

Introduce new DB structure and DB migration mechanism.
Index `timestamp`, `to_jid`, `from_jid` columns to improve performance.
Add trigger for `replaced_by_db_id` calculation by DB on message insert.

Now LMC messages are interconnected with original messages,
this way we have fast access to last (hence correct) applicable edits,
as well as reference to the original message from any edit (in case of chained edits).

Change the way LMC messages are being displayed. Now we check if we
can replace a message from current buffer. If we don't have a message in
the buffer, it might've been lost, but we can still display it as a
new message.

Further information available here:
https://github.com/profanity-im/profanity/pull/1893
https://github.com/profanity-im/profanity/issues/1899
https://github.com/profanity-im/profanity/pull/1902
This commit is contained in:
John Hernandez 2023-11-01 17:11:46 +01:00
parent 0b957d6207
commit d89b5a04fa
3 changed files with 273 additions and 94 deletions

View File

@ -36,6 +36,7 @@
#include "config.h"
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sqlite3.h>
#include <glib.h>
#include <stdio.h>
@ -58,9 +59,14 @@ static void _add_to_db(ProfMessage* message, char* type, const Jid* const from_j
static char* _get_db_filename(ProfAccount* account);
static prof_msg_type_t _get_message_type_type(const char* const type);
static prof_enc_t _get_message_enc_type(const char* const encstr);
static int _get_db_version(void);
static gboolean _migrate_to_v2(void);
static gboolean _check_available_space_for_db_migration(char* path_to_db);
#define auto_sqlite __attribute__((__cleanup__(auto_free_sqlite)))
static const int latest_version = 2;
static void
auto_free_sqlite(gchar** str)
{
@ -97,43 +103,112 @@ log_database_init(ProfAccount* account)
}
char* err_msg;
// id is the ID of DB the entry
// from_jid is the senders jid
// to_jid is the receivers jid
// from_resource is the senders resource
// to_jid is the receivers resource
// message is the message text
// timestamp the timestamp like "2020/03/24 11:12:14"
int db_version = _get_db_version();
if (db_version == latest_version) {
return TRUE;
}
// ChatLogs Table
// Contains all chat messages
//
// id is primary key
// from_jid is the sender's jid
// to_jid is the receiver's jid
// from_resource is the sender's resource
// to_resource is the receiver's resource
// message is the message's text
// timestamp is the timestamp like "2020/03/24 11:12:14"
// type is there to distinguish: message (chat), MUC message (muc), muc pm (mucpm)
// stanza_id is the ID in <message>
// archive_id is the stanza-id from from XEP-0359: Unique and Stable Stanza IDs used for XEP-0313: Message Archive Management
// replace_id is the ID from XEP-0308: Last Message Correction
// encryption is to distinguish: none, omemo, otr, pgp
// marked_read is 0/1 whether a message has been marked as read via XEP-0333: Chat Markers
char* query = "CREATE TABLE IF NOT EXISTS `ChatLogs` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `from_jid` TEXT NOT NULL, `to_jid` TEXT NOT NULL, `from_resource` TEXT, `to_resource` TEXT, `message` TEXT, `timestamp` TEXT, `type` TEXT, `stanza_id` TEXT, `archive_id` TEXT, `replace_id` TEXT, `encryption` TEXT, `marked_read` INTEGER)";
// replace_id is the ID from XEP-0308: Last Message Correction
// replaces_db_id is ID (primary key) of the original message that LMC message corrects/replaces
// replaced_by_db_id is ID (primary key) of the last correcting (LMC) message for the original message
char* query = "CREATE TABLE IF NOT EXISTS `ChatLogs` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT, "
"`from_jid` TEXT NOT NULL, "
"`to_jid` TEXT NOT NULL, "
"`from_resource` TEXT, "
"`to_resource` TEXT, "
"`message` TEXT, "
"`timestamp` TEXT, "
"`type` TEXT, "
"`stanza_id` TEXT, "
"`archive_id` TEXT, "
"`encryption` TEXT, "
"`marked_read` INTEGER, "
"`replace_id` TEXT, "
"`replaces_db_id` INTEGER, "
"`replaced_by_db_id` INTEGER)";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
goto out;
}
query = "CREATE TABLE IF NOT EXISTS `DbVersion` ( `dv_id` INTEGER PRIMARY KEY, `version` INTEGER UNIQUE)";
query = "CREATE TRIGGER IF NOT EXISTS update_corrected_message "
"AFTER INSERT ON ChatLogs "
"FOR EACH ROW "
"WHEN NEW.replaces_db_id IS NOT NULL "
"BEGIN "
"UPDATE ChatLogs "
"SET replaced_by_db_id = NEW.id "
"WHERE id = NEW.replaces_db_id; "
"END;";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
log_error("Unable to add `update_corrected_message` trigger.");
goto out;
}
query = "CREATE INDEX IF NOT EXISTS ChatLogs_timestamp_IDX ON `ChatLogs` (`timestamp`)";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
log_error("Unable to create index for timestamp.");
goto out;
}
query = "CREATE INDEX IF NOT EXISTS ChatLogs_to_from_jid_IDX ON `ChatLogs` (`to_jid`, `from_jid`)";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
log_error("Unable to create index for to_jid.");
goto out;
}
query = "CREATE TABLE IF NOT EXISTS `DbVersion` (`dv_id` INTEGER PRIMARY KEY, `version` INTEGER UNIQUE)";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
goto out;
}
query = "INSERT OR IGNORE INTO `DbVersion` (`version`) VALUES('1')";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
if (db_version == -1) {
query = "INSERT OR IGNORE INTO `DbVersion` (`version`) VALUES ('2')";
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
goto out;
}
db_version = _get_db_version();
}
// Unlikely event, but we don't want to migrate if we are just unable to determine the DB version
if (db_version == -1) {
cons_show_error("DB Initialization Error: Unable to check DB version.");
goto out;
}
if (db_version < latest_version) {
cons_show("Migrating database schema. This operation may take a while...");
if (db_version < 2 && (!_check_available_space_for_db_migration(filename) || !_migrate_to_v2())) {
cons_show_error("Database Initialization Error: Unable to migrate database to version 2. Please, check error logs for details.");
goto out;
}
cons_show("Database schema migration was successful.");
}
log_debug("Initialized SQLite database: %s", filename);
return TRUE;
out:
if (err_msg) {
log_error("SQLite error: %s", err_msg);
log_error("SQLite error in log_database_init(): %s", err_msg);
sqlite3_free(err_msg);
} else {
log_error("Unknown SQLite error");
log_error("Unknown SQLite error in log_database_init().");
}
return FALSE;
}
@ -202,17 +277,17 @@ ProfMessage*
log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last)
{
sqlite3_stmt* stmt = NULL;
gchar* query;
const char* jid = connection_get_fulljid();
auto_jid Jid* myjid = jid_create(jid);
if (!myjid)
return NULL;
if (is_last) {
query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE (`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q') ORDER BY `timestamp` DESC LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid);
} else {
query = sqlite3_mprintf("SELECT * FROM (SELECT `archive_id`, `timestamp` from `ChatLogs` WHERE (`from_jid` = '%q' AND `to_jid` = '%q') OR (`from_jid` = '%q' AND `to_jid` = '%q') ORDER BY `timestamp` ASC LIMIT 1) ORDER BY `timestamp` ASC;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid);
}
const char* order = is_last ? "DESC" : "ASC";
auto_sqlite char* query = sqlite3_mprintf("SELECT `archive_id`, `timestamp` FROM `ChatLogs` WHERE "
"(`from_jid` = '%q' AND `to_jid` = '%q') OR "
"(`from_jid` = '%q' AND `to_jid` = '%q') "
"ORDER BY `timestamp` %s LIMIT 1;",
contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, order);
if (!query) {
log_error("Could not allocate memory for SQL query in log_database_get_limits_info()");
@ -221,7 +296,7 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
log_error("Unknown SQLite error in log_database_get_last_info()");
log_error("Unknown SQLite error in log_database_get_last_info().");
return NULL;
}
@ -235,7 +310,6 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
msg->timestamp = g_date_time_new_from_iso8601(date, NULL);
}
sqlite3_finalize(stmt);
sqlite3_free(query);
return msg;
}
@ -259,9 +333,9 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s
auto_gchar gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now);
auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM ("
"SELECT COALESCE(B.`message`, A.`message`) AS message, "
"A.`timestamp`, A.`from_jid`, A.`type`, A.`encryption` FROM `ChatLogs` AS A "
"LEFT JOIN `ChatLogs` AS B ON (A.`stanza_id` = B.`replace_id` AND A.`from_jid` = B.`from_jid`) "
"WHERE A.`replace_id` = '' "
"A.`timestamp`, A.`from_jid`, A.`to_jid`, A.`type`, A.`encryption` FROM `ChatLogs` AS A "
"LEFT JOIN `ChatLogs` AS B ON (A.`replaced_by_db_id` = B.`id` AND A.`from_jid` = B.`from_jid`) "
"WHERE (A.`replaces_db_id` IS NULL OR A.`replaces_db_id` = '') "
"AND ((A.`from_jid` = '%q' AND A.`to_jid` = '%q') OR (A.`from_jid` = '%q' AND A.`to_jid` = '%q')) "
"AND A.`timestamp` < '%q' "
"AND (%Q IS NULL OR A.`timestamp` > %Q) "
@ -278,22 +352,23 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s
int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
log_error("Unknown SQLite error in log_database_get_previous_chat()");
log_error("SQLite error in log_database_get_previous_chat(): %s", sqlite3_errmsg(g_chatlog_database));
return NULL;
}
GSList* history = NULL;
while (sqlite3_step(stmt) == SQLITE_ROW) {
// TODO: also save to jid. since now part of profmessage
char* message = (char*)sqlite3_column_text(stmt, 0);
char* date = (char*)sqlite3_column_text(stmt, 1);
char* from = (char*)sqlite3_column_text(stmt, 2);
char* type = (char*)sqlite3_column_text(stmt, 3);
char* encryption = (char*)sqlite3_column_text(stmt, 4);
char* to_jid = (char*)sqlite3_column_text(stmt, 3);
char* type = (char*)sqlite3_column_text(stmt, 4);
char* encryption = (char*)sqlite3_column_text(stmt, 5);
ProfMessage* msg = message_init();
msg->from_jid = jid_create(from);
msg->to_jid = jid_create(to_jid);
msg->plain = strdup(message);
msg->timestamp = g_date_time_new_from_iso8601(date, NULL);
msg->type = _get_message_type_type(type);
@ -375,6 +450,7 @@ static void
_add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Jid* const to_jid)
{
auto_gchar gchar* pref_dblog = prefs_get_string(PREF_DBLOG);
sqlite_int64 original_message_id = -1;
if (g_strcmp0(pref_dblog, "off") == 0) {
return;
@ -407,10 +483,10 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
type = (char*)_get_message_type_str(message->type);
}
// Check LMC validity (XEP-0308)
// Apply LMC and check its validity (XEP-0308)
if (message->replace_id) {
auto_sqlite char* replace_check_query = sqlite3_mprintf("SELECT `from_jid` FROM `ChatLogs` WHERE `stanza_id` = '%q'",
message->replace_id ? message->replace_id : "");
auto_sqlite char* replace_check_query = sqlite3_mprintf("SELECT `id`, `from_jid`, `replaces_db_id` FROM `ChatLogs` WHERE `stanza_id` = %Q ORDER BY `timestamp` DESC LIMIT 1",
message->replace_id);
if (!replace_check_query) {
log_error("Could not allocate memory for SQL replace query in log_database_add()");
@ -419,48 +495,60 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
sqlite3_stmt* lmc_stmt = NULL;
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, replace_check_query, -1, &lmc_stmt, NULL)) {
if (sqlite3_step(lmc_stmt) == SQLITE_ROW) {
const char* from_jid_orig = (const char*)sqlite3_column_text(lmc_stmt, 0);
if (SQLITE_OK != sqlite3_prepare_v2(g_chatlog_database, replace_check_query, -1, &lmc_stmt, NULL)) {
log_error("SQLite error in _add_to_db() on selecting original message: %s", sqlite3_errmsg(g_chatlog_database));
return;
}
if (g_strcmp0(from_jid_orig, from_jid->barejid) != 0) {
log_error("Mismatch in sender JIDs when trying to do LMC. Corrected message sender: %s. Original message sender: %s. Replace-ID: %s. Message: %s", from_jid->barejid, from_jid_orig, message->replace_id, message->plain);
cons_show_error("%s sent message correction with mismatched sender. See log for details.", from_jid->barejid);
sqlite3_finalize(lmc_stmt);
return;
}
if (sqlite3_step(lmc_stmt) == SQLITE_ROW) {
original_message_id = sqlite3_column_int64(lmc_stmt, 0);
const char* from_jid_orig = (const char*)sqlite3_column_text(lmc_stmt, 1);
// Handle non-XEP-compliant replacement messages (edit->edit->original)
sqlite_int64 tmp = sqlite3_column_int64(lmc_stmt, 2);
original_message_id = tmp ? tmp : original_message_id;
if (g_strcmp0(from_jid_orig, from_jid->barejid) != 0) {
log_error("Mismatch in sender JIDs when trying to do LMC. Corrected message sender: %s. Original message sender: %s. Replace-ID: %s. Message: %s", from_jid->barejid, from_jid_orig, message->replace_id, message->plain);
cons_show_error("%s sent a message correction with mismatched sender. See log for details.", from_jid->barejid);
sqlite3_finalize(lmc_stmt);
return;
}
sqlite3_finalize(lmc_stmt);
} else {
log_warning("Got LMC message that does not have original message counterpart in the database from %s", message->from_jid->fulljid);
}
sqlite3_finalize(lmc_stmt);
}
// stanza-id (XEP-0359) doesn't have to be present in the message.
// But if it's duplicated, it's a serious server-side problem, so we better track it.
if (message->stanzaid) {
auto_sqlite char* duplicate_check_query = sqlite3_mprintf("SELECT 1 FROM `ChatLogs` WHERE (`archive_id` = '%q')",
message->stanzaid);
if (!duplicate_check_query) {
log_error("Could not allocate memory for SQL duplicate query in log_database_add()");
return;
}
sqlite3_stmt* stmt;
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, duplicate_check_query, -1, &stmt, NULL)) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
log_error("Duplicate stanza-id found for the message. stanza_id: %s; archive_id: %s; sender: %s; content: %s", message->id, message->stanzaid, from_jid->barejid, message->plain);
cons_show_error("Got a message with duplicate (server-generated) stanza-id from %s.", from_jid->fulljid);
}
sqlite3_finalize(stmt);
}
}
// Check for duplicate messages
auto_sqlite char* duplicate_check_query = sqlite3_mprintf("SELECT 1 FROM `ChatLogs` WHERE (`archive_id` = '%q' AND `archive_id` != '') OR (`stanza_id` = '%q' AND `stanza_id` != '')",
message->stanzaid ? message->stanzaid : "",
message->id ? message->id : "");
auto_sqlite char* orig_message_id = original_message_id == -1 ? NULL : sqlite3_mprintf("%d", original_message_id);
if (!duplicate_check_query) {
log_error("Could not allocate memory for SQL duplicate query in log_database_add()");
return;
}
int duplicate_exists = 0;
sqlite3_stmt* stmt;
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, duplicate_check_query, -1, &stmt, NULL)) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
duplicate_exists = 1;
}
sqlite3_finalize(stmt);
}
if (duplicate_exists) {
log_warning("Duplicate stanza-id found for the message. stanza_id: %s; archive_id: %s; sender: %s; content: %s", message->id, message->stanzaid, from_jid->barejid, message->plain);
return;
}
// Insert the message
auto_sqlite char* query = sqlite3_mprintf("INSERT INTO `ChatLogs` (`from_jid`, `from_resource`, `to_jid`, `to_resource`, `message`, `timestamp`, `stanza_id`, `archive_id`, `replace_id`, `type`, `encryption`) VALUES ('%q', '%q', '%q', '%q', '%q', '%q', '%q', '%q', '%q', '%q', '%q')",
auto_sqlite char* query = sqlite3_mprintf("INSERT INTO `ChatLogs` "
"(`from_jid`, `from_resource`, `to_jid`, `to_resource`, "
"`message`, `timestamp`, `stanza_id`, `archive_id`, "
"`replaces_db_id`, `replace_id`, `type`, `encryption`) "
"VALUES (%Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q)",
from_jid->barejid,
from_jid->resourcepart ? from_jid->resourcepart : "",
to_jid->barejid,
@ -469,6 +557,7 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
date_fmt ? date_fmt : "",
message->id ? message->id : "",
message->stanzaid ? message->stanzaid : "",
orig_message_id,
message->replace_id ? message->replace_id : "",
type ? type : "",
enc ? enc : "");
@ -481,10 +570,10 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
if (err_msg) {
log_error("SQLite error: %s", err_msg);
log_error("SQLite error in _add_to_db(): %s", err_msg);
sqlite3_free(err_msg);
} else {
log_error("Unknown SQLite error");
log_error("Unknown SQLite error in _add_to_db().");
}
} else {
int inserted_rows_count = sqlite3_changes(g_chatlog_database);
@ -493,3 +582,96 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji
}
}
}
static int
_get_db_version(void)
{
int current_version = -1;
const char* query = "SELECT `version` FROM `DbVersion` LIMIT 1";
sqlite3_stmt* statement;
if (sqlite3_prepare_v2(g_chatlog_database, query, -1, &statement, NULL) == SQLITE_OK) {
if (sqlite3_step(statement) == SQLITE_ROW) {
current_version = sqlite3_column_int(statement, 0);
}
sqlite3_finalize(statement);
}
return current_version;
}
/**
* Migration to version 2 introduces new columns. Returns TRUE on success.
*
* New columns:
* `replaces_db_id` database ID for correcting message of the original message
* `replaced_by_db_id` database ID for original message of the last correcting message
*/
static gboolean
_migrate_to_v2(void)
{
char* err_msg = NULL;
const char* sql_statements[] = {
"BEGIN TRANSACTION",
"ALTER TABLE `ChatLogs` ADD COLUMN `replaces_db_id` INTEGER;",
"ALTER TABLE `ChatLogs` ADD COLUMN `replaced_by_db_id` INTEGER;",
"UPDATE `ChatLogs` AS A "
"SET `replaces_db_id` = B.`id` "
"FROM `ChatLogs` AS B "
"WHERE A.`replace_id` IS NOT NULL AND A.`replace_id` != '' "
"AND A.`replace_id` = B.`stanza_id` "
"AND A.`from_jid` = B.`from_jid` AND A.`to_jid` = B.`to_jid`;",
"UPDATE `ChatLogs` AS A "
"SET `replaced_by_db_id` = B.`id` "
"FROM `ChatLogs` AS B "
"WHERE (A.`replace_id` IS NULL OR A.`replace_id` = '') "
"AND A.`id` = B.`replaces_db_id` "
"AND A.`from_jid` = B.`from_jid`;",
"UPDATE `DbVersion` SET `version` = 2",
"END TRANSACTION"
};
int statements_count = sizeof(sql_statements) / sizeof(sql_statements[0]);
for (int i = 0; i < statements_count; i++) {
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, sql_statements[i], NULL, 0, &err_msg)) {
log_error("SQLite error in _migrate_to_v2() on statement %d: %s", i, err_msg);
if (err_msg) {
sqlite3_free(err_msg);
err_msg = NULL;
}
goto cleanup;
}
}
return TRUE;
cleanup:
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, "ROLLBACK;", NULL, 0, &err_msg)) {
log_error("[DB Migration] Unable to ROLLBACK: %s", err_msg);
if (err_msg) {
sqlite3_free(err_msg);
}
}
return FALSE;
}
// Checks if there is more system storage space available than current database takes + 40% (for indexing and other potential size increases)
static gboolean
_check_available_space_for_db_migration(char* path_to_db)
{
struct stat file_stat;
struct statvfs fs_stat;
if (statvfs(path_to_db, &fs_stat) == 0 && stat(path_to_db, &file_stat) == 0) {
unsigned long long file_size = file_stat.st_size / 1024;
unsigned long long available_space_kb = fs_stat.f_frsize * fs_stat.f_bavail / 1024;
log_debug("_check_available_space_for_db_migration(): Available space on disk: %llu KB; DB size: %llu KB", available_space_kb, file_size);
return (available_space_kb >= (file_size + (file_size * 10 / 4)));
} else {
log_error("Error checking available space.");
return FALSE;
}
}

View File

@ -451,8 +451,8 @@ chatwin_outgoing_msg(ProfChatWin* chatwin, const char* const message, char* id,
win_print_outgoing((ProfWin*)chatwin, enc_char, id, replace_id, message);
}
// save last id and message for LMC
if (id) {
// save last id and message for LMC in case if it's not LMC message
if (id && !replace_id) {
_chatwin_set_last_message(chatwin, id, message);
}
}

View File

@ -1335,19 +1335,27 @@ win_show_status_string(ProfWin* window, const char* const from,
win_appendln(window, presence_colour, "");
}
static void
/** Corrects the visual representation of a message with prior check for sender validity.
*
* Returns TRUE if the message was successfully corrected and should not be printed, FALSE otherwise.
*/
static gboolean
_win_correct(ProfWin* window, const char* const message, const char* const id, const char* const replace_id, const char* const from_jid)
{
if (!replace_id) {
return FALSE;
}
ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, replace_id);
if (!entry) {
log_debug("Replace ID %s could not be found in buffer. Message: %s", replace_id, message);
return;
log_warning("Replace ID %s could not be found in buffer. Message: %s", replace_id, message);
return FALSE;
}
if (g_strcmp0(entry->from_jid, from_jid) != 0) {
log_debug("Illicit LMC attempt from %s for message from %s with: %s", from_jid, entry->from_jid, message);
cons_show("Illicit LMC attempt from %s for message from %s", from_jid, entry->from_jid);
return;
return TRUE;
}
/*TODO: set date?
@ -1369,12 +1377,10 @@ _win_correct(ProfWin* window, const char* const message, const char* const id, c
}
entry->message = strdup(message);
if (entry->id) {
free(entry->id);
}
entry->id = strdup(id);
// LMC requires original message ID, hence ID remains the same
win_redraw(window);
return TRUE;
}
void
@ -1406,9 +1412,7 @@ win_print_incoming(ProfWin* window, const char* const display_name_from, ProfMes
enc_char = strdup("-");
}
if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) {
_win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->barejid);
} else {
if (!prefs_get_boolean(PREF_CORRECTION_ALLOW) || !_win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->barejid)) {
// Prevent duplicate messages when current client is sending a message or if it's mam
if (g_strcmp0(message->from_jid->fulljid, connection_get_fulljid()) != 0 && !message->is_mam) {
_win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, display_name_from, message->from_jid->barejid, message->id, "%s", message->plain);
@ -1434,9 +1438,7 @@ win_print_them(ProfWin* window, theme_item_t theme_item, const char* const show_
void
win_println_incoming_muc_msg(ProfWin* window, char* show_char, int flags, const ProfMessage* const message)
{
if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && message->replace_id) {
_win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->fulljid);
} else {
if (!prefs_get_boolean(PREF_CORRECTION_ALLOW) || !_win_correct(window, message->plain, message->id, message->replace_id, message->from_jid->fulljid)) {
_win_printf(window, show_char, 0, message->timestamp, flags | NO_ME, THEME_TEXT_THEM, message->from_jid->resourcepart, message->from_jid->fulljid, message->id, "%s", message->plain);
}
@ -1448,9 +1450,7 @@ win_print_outgoing_muc_msg(ProfWin* window, char* show_char, const char* const m
{
GDateTime* timestamp = g_date_time_new_now_local();
if (prefs_get_boolean(PREF_CORRECTION_ALLOW) && replace_id) {
_win_correct(window, message, id, replace_id, me);
} else {
if (!prefs_get_boolean(PREF_CORRECTION_ALLOW) || !_win_correct(window, message, id, replace_id, me)) {
_win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, me, me, id, "%s", message);
}
@ -1464,9 +1464,7 @@ win_print_outgoing(ProfWin* window, const char* show_char, const char* const id,
GDateTime* timestamp = g_date_time_new_now_local();
const char* myjid = connection_get_fulljid();
if (replace_id) {
_win_correct(window, message, id, replace_id, myjid);
} else {
if (!_win_correct(window, message, id, replace_id, myjid)) {
auto_gchar gchar* outgoing_str = prefs_get_string(PREF_OUTGOING_STAMP);
_win_printf(window, show_char, 0, timestamp, 0, THEME_TEXT_ME, outgoing_str, myjid, id, "%s", message);
}
@ -1625,8 +1623,7 @@ win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const ch
receipt->received = FALSE;
const char* myjid = connection_get_fulljid();
if (replace_id) {
_win_correct(window, message, id, replace_id, myjid);
if (_win_correct(window, message, id, replace_id, myjid)) {
free(receipt); // TODO: probably we should use this in _win_correct()
} else {
buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, myjid, message, receipt, id);