136 lines
5.4 KiB
C
136 lines
5.4 KiB
C
/*
|
|
* chat_profile.c — профиль узла: имя, адреса, ui_state
|
|
*
|
|
* Вынесено из chat_core.c для уменьшения размера модуля.
|
|
*/
|
|
|
|
#include "chat_core_priv.h"
|
|
#include "chat_event.h"
|
|
#include "chat_member.h"
|
|
#include "../../lib/json_flat.h"
|
|
#include "../routing_layer/topo_node_sqlite.h"
|
|
#include "../routing_layer/topo_node.h"
|
|
#include "member_sync.h"
|
|
|
|
#include "../utun_instance.h"
|
|
#include "../transport_layer/etcp.h"
|
|
#include "../transport_layer/etcp_connections.h"
|
|
#include "../transport_layer/secure_channel.h"
|
|
#include "../ntp_time.h"
|
|
#include "../../lib/u_async.h"
|
|
#include "../../lib/mem.h"
|
|
#include "../../lib/platform_compat.h"
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
void chat_core_set_my_node_id(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
|
struct chat_core_ctx* cc = CC(inst);
|
|
if (cc) cc->my_node_id = node_id;
|
|
}
|
|
|
|
void chat_core_update_my_name(struct UTUN_INSTANCE* inst, const char* name) {
|
|
struct chat_core_ctx* cc = CC(inst);
|
|
if (!cc || !cc->initialized || !name) return;
|
|
topo_node_sqlite_node_update_verified(cc->db, cc->my_node_id,
|
|
name, inst->my_keys.public_key, inst->my_ed25519_pubkey,
|
|
(uint64_t)ntp_time_get_seconds(inst), 0);
|
|
snprintf(inst->name, sizeof(inst->name), "%s", name);
|
|
chat_core_update_my_member(inst);
|
|
}
|
|
|
|
void chat_core_update_my_name_trampoline(void* arg) {
|
|
struct update_my_name_arg* a = (struct update_my_name_arg*)arg;
|
|
if (!a) return;
|
|
chat_core_update_my_name(a->inst, a->name);
|
|
u_free(arg);
|
|
}
|
|
|
|
/* ── Обновление своего мембера (userinfo + update_ts + подпись + синк).
|
|
Адреса больше не здесь — их синхронизирует BGP/topo_group. ── */
|
|
|
|
void chat_core_update_my_member(struct UTUN_INSTANCE* inst) {
|
|
struct chat_core_ctx* cc = CC(inst);
|
|
if (!cc || !cc->initialized || !cc->db || !inst) return;
|
|
|
|
uint64_t myid = cc->my_node_id;
|
|
const char* my_name = inst->name[0] ? inst->name : "";
|
|
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", my_name);
|
|
|
|
sqlite3_stmt* cs = NULL;
|
|
sqlite3_prepare_v2(cc->db, "SELECT channel_id FROM channels", -1, &cs, NULL);
|
|
if (!cs) return;
|
|
while (sqlite3_step(cs) == SQLITE_ROW) {
|
|
const char* ch = (const char*)sqlite3_column_text(cs, 0);
|
|
if (!ch) continue;
|
|
|
|
char tbl[80]; peers_table_name(ch, tbl, sizeof(tbl));
|
|
uint8_t join_sig[64]; uint64_t join_ts = 0;
|
|
if (topo_node_sqlite_member_get_join(cc->db, ch, myid, join_sig, &join_ts) != 0) continue;
|
|
|
|
uint64_t local_uts = 0;
|
|
{ char q[256]; snprintf(q, sizeof(q), "SELECT update_ts FROM \"%s\" WHERE node_id=?", tbl);
|
|
sqlite3_stmt* ps = NULL;
|
|
if (sqlite3_prepare_v2(cc->db, q, -1, &ps, NULL) == SQLITE_OK) {
|
|
sqlite3_bind_int64(ps, 1, (sqlite3_int64)myid);
|
|
if (sqlite3_step(ps) == SQLITE_ROW) local_uts = (uint64_t)sqlite3_column_int64(ps, 0);
|
|
sqlite3_finalize(ps);
|
|
}
|
|
}
|
|
|
|
uint64_t now = (uint64_t)ntp_time_get_seconds(inst);
|
|
uint64_t update_ts = (now > local_uts) ? now : (local_uts + 1); /* строго больше локального */
|
|
|
|
uint8_t umsg[8192];
|
|
int ulen = member_sync_build_update_msg(join_sig, update_ts, juser, umsg, (int)sizeof(umsg));
|
|
if (ulen < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: build_update_msg failed ch=%s", CC_ID, ch); continue; }
|
|
uint8_t update_sig[64]; memset(update_sig, 0, 64);
|
|
EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, 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, update_sig, &(size_t){64}, umsg, (size_t)ulen);
|
|
EVP_MD_CTX_free(mdctx);
|
|
}
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
|
|
int r2 = member_sync_put(inst, ch, myid, inst->my_keys.public_key,
|
|
inst->my_ed25519_pubkey, join_sig, join_ts,
|
|
update_sig, update_ts, juser, NULL, NULL, 0, 0, NULL);
|
|
if (r2 < 0) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: member_sync_put(self) FAILED ch=%s rc=%d", CC_ID, ch, r2);
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: update my member ch=%s ts=%llu", CC_ID, ch,
|
|
(unsigned long long)update_ts);
|
|
}
|
|
sqlite3_finalize(cs);
|
|
}
|
|
|
|
void chat_core_sync_my_addresses(struct UTUN_INSTANCE* inst) {
|
|
if (inst)
|
|
topo_node_update_my_addresses(inst);
|
|
chat_core_update_my_member(inst);
|
|
}
|
|
|
|
void chat_core_save_ui_state(struct UTUN_INSTANCE* inst, const char* key, const char* value) {
|
|
struct chat_core_ctx* cc = CC(inst);
|
|
if (!cc || !cc->initialized || !key || !value) return;
|
|
sqlite3_stmt* st = NULL;
|
|
sqlite3_prepare_v2(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);
|
|
}
|
|
}
|
|
|
|
void chat_core_save_ui_state_trampoline(void* arg) {
|
|
struct save_ui_state_arg* a = (struct save_ui_state_arg*)arg;
|
|
if (!a) return;
|
|
const char* key = a->data;
|
|
const char* value = key + strlen(key) + 1;
|
|
chat_core_save_ui_state(a->inst, key, value);
|
|
u_free(arg);
|
|
}
|