Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -896,3 +896,90 @@ _migrate_to_v3(void)

return FALSE;
}

gboolean
log_database_update_archive_id(const prof_msg_type_t type, const char* const room_or_contact_jid, const char* const stanza_id, const char* const archive_id)
{
if (!g_chatlog_database || !stanza_id || !archive_id || !room_or_contact_jid) {
return FALSE;
}

const char* our_barejid = connection_get_jid()->barejid;
sqlite3_stmt* stmt = NULL;
gboolean success = FALSE;
const char* type_str = _get_message_type_str(type);

if (!type_str) {
return FALSE;
}

auto_sqlite char* query = sqlite3_mprintf(
"UPDATE `ChatLogs` "
"SET `archive_id` = %Q "
"WHERE `stanza_id` = %Q "
" AND `from_jid` = %Q "
" AND `to_jid` = %Q "
" AND `type` = %Q "
" AND `archive_id` IS NULL",
archive_id, stanza_id, our_barejid, room_or_contact_jid, type_str);

if (query) {
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL)) {
if (sqlite3_step(stmt) == SQLITE_DONE) {
success = TRUE;
}
sqlite3_finalize(stmt);
}
}

return success;
}

char*
log_database_get_decrypted_message(const prof_msg_type_t type, const char* const from_jid, const char* const to_jid, const char* const stanza_id, const char* const archive_id)
{
if (!g_chatlog_database || !from_jid || !to_jid) {
return NULL;
}

sqlite3_stmt* stmt = NULL;
char* decrypted_text = NULL;
auto_sqlite char* query = NULL;
const char* type_str = _get_message_type_str(type);

if (!type_str) {
return NULL;
}

if (stanza_id && strlen(stanza_id) > 0) {
query = sqlite3_mprintf(
"SELECT `message` FROM `ChatLogs` "
"WHERE `stanza_id` = %Q "
" AND `type` = %Q "
" AND ((`from_jid` = %Q AND `to_jid` = %Q) OR (`from_jid` = %Q AND `to_jid` = %Q)) "
"LIMIT 1",
stanza_id, type_str, from_jid, to_jid, to_jid, from_jid);
} else if (archive_id && strlen(archive_id) > 0) {
query = sqlite3_mprintf(
"SELECT `message` FROM `ChatLogs` "
"WHERE `archive_id` = %Q "
" AND `type` = %Q "
" AND ((`from_jid` = %Q AND `to_jid` = %Q) OR (`from_jid` = %Q AND `to_jid` = %Q)) "
"LIMIT 1",
archive_id, type_str, from_jid, to_jid, to_jid, from_jid);
}

if (query) {
if (SQLITE_OK == sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL)) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char* text = (const char*)sqlite3_column_text(stmt, 0);
if (text) {
decrypted_text = strdup(text);
}
}
sqlite3_finalize(stmt);
}
}

return decrypted_text;
}
2 changes: 2 additions & 0 deletions src/database.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ int log_database_get_chat_count(const gchar* const contact_barejid, const char*
int log_database_get_muc_count(const gchar* const room_jid, const char* start_time, const char* end_time);
ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last);
ProfMessage* log_database_get_limits_info_muc(const gchar* const room_jid, gboolean is_last);
gboolean log_database_update_archive_id(const prof_msg_type_t type, const char* const room_or_contact_jid, const char* const stanza_id, const char* const archive_id);
char* log_database_get_decrypted_message(const prof_msg_type_t type, const char* const from_jid, const char* const to_jid, const char* const stanza_id, const char* const archive_id);
void log_database_close(void);

#endif // DATABASE_H
20 changes: 18 additions & 2 deletions src/event/server_events.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,14 @@ sv_ev_room_message(ProfMessage* message)
gboolean is_duplicate = FALSE;

// only log message not coming from this client (but maybe same account, different client)
// our messages are logged when outgoing
if (!(g_strcmp0(mynick, message->from_jid->resourcepart) == 0 && message_is_sent_by_us(message, TRUE))) {
// our messages are logged when outgoing, and we update them when reflected
gboolean is_self_reflection = (g_strcmp0(mynick, message->from_jid->resourcepart) == 0 && message_is_sent_by_us(message, TRUE));

if (is_self_reflection) {
if (message->id && message->stanzaid) {
log_database_update_archive_id(PROF_MSG_TYPE_MUC, message->from_jid->barejid, message->id, message->stanzaid);
}
} else {
if (!_log_muc(message)) {
is_duplicate = TRUE;
}
Expand Down Expand Up @@ -651,6 +657,16 @@ _sv_ev_incoming_plain(ProfChatWin* chatwin, gboolean new_win, ProfMessage* messa
void
sv_ev_incoming_message(ProfMessage* message)
{
gboolean is_self_reflection = (message->is_mam && equals_our_barejid(message->from_jid->barejid));

if (is_self_reflection) {
if (message->id && message->stanzaid && message->to_jid) {
log_database_update_archive_id(PROF_MSG_TYPE_CHAT, message->to_jid->barejid, message->id, message->stanzaid);
}
rosterwin_roster();
return;
}

gboolean new_win = FALSE;
ProfChatWin* chatwin;
char* looking_for_jid = message->from_jid->barejid;
Expand Down
48 changes: 42 additions & 6 deletions src/xmpp/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "xmpp/xmpp.h"
#include "xmpp/form.h"
#include "xmpp/iq.h"
#include "database.h"

#ifdef HAVE_OMEMO
#include "xmpp/omemo.h"
Expand Down Expand Up @@ -1136,9 +1137,27 @@ _handle_groupchat(xmpp_stanza_t* const stanza, gboolean is_mam, const char* resu
#ifdef HAVE_OMEMO
message->plain = omemo_receive_message(stanza, &message->trusted, &message->omemo_err);
if (message->omemo_err != OMEMO_ERR_NONE) {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
message->plain = omemo_error_to_string(message->omemo_err);
// Get previously decrypted plaintext from database
// Historical replays (MAM and standard delayed MUC history) fail decryption due to OMEMO key deletion.
// Realtime messages skip this.
char* db_decrypted = log_database_get_decrypted_message(
PROF_MSG_TYPE_MUC,
from_jid->barejid,
connection_get_jid()->barejid,
message->id,
message->stanzaid);
if (db_decrypted) {
if (message->plain) {
free(message->plain);
}
message->plain = db_decrypted;
message->omemo_err = OMEMO_ERR_NONE;
message->enc = PROF_MSG_ENC_OMEMO;
} else {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
message->plain = omemo_error_to_string(message->omemo_err);
}
}
} else if (message->plain != NULL) {
message->enc = PROF_MSG_ENC_OMEMO;
Expand Down Expand Up @@ -1487,9 +1506,26 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c
// check omemo encryption
message->plain = omemo_receive_message(stanza, &message->trusted, &message->omemo_err);
if (message->omemo_err != OMEMO_ERR_NONE) {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
message->plain = omemo_error_to_string(message->omemo_err);
// Get previously decrypted plaintext from database
// Historical replays (MAM) fail decryption due to OMEMO key deletion/self-reflections.
char* db_decrypted = log_database_get_decrypted_message(
PROF_MSG_TYPE_CHAT,
message->from_jid->barejid,
message->to_jid ? message->to_jid->barejid : connection_get_jid()->barejid,
message->id,
message->stanzaid);
if (db_decrypted) {
if (message->plain) {
free(message->plain);
}
message->plain = db_decrypted;
message->omemo_err = OMEMO_ERR_NONE;
message->enc = PROF_MSG_ENC_OMEMO;
} else {
message->enc = PROF_MSG_ENC_OMEMO;
if (message->plain == NULL) {
message->plain = omemo_error_to_string(message->omemo_err);
}
}
} else if (message->plain != NULL) {
message->enc = PROF_MSG_ENC_OMEMO;
Expand Down
12 changes: 12 additions & 0 deletions tests/unittests/database/stub_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,15 @@ void
log_database_close(void)
{
}

gboolean
log_database_update_archive_id(const prof_msg_type_t type, const char* const room_or_contact_jid, const char* const stanza_id, const char* const archive_id)
{
return FALSE;
}

char*
log_database_get_decrypted_message(const prof_msg_type_t type, const char* const from_jid, const char* const to_jid, const char* const stanza_id, const char* const archive_id)
{
return NULL;
}
Loading