mirror of
https://git.jabber.space/devs/cproof.git
synced 2026-07-18 00:36:21 +00:00
fix(verify): per-contact context in output, demote duplicate stanza-id to debug
This commit is contained in:
@@ -211,9 +211,13 @@ _first_pass(FILE* fp, const char* basename,
|
|||||||
|
|
||||||
if (pl->stanza_id && pl->stanza_id[0] != '\0') {
|
if (pl->stanza_id && pl->stanza_id[0] != '\0') {
|
||||||
if (g_hash_table_contains(seen_stanza_ids, pl->stanza_id)) {
|
if (g_hash_table_contains(seen_stanza_ids, pl->stanza_id)) {
|
||||||
*issues = g_slist_prepend(*issues,
|
// Older clients (Pidgin, Adium, even early Profanity)
|
||||||
_issue_new(INTEGRITY_WARNING, basename, lineno,
|
// sometimes reuse client-generated stanza-ids across
|
||||||
g_strdup_printf("Duplicate stanza-id \"%s\"", pl->stanza_id)));
|
// distinct messages, so a collision is not a real
|
||||||
|
// integrity defect — log it for diagnostics but don't
|
||||||
|
// surface it through /history verify.
|
||||||
|
log_debug("flatfile verify: duplicate stanza-id \"%s\" at %s:%d",
|
||||||
|
pl->stanza_id, basename, lineno);
|
||||||
} else {
|
} else {
|
||||||
g_hash_table_insert(seen_stanza_ids, g_strdup(pl->stanza_id), GINT_TO_POINTER(lineno));
|
g_hash_table_insert(seen_stanza_ids, g_strdup(pl->stanza_id), GINT_TO_POINTER(lineno));
|
||||||
}
|
}
|
||||||
@@ -302,7 +306,13 @@ _verify_contact_dir(const char* cdir_path, GSList** issues)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* basename = "history.log";
|
// Reconstruct the contact JID from the directory name so issue reports
|
||||||
|
// identify which contact the problem belongs to (every contact has its
|
||||||
|
// own history.log, so the bare filename is ambiguous).
|
||||||
|
auto_gchar gchar* dir_name = g_path_get_basename(cdir_path);
|
||||||
|
auto_gcharv gchar** parts = g_strsplit(dir_name, "_at_", -1);
|
||||||
|
auto_gchar gchar* contact_jid = g_strjoinv("@", parts);
|
||||||
|
auto_gchar gchar* basename = g_strdup_printf("%s/history.log", contact_jid);
|
||||||
|
|
||||||
_check_permissions(filepath, basename, issues);
|
_check_permissions(filepath, basename, issues);
|
||||||
|
|
||||||
|
|||||||
@@ -466,23 +466,36 @@ _sqlite_verify_integrity(const gchar* const contact_barejid)
|
|||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check timestamp ordering for a specific contact or all
|
// Check timestamp ordering, restricted to consecutive rows belonging
|
||||||
|
// to the same conversation pair so adjacency does not span unrelated
|
||||||
|
// contacts. Adjacent rows by id from different conversations are
|
||||||
|
// legitimately out-of-order and should not be reported.
|
||||||
const Jid* myjid = connection_get_jid();
|
const Jid* myjid = connection_get_jid();
|
||||||
if (myjid && myjid->barejid) {
|
if (myjid && myjid->barejid) {
|
||||||
auto_sqlite char* query = NULL;
|
auto_sqlite char* query = NULL;
|
||||||
if (contact_barejid) {
|
if (contact_barejid) {
|
||||||
query = sqlite3_mprintf(
|
query = sqlite3_mprintf(
|
||||||
"SELECT A.`id`, A.`timestamp`, B.`id`, B.`timestamp` FROM `ChatLogs` A "
|
"SELECT A.`id`, A.`timestamp`, A.`from_jid`, A.`to_jid`, B.`id`, B.`timestamp` "
|
||||||
"JOIN `ChatLogs` B ON B.`id` = A.`id` + 1 "
|
"FROM `ChatLogs` A "
|
||||||
|
"JOIN `ChatLogs` B ON B.`from_jid` = A.`from_jid` AND B.`to_jid` = A.`to_jid` "
|
||||||
|
" AND B.`id` > A.`id` "
|
||||||
"WHERE A.`timestamp` > B.`timestamp` "
|
"WHERE A.`timestamp` > B.`timestamp` "
|
||||||
"AND ((A.`from_jid` = %Q AND A.`to_jid` = %Q) OR (A.`from_jid` = %Q AND A.`to_jid` = %Q)) "
|
"AND ((A.`from_jid` = %Q AND A.`to_jid` = %Q) OR (A.`from_jid` = %Q AND A.`to_jid` = %Q)) "
|
||||||
|
"AND NOT EXISTS (SELECT 1 FROM `ChatLogs` C "
|
||||||
|
" WHERE C.`from_jid` = A.`from_jid` AND C.`to_jid` = A.`to_jid` "
|
||||||
|
" AND C.`id` > A.`id` AND C.`id` < B.`id`) "
|
||||||
"LIMIT 50",
|
"LIMIT 50",
|
||||||
contact_barejid, myjid->barejid, myjid->barejid, contact_barejid);
|
contact_barejid, myjid->barejid, myjid->barejid, contact_barejid);
|
||||||
} else {
|
} else {
|
||||||
query = sqlite3_mprintf(
|
query = sqlite3_mprintf(
|
||||||
"SELECT A.`id`, A.`timestamp`, B.`id`, B.`timestamp` FROM `ChatLogs` A "
|
"SELECT A.`id`, A.`timestamp`, A.`from_jid`, A.`to_jid`, B.`id`, B.`timestamp` "
|
||||||
"JOIN `ChatLogs` B ON B.`id` = A.`id` + 1 "
|
"FROM `ChatLogs` A "
|
||||||
|
"JOIN `ChatLogs` B ON B.`from_jid` = A.`from_jid` AND B.`to_jid` = A.`to_jid` "
|
||||||
|
" AND B.`id` > A.`id` "
|
||||||
"WHERE A.`timestamp` > B.`timestamp` "
|
"WHERE A.`timestamp` > B.`timestamp` "
|
||||||
|
"AND NOT EXISTS (SELECT 1 FROM `ChatLogs` C "
|
||||||
|
" WHERE C.`from_jid` = A.`from_jid` AND C.`to_jid` = A.`to_jid` "
|
||||||
|
" AND C.`id` > A.`id` AND C.`id` < B.`id`) "
|
||||||
"LIMIT 50");
|
"LIMIT 50");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,15 +503,19 @@ _sqlite_verify_integrity(const gchar* const contact_barejid)
|
|||||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
int id_a = sqlite3_column_int(stmt, 0);
|
int id_a = sqlite3_column_int(stmt, 0);
|
||||||
const char* ts_a = (const char*)sqlite3_column_text(stmt, 1);
|
const char* ts_a = (const char*)sqlite3_column_text(stmt, 1);
|
||||||
int id_b = sqlite3_column_int(stmt, 2);
|
const char* from_a = (const char*)sqlite3_column_text(stmt, 2);
|
||||||
const char* ts_b = (const char*)sqlite3_column_text(stmt, 3);
|
const char* to_a = (const char*)sqlite3_column_text(stmt, 3);
|
||||||
|
int id_b = sqlite3_column_int(stmt, 4);
|
||||||
|
const char* ts_b = (const char*)sqlite3_column_text(stmt, 5);
|
||||||
|
|
||||||
integrity_issue_t* issue = g_malloc0(sizeof(integrity_issue_t));
|
integrity_issue_t* issue = g_malloc0(sizeof(integrity_issue_t));
|
||||||
issue->level = INTEGRITY_WARNING;
|
issue->level = INTEGRITY_WARNING;
|
||||||
issue->file = g_strdup("chatlog.db");
|
issue->file = g_strdup_printf("%s↔%s/chatlog.db",
|
||||||
|
from_a ? from_a : "?", to_a ? to_a : "?");
|
||||||
issue->line = id_a;
|
issue->line = id_a;
|
||||||
issue->message = g_strdup_printf("Timestamp out of order: row %d (%s) > row %d (%s)",
|
issue->message = g_strdup_printf("Timestamp out of order: row %d (%s) > row %d (%s)",
|
||||||
id_a, ts_a ? ts_a : "NULL", id_b, ts_b ? ts_b : "NULL");
|
id_a, ts_a ? ts_a : "NULL",
|
||||||
|
id_b, ts_b ? ts_b : "NULL");
|
||||||
issues = g_slist_append(issues, issue);
|
issues = g_slist_append(issues, issue);
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
@@ -506,7 +523,7 @@ _sqlite_verify_integrity(const gchar* const contact_barejid)
|
|||||||
|
|
||||||
// Check broken LMC references
|
// Check broken LMC references
|
||||||
auto_sqlite char* lmc_query = sqlite3_mprintf(
|
auto_sqlite char* lmc_query = sqlite3_mprintf(
|
||||||
"SELECT A.`id`, A.`replaces_db_id` FROM `ChatLogs` A "
|
"SELECT A.`id`, A.`replaces_db_id`, A.`from_jid`, A.`to_jid` FROM `ChatLogs` A "
|
||||||
"WHERE A.`replaces_db_id` IS NOT NULL "
|
"WHERE A.`replaces_db_id` IS NOT NULL "
|
||||||
"AND NOT EXISTS (SELECT 1 FROM `ChatLogs` B WHERE B.`id` = A.`replaces_db_id`) "
|
"AND NOT EXISTS (SELECT 1 FROM `ChatLogs` B WHERE B.`id` = A.`replaces_db_id`) "
|
||||||
"LIMIT 50");
|
"LIMIT 50");
|
||||||
@@ -515,10 +532,13 @@ _sqlite_verify_integrity(const gchar* const contact_barejid)
|
|||||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
int id = sqlite3_column_int(stmt, 0);
|
int id = sqlite3_column_int(stmt, 0);
|
||||||
int replaces_id = sqlite3_column_int(stmt, 1);
|
int replaces_id = sqlite3_column_int(stmt, 1);
|
||||||
|
const char* from = (const char*)sqlite3_column_text(stmt, 2);
|
||||||
|
const char* to = (const char*)sqlite3_column_text(stmt, 3);
|
||||||
|
|
||||||
integrity_issue_t* issue = g_malloc0(sizeof(integrity_issue_t));
|
integrity_issue_t* issue = g_malloc0(sizeof(integrity_issue_t));
|
||||||
issue->level = INTEGRITY_ERROR;
|
issue->level = INTEGRITY_ERROR;
|
||||||
issue->file = g_strdup("chatlog.db");
|
issue->file = g_strdup_printf("%s↔%s/chatlog.db",
|
||||||
|
from ? from : "?", to ? to : "?");
|
||||||
issue->line = id;
|
issue->line = id;
|
||||||
issue->message = g_strdup_printf("Broken LMC reference: row %d references non-existent row %d",
|
issue->message = g_strdup_printf("Broken LMC reference: row %d references non-existent row %d",
|
||||||
id, replaces_id);
|
id, replaces_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user