From 2d179ec41d438702d6788d7590ffaa9ef025e1d0 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 14 Jul 2026 17:46:33 +0300 Subject: [PATCH] fix chatgui: message format, member notify, chain_hash sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - on_db_sync_insert: parse JSON {"n":...,"ct":...,"d":...}, store d→data, n→node_id, ct→content_type with proper chain_hash (fixes JSON displayed instead of message text) - chat_core_cursor_open: SELECT chain_hash column - chat_core_cursor_next: include chain_hash(32) in output record - cs_handle_send_data: parse chain_hash in received records (fixes chain_hash mismatch → all sync records were dropped) - cs_handle_channel_join: post GUI_EVT_MEMBERS_CHANGED (fixes creator not seeing new member after join) - cs_handle_welcome: trigger INIT_SYNC with inviter after join (fixes no message sync after invite join) --- tools/chatgui/transport/chat_core.c | 42 ++++++++++++++++++++++------- tools/chatgui/transport/chat_sync.c | 26 +++++++++++++++++- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/tools/chatgui/transport/chat_core.c b/tools/chatgui/transport/chat_core.c index 95f867cd..344a0791 100644 --- a/tools/chatgui/transport/chat_core.c +++ b/tools/chatgui/transport/chat_core.c @@ -453,7 +453,7 @@ uint32_t chat_core_cursor_open(const char* ch_id) { if (g_cc.cursors[i]) continue; char tbl[80]; msg_table_name(ch_id, tbl, sizeof(tbl)); char sql[256]; snprintf(sql, sizeof(sql), - "SELECT timestamp, datahash, node_id, content_type, data" + "SELECT timestamp, datahash, node_id, content_type, data, chain_hash" " FROM \"%s\" ORDER BY timestamp, datahash ASC", tbl); sqlite3_stmt* stmt = NULL; if (sqlite3_prepare_v2(g_cc.db, sql, -1, &stmt, NULL) != SQLITE_OK) return 0; @@ -479,10 +479,12 @@ int chat_core_cursor_next(uint32_t cursor_id, uint8_t* buf, size_t buf_size, uint64_t nid= (uint64_t)sqlite3_column_int64(stmt, 2); const char* ct = (const char*)sqlite3_column_text(stmt, 3); int ct_len = sqlite3_column_bytes(stmt, 3); - const uint8_t* data = (const uint8_t*)sqlite3_column_blob(stmt, 4); + const uint8_t* dptr = (const uint8_t*)sqlite3_column_blob(stmt, 4); int dlen = sqlite3_column_bytes(stmt, 4); + const uint8_t* ch_ptr = (const uint8_t*)sqlite3_column_blob(stmt, 5); + int ch_len = sqlite3_column_bytes(stmt, 5); - size_t need = 8 + 8 + 8 + 1 + (size_t)ct_len + 4 + (size_t)dlen; + size_t need = 8 + 8 + 8 + 1 + (size_t)ct_len + 4 + (size_t)dlen + 32; if (need > buf_size) return -1; uint8_t* out = buf; @@ -493,7 +495,9 @@ int chat_core_cursor_next(uint32_t cursor_id, uint8_t* buf, size_t buf_size, if (ct_len) { memcpy(out, ct, (size_t)ct_len); out += ct_len; } uint32_t dl32 = (uint32_t)dlen; memcpy(out, &dl32, 4); out += 4; - if (dlen) { memcpy(out, data, (size_t)dlen); out += dlen; } + if (dlen) { memcpy(out, dptr, (size_t)dlen); out += dlen; } + if (ch_ptr && ch_len >= 32) memcpy(out, ch_ptr, 32); else memset(out, 0, 32); + out += 32; *out_len = (size_t)(out - buf); return 0; } @@ -994,22 +998,40 @@ static void si_register(struct DB_SYNC_INSTANCE* si, const char* ch_id) { static void on_db_sync_insert(struct DB_SYNC_INSTANCE* si, const char* data, size_t len, uint64_t author, void* arg) { const char* ch_id = (const char*)arg; - uint64_t dh; compute_datahash((const uint8_t*)data, len, &dh); + /* parse JSON: {"n":,"ch":"","ct":"","d":""} */ + const char* p = data; const char* end = data + len; uint64_t jn = 0; + { /* "n" */ + const char* n = strstr(p, "\"n\":"); if (!n) return; + n += 4; jn = strtoull(n, NULL, 0); p = n; + } + const char* jct = "text/plain"; size_t jct_len = 10; + { /* "ct" */ + const char* ct = strstr(p, "\"ct\":\""); if (ct) { ct += 6; const char* ce = strchr(ct, '"'); if (ce) { jct = ct; jct_len = (size_t)(ce - ct); p = ce; } } + } + const char* jd_start = NULL; size_t jd_len = 0; + { /* "d" */ + const char* d = strstr(p, "\"d\":\""); if (d) { d += 5; const char* de = d; while (de < end) { if (*de == '"' && (de == d || *(de - 1) != '\\')) break; de++; } if (de < end) { jd_start = d; jd_len = (size_t)(de - d); } } + } + if (!jd_start) return; + + uint64_t dh; compute_datahash((const uint8_t*)jd_start, jd_len, &dh); uint64_t ts = get_time_us(); char tbl[80]; msg_table_name(ch_id, tbl, sizeof(tbl)); + uint8_t prev_ch[32]; get_last_chain_hash(ch_id, prev_ch); + uint8_t chain_h[32]; compute_chain_hash(prev_ch, (int64_t)ts, dh, chain_h); char sql[512]; snprintf(sql, sizeof(sql), "INSERT OR IGNORE INTO \"%s\" (node_id,content_type,data,timestamp,datahash,chain_hash,signature,is_outgoing,is_read,sync_flags)" " VALUES(?,?,?,?,?,?,?,?,?,?)", tbl); sqlite3_stmt* st=NULL; if (sqlite3_prepare_v2(g_cc.db,sql,-1,&st,NULL)!=SQLITE_OK) return; - sqlite3_bind_int64(st,1,(sqlite3_int64)author); - sqlite3_bind_text(st,2,"text/plain",-1,SQLITE_STATIC); - sqlite3_bind_blob(st,3,data,(int)len,SQLITE_STATIC); + sqlite3_bind_int64(st,1,(sqlite3_int64)jn); + sqlite3_bind_text(st,2,jct,(int)jct_len,SQLITE_STATIC); + sqlite3_bind_blob(st,3,jd_start,(int)jd_len,SQLITE_STATIC); sqlite3_bind_int64(st,4,(sqlite3_int64)ts); sqlite3_bind_int64(st,5,(sqlite3_int64)dh); - { static const uint8_t z32[32]={0}; sqlite3_bind_blob(st,6,z32,32,SQLITE_STATIC); } + sqlite3_bind_blob(st,6,chain_h,32,SQLITE_STATIC); { static const uint8_t z64[64]={0}; sqlite3_bind_blob(st,7,z64,64,SQLITE_STATIC); } - sqlite3_bind_int(st,8,author==g_cc.my_node_id?1:0); + sqlite3_bind_int(st,8,(uint64_t)jn==g_cc.my_node_id?1:0); sqlite3_bind_int(st,9,1); sqlite3_bind_int(st,10,0); int rc=sqlite3_step(st); sqlite3_finalize(st); if (rc==SQLITE_DONE) { diff --git a/tools/chatgui/transport/chat_sync.c b/tools/chatgui/transport/chat_sync.c index c9ea3711..1fdd7ba0 100644 --- a/tools/chatgui/transport/chat_sync.c +++ b/tools/chatgui/transport/chat_sync.c @@ -213,8 +213,9 @@ static void cs_handle_send_data(struct chat_sync* cs, uint64_t peer, ptr += ct_len; remain -= ct_len; if (remain < 4) break; uint32_t dlen; memcpy(&dlen, ptr, 4); ptr += 4; remain -= 4; - if (remain < dlen) break; + if (remain < dlen + 32) break; ptr += dlen; remain -= dlen; + ptr += 32; remain -= 32; /* chain_hash */ size_t reclen = (size_t)(ptr - rec_start); chat_core_insert_record(ch_id, rec_start, reclen); @@ -949,6 +950,9 @@ static void cs_handle_channel_join(struct chat_sync* cs, uint64_t peer, ch = cs_find(cs, ch_id); } + { uint8_t evt[65]; uint8_t cl=(uint8_t)strlen(ch_id); evt[0]=cl; memcpy(evt+1,ch_id,cl); + gui_bridge_post(GUI_EVT_MEMBERS_CHANGED, evt, 1+cl); } + DEBUG_INFO(DEBUG_CATEGORY_CONNECTIVITY, "%s: JOIN accepted node=0x%016llx ch=%s addrs=%d", CS_ID, (unsigned long long)node_id, ch_id, addr_cnt); } @@ -1000,6 +1004,26 @@ static void cs_handle_welcome(struct chat_sync* cs, uint64_t peer, cs_refresh_channels(cs); + /* initiate message sync with the inviter after join */ + for (int i = 0; i < cs->channel_count; i++) { + struct channel_cache* ch2 = &cs->channels[i]; + if (strcmp(ch2->channel_id, ch_id) != 0) continue; + int found = 0; + for (int j = 0; j < ch2->peer_count; j++) + if (ch2->peer_ids[j] == peer) { found = 1; break; } + if (found) { + ch2->synced = CS_SYNC_IN_PROGRESS; + uint8_t msg[37]; msg[0] = CS_MSG_INIT_SYNC; + chat_core_chain_hash_at(ch2->channel_id, + ch2->msg_count > 0 ? ch2->msg_count - 1 : 0, ch2->last_chain_hash); + memcpy(msg + 1, &ch2->msg_count, 4); + memcpy(msg + 5, ch2->last_chain_hash, 32); + cs_send(cs, ch2->channel_id, peer, msg, 37); + member_sync_start(cs->inst, peer, ch2->channel_id, _on_member_sync_done, ch2); + } + break; + } + uint8_t evt[65]; uint8_t ch_id_len = (uint8_t)strlen(ch_id); evt[0] = ch_id_len; memcpy(evt + 1, ch_id, ch_id_len); gui_bridge_post(GUI_EVT_CHANNEL_UPDATED, evt, 1 + ch_id_len);