fix(export): hash stanza_id with content instead of trusting it as sole dedup key
All checks were successful
CI Code / Check spelling (pull_request) Successful in 26s
CI Code / Check coding style (pull_request) Successful in 1m6s
CI Code / Code Coverage (pull_request) Successful in 2m44s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m50s
CI Code / Linux (arch) (pull_request) Successful in 5m25s
CI Code / Linux (debian) (pull_request) Successful in 6m50s

This commit is contained in:
2026-05-05 16:06:54 +03:00
parent 2990ec795a
commit 26db671a38

View File

@@ -90,18 +90,21 @@ _close_temp_sqlite(int opened)
// =========================================================================
// Returns a g_strdup'd key — callers free with g_free (gchar* convention).
//
// stanza_id is mixed into the hash but is NOT used as the sole key, since
// some clients (older Pidgin, Adium) reuse incremental ids per session and
// servers echo them back unchanged. Trusting stanza_id alone silently drops
// legitimate distinct messages on collision. By hashing id + timestamp +
// from_jid + body together we preserve id as a disambiguator without making
// it a single point of failure: true exact duplicates still dedup, but two
// messages that share an id with different content (or different timestamps)
// stay distinct.
static gchar*
_make_dedup_key(const char* stanza_id, const char* timestamp, const char* from_jid, const char* body)
{
if (stanza_id && stanza_id[0] != '\0')
return g_strdup(stanza_id);
// Fallback for messages without stanza-id: SHA-256 over the FULL body,
// not the first 256 bytes. The previous prefix-only hash collided on
// legitimate near-identical templates (signatures, code blocks, paste-
// bombs) — second occurrence was silently dropped on import. Hashing
// whole body is microseconds of extra work per row.
GChecksum* sum = g_checksum_new(G_CHECKSUM_SHA256);
g_checksum_update(sum, (const guchar*)(stanza_id ? stanza_id : ""), -1);
g_checksum_update(sum, (const guchar*)"|", 1);
g_checksum_update(sum, (const guchar*)(timestamp ? timestamp : ""), -1);
g_checksum_update(sum, (const guchar*)"|", 1);
g_checksum_update(sum, (const guchar*)(from_jid ? from_jid : ""), -1);