/* * chat_profile.c — профиль узла: имя, адреса, ui_state * * Вынесено из chat_core.c для уменьшения размера модуля. */ #include "chat_core_priv.h" #include "gui_bridge.h" #include "../../../lib/json_flat.h" #include "topo_node_sqlite.h" #include "member_sync.h" #include "../../../src/utun_instance.h" #include "etcp.h" #include "secure_channel.h" #include "../../../src/ntp_time.h" #include "../../../lib/u_async.h" #include "../../../lib/mem.h" #include "../../../lib/platform_compat.h" #include void chat_core_set_my_node_id(uint64_t node_id) { g_cc.my_node_id = node_id; } void chat_core_update_my_name(const char* name) { if (!g_cc.initialized || !name) return; topo_node_sqlite_node_update_verified(g_cc.db, g_cc.my_node_id, name, g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, (uint64_t)ntp_time_get_seconds(g_cc.inst), 0); snprintf(g_cc.inst->name, sizeof(g_cc.inst->name), "%s", name); uint64_t myid = g_cc.my_node_id; sqlite3_stmt* cs = NULL; sqlite3_prepare_v2(g_cc.db, "SELECT channel_id, x25519_pubkey, ed25519_pubkey FROM channels", -1, &cs, NULL); if (cs) { while (sqlite3_step(cs) == SQLITE_ROW) { const char* ch = (const char*)sqlite3_column_text(cs, 0); const uint8_t* ch_x25519 = sqlite3_column_blob(cs, 1); const uint8_t* ch_ed25519 = sqlite3_column_blob(cs, 2); if (!ch || !ch_x25519 || !ch_ed25519) continue; char tbl[80]; peers_table_name(ch, tbl, sizeof(tbl)); char buf[256]; snprintf(buf, sizeof(buf), "SELECT 1 FROM \"%s\" WHERE node_id=?", tbl); sqlite3_stmt* ps = NULL; if (sqlite3_prepare_v2(g_cc.db, buf, -1, &ps, NULL) == SQLITE_OK) { sqlite3_bind_int64(ps, 1, (sqlite3_int64)myid); if (sqlite3_step(ps) == SQLITE_ROW) { uint8_t join_msg[256]; size_t mlen = 0; memcpy(join_msg + mlen, ch_x25519, 32); mlen += 32; memcpy(join_msg + mlen, ch_ed25519, 32); mlen += 32; memcpy(join_msg + mlen, &myid, 8); mlen += 8; memcpy(join_msg + mlen, g_cc.inst->my_keys.public_key, 32); mlen += 32; uint64_t join_ts = (uint64_t)ntp_time_get_seconds(g_cc.inst); memcpy(join_msg + mlen, &join_ts, 8); mlen += 8; uint8_t new_sig[64]; memset(new_sig, 0, 64); EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, g_cc.inst->my_ed25519_privkey, 32); if (pkey) { EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); if (mdctx) { if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) == 1) EVP_DigestSign(mdctx, new_sig, &(size_t){64}, join_msg, mlen); EVP_MD_CTX_free(mdctx); } EVP_PKEY_free(pkey); } char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", name); int r1 = topo_node_sqlite_member_put(g_cc.db, ch, myid, new_sig, join_ts, NULL, 0, g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, juser, NULL); if (r1 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r1); int r2 = member_sync_put(g_cc.inst, ch, myid, g_cc.inst->my_keys.public_key, g_cc.inst->my_ed25519_pubkey, new_sig, join_ts, NULL, 0, juser, NULL, 0); if (r2 != 0) DEBUG_ERROR(DEBUG_CATEGORY_CONNECTIVITY, "%s: member_sync_put(my_name) FAILED ch=%s rc=%d", CC_ID, ch, r2); uint8_t evt[65]; uint8_t cl = (uint8_t)strlen(ch); evt[0] = cl; memcpy(evt + 1, ch, cl); gui_bridge_post(GUI_EVT_MEMBERS_CHANGED, evt, 1 + cl); } sqlite3_finalize(ps); } } sqlite3_finalize(cs); } } void chat_core_update_my_name_trampoline(void* arg) { chat_core_update_my_name((const char*)arg); u_free(arg); } void chat_core_sync_my_addresses(void) { if (!g_cc.initialized || !g_cc.db || !g_cc.inst) return; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] sync_my_addresses START my_node=0x%016llx", CC_ID, (unsigned long long)g_cc.my_node_id); sqlite3_stmt* del = NULL; sqlite3_prepare_v2(g_cc.db, "DELETE FROM node_addresses WHERE node_id=? AND addr_type=0", -1, &del, NULL); if (del) { sqlite3_bind_int64(del, 1, (sqlite3_int64)g_cc.my_node_id); sqlite3_step(del); int deleted = sqlite3_changes(g_cc.db); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] DELETE addr_type=0 for my_node=0x%016llx: %d rows deleted", CC_ID, (unsigned long long)g_cc.my_node_id, deleted); sqlite3_finalize(del); } sqlite3_stmt* ins = NULL; sqlite3_prepare_v2(g_cc.db, "INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)" " VALUES(?,?,1,?,?,0,?)", -1, &ins, NULL); if (!ins) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] FAIL prepare INSERT stmt my_node=0x%016llx", CC_ID, (unsigned long long)g_cc.my_node_id); return; } struct ETCP_SOCKET* sock = g_cc.inst->etcp_sockets; int sock_count = 0, written = 0; while (sock) { sock_count++; struct sockaddr_storage* sa = sock->interface_addr.ss_family ? &sock->interface_addr : NULL; if (!sa) sa = sock->local_addr.ss_family ? &sock->local_addr : NULL; if (!sa) { DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] socket %d — no interface/local addr", CC_ID, sock_count); sock = sock->next; continue; } if (sa->ss_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)sa; sqlite3_bind_int64(ins, 1, (sqlite3_int64)g_cc.my_node_id); sqlite3_bind_int(ins, 2, 4); sqlite3_bind_blob(ins, 3, &sin->sin_addr, 4, SQLITE_STATIC); sqlite3_bind_int(ins, 4, (int)ntohs(sin->sin_port)); sqlite3_bind_int(ins, 5, (int)sock->sock_id); sqlite3_step(ins); sqlite3_reset(ins); uint8_t* ip = (uint8_t*)&sin->sin_addr; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] INSERT my_node=0x%016llx sock=%d %d.%d.%d.%d:%d", CC_ID, (unsigned long long)g_cc.my_node_id, sock->sock_id, ip[0], ip[1], ip[2], ip[3], (int)ntohs(sin->sin_port)); written++; } else if (sa->ss_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; sqlite3_bind_int64(ins, 1, (sqlite3_int64)g_cc.my_node_id); sqlite3_bind_int(ins, 2, 6); sqlite3_bind_blob(ins, 3, &sin6->sin6_addr, 16, SQLITE_STATIC); sqlite3_bind_int(ins, 4, (int)ntohs(sin6->sin6_port)); sqlite3_bind_int(ins, 5, (int)sock->sock_id); sqlite3_step(ins); sqlite3_reset(ins); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] INSERT v6 my_node=0x%016llx sock=%d port=%d", CC_ID, (unsigned long long)g_cc.my_node_id, sock->sock_id, (int)ntohs(sin6->sin6_port)); written++; } sock = sock->next; } sqlite3_finalize(ins); if (sock_count == 0) DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] etcp_sockets is NULL — NO sockets, addresses NOT written!", CC_ID); else if (written == 0) DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] %d sockets found but 0 addresses written (all have no interface_addr nor local_addr)", CC_ID, sock_count); else DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] DONE: %d of %d sockets written, my_node=0x%016llx", CC_ID, written, sock_count, (unsigned long long)g_cc.my_node_id); sqlite3_stmt* chk = NULL; sqlite3_prepare_v2(g_cc.db, "SELECT COUNT(*) FROM node_addresses WHERE node_id=? AND addr_type=0", -1, &chk, NULL); if (chk) { sqlite3_bind_int64(chk, 1, (sqlite3_int64)g_cc.my_node_id); int cnt = sqlite3_step(chk) == SQLITE_ROW ? sqlite3_column_int(chk, 0) : -1; DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "%s: [ADDR_SYNC] VERIFY: total addr_type=0 rows for my_node=0x%016llx = %d", CC_ID, (unsigned long long)g_cc.my_node_id, cnt); sqlite3_finalize(chk); } } void chat_core_save_ui_state(const char* key, const char* value) { if (!g_cc.initialized || !key || !value) return; sqlite3_stmt* st = NULL; sqlite3_prepare_v2(g_cc.db, "INSERT OR REPLACE INTO ui_state(key, value) VALUES(?, ?)", -1, &st, NULL); if (st) { sqlite3_bind_text(st, 1, key, -1, SQLITE_STATIC); sqlite3_bind_text(st, 2, value, -1, SQLITE_STATIC); sqlite3_step(st); sqlite3_finalize(st); } } struct save_ui_state_arg { char data[256]; }; void chat_core_save_ui_state_trampoline(void* arg) { struct save_ui_state_arg* a = (struct save_ui_state_arg*)arg; const char* key = a->data; const char* value = key + strlen(key) + 1; chat_core_save_ui_state(key, value); u_free(arg); }