|
|
|
@ -1,5 +1,6 @@ |
|
|
|
#include "chat_sync.h" |
|
|
|
#include "chat_sync.h" |
|
|
|
#include "chat_core.h" |
|
|
|
#include "chat_core.h" |
|
|
|
|
|
|
|
#include "chat_setting.h" |
|
|
|
#include "chat_event.h" |
|
|
|
#include "chat_event.h" |
|
|
|
#include "../routing_layer/topo_node_sqlite.h" |
|
|
|
#include "../routing_layer/topo_node_sqlite.h" |
|
|
|
#include "../routing_layer/topo_group.h" |
|
|
|
#include "../routing_layer/topo_group.h" |
|
|
|
@ -50,6 +51,11 @@ struct chat_sync { |
|
|
|
uint8_t sync_scheduled; |
|
|
|
uint8_t sync_scheduled; |
|
|
|
uint64_t pending_invite_ch_id; |
|
|
|
uint64_t pending_invite_ch_id; |
|
|
|
uint64_t pending_invite_node_id; |
|
|
|
uint64_t pending_invite_node_id; |
|
|
|
|
|
|
|
char pending_password[128]; /* пароль для CHANNEL_INFO_REQ */ |
|
|
|
|
|
|
|
/* Pending incoming invites (для ask/deny) */ |
|
|
|
|
|
|
|
uint64_t pending_inv_in_ch_id[4]; |
|
|
|
|
|
|
|
uint64_t pending_inv_in_node_id[4]; |
|
|
|
|
|
|
|
int pending_inv_in_count; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#define CS_ID "chat_sync" |
|
|
|
#define CS_ID "chat_sync" |
|
|
|
@ -72,6 +78,7 @@ static const char* cs_msg_name(uint8_t type) { |
|
|
|
case CS_MSG_PEER_UPSERT: return "PEER_UPSERT"; |
|
|
|
case CS_MSG_PEER_UPSERT: return "PEER_UPSERT"; |
|
|
|
case CS_MSG_ERROR: return "ERROR"; |
|
|
|
case CS_MSG_ERROR: return "ERROR"; |
|
|
|
case CS_MSG_PEER_REMOVE: return "PEER_REMOVE"; |
|
|
|
case CS_MSG_PEER_REMOVE: return "PEER_REMOVE"; |
|
|
|
|
|
|
|
case CS_MSG_CHANNEL_INVITE: return "CHANNEL_INVITE"; |
|
|
|
default: return "???"; |
|
|
|
default: return "???"; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -136,6 +143,8 @@ static void cs_handle_peer_upsert(struct chat_sync* cs, uint64_t peer, |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len); |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len); |
|
|
|
static void cs_handle_peer_remove(struct chat_sync* cs, uint64_t peer, |
|
|
|
static void cs_handle_peer_remove(struct chat_sync* cs, uint64_t peer, |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len); |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len); |
|
|
|
|
|
|
|
static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer, |
|
|
|
|
|
|
|
const char* ch_id, const uint8_t* pl, size_t len); |
|
|
|
|
|
|
|
|
|
|
|
/* ── Recv dispatcher ── */ |
|
|
|
/* ── Recv dispatcher ── */ |
|
|
|
|
|
|
|
|
|
|
|
@ -178,6 +187,7 @@ static void chat_sync_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
case CS_MSG_PEER_REMOVE: cs_handle_peer_remove(g_cs, peer, ch_id, pl, plen); break; |
|
|
|
case CS_MSG_PEER_REMOVE: cs_handle_peer_remove(g_cs, peer, ch_id, pl, plen); break; |
|
|
|
|
|
|
|
case CS_MSG_CHANNEL_INVITE: cs_handle_channel_invite(g_cs, peer, ch_id, pl, plen); break; |
|
|
|
default: DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: UNKNOWN msg type=%02x from=%016llx", CS_ID, type, (unsigned long long)peer); break; |
|
|
|
default: DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: UNKNOWN msg type=%02x from=%016llx", CS_ID, type, (unsigned long long)peer); break; |
|
|
|
} |
|
|
|
} |
|
|
|
u_free(entry->dgram); queue_entry_free(entry); |
|
|
|
u_free(entry->dgram); queue_entry_free(entry); |
|
|
|
@ -405,12 +415,16 @@ static void cs_start_channel_join(struct chat_sync* cs, uint64_t peer) { |
|
|
|
if (!cs || cs->info_req_timer) return; |
|
|
|
if (!cs || cs->info_req_timer) return; |
|
|
|
char ch_id_str[64]; |
|
|
|
char ch_id_str[64]; |
|
|
|
snprintf(ch_id_str, sizeof(ch_id_str), "%llu", (unsigned long long)cs->pending_invite_ch_id); |
|
|
|
snprintf(ch_id_str, sizeof(ch_id_str), "%llu", (unsigned long long)cs->pending_invite_ch_id); |
|
|
|
uint8_t req[1] = { CS_MSG_CHANNEL_INFO_REQ }; |
|
|
|
size_t plen = strlen(cs->pending_password); |
|
|
|
cs_send(cs, ch_id_str, peer, req, 1); |
|
|
|
uint8_t req[130]; |
|
|
|
|
|
|
|
req[0] = CS_MSG_CHANNEL_INFO_REQ; |
|
|
|
|
|
|
|
req[1] = (uint8_t)(plen > 127 ? 127 : plen); |
|
|
|
|
|
|
|
if (plen > 0) memcpy(req + 2, cs->pending_password, plen > 127 ? 127 : plen); |
|
|
|
|
|
|
|
cs_send(cs, ch_id_str, peer, req, 2 + (plen > 127 ? 127 : (int)plen)); |
|
|
|
cs->info_req_timer = uasync_set_timeout(cs->inst->ua, |
|
|
|
cs->info_req_timer = uasync_set_timeout(cs->inst->ua, |
|
|
|
CS_INFO_REQ_TIMEOUT_MS * 10, cs, cs_info_req_timeout_cb, "cs_info_req"); |
|
|
|
CS_INFO_REQ_TIMEOUT_MS * 10, cs, cs_info_req_timeout_cb, "cs_info_req"); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: sent CHANNEL_INFO_REQ ch=%llu peer=0x%016llx", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: sent CHANNEL_INFO_REQ ch=%llu peer=0x%016llx pass_len=%zu", |
|
|
|
CS_ID, (unsigned long long)cs->pending_invite_ch_id, (unsigned long long)peer); |
|
|
|
CS_ID, (unsigned long long)cs->pending_invite_ch_id, (unsigned long long)peer, plen); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* ── Connection callbacks ── */ |
|
|
|
/* ── Connection callbacks ── */ |
|
|
|
@ -695,7 +709,8 @@ static void cm_invite_trampoline(void* arg) { |
|
|
|
void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id, |
|
|
|
void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id, |
|
|
|
const uint8_t* pubkey_bin, |
|
|
|
const uint8_t* pubkey_bin, |
|
|
|
const uint8_t* addrs_data, int addr_count, |
|
|
|
const uint8_t* addrs_data, int addr_count, |
|
|
|
int addrs_data_len) { |
|
|
|
int addrs_data_len, |
|
|
|
|
|
|
|
const char* password) { |
|
|
|
if (!g_cs || !g_cs->inst || !g_cs->inst->ua) { |
|
|
|
if (!g_cs || !g_cs->inst || !g_cs->inst->ua) { |
|
|
|
int r = -7; |
|
|
|
int r = -7; |
|
|
|
#ifdef __ANDROID__ |
|
|
|
#ifdef __ANDROID__ |
|
|
|
@ -724,13 +739,21 @@ void chat_sync_connect_from_invite(uint64_t channel_id, uint64_t node_id, |
|
|
|
|
|
|
|
|
|
|
|
g_cs->pending_invite_ch_id = channel_id; |
|
|
|
g_cs->pending_invite_ch_id = channel_id; |
|
|
|
g_cs->pending_invite_node_id = node_id; |
|
|
|
g_cs->pending_invite_node_id = node_id; |
|
|
|
|
|
|
|
if (password && password[0]) { |
|
|
|
|
|
|
|
size_t pl = strlen(password); |
|
|
|
|
|
|
|
if (pl >= sizeof(g_cs->pending_password)) pl = sizeof(g_cs->pending_password) - 1; |
|
|
|
|
|
|
|
memcpy(g_cs->pending_password, password, pl); |
|
|
|
|
|
|
|
g_cs->pending_password[pl] = '\0'; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
g_cs->pending_password[0] = '\0'; |
|
|
|
|
|
|
|
} |
|
|
|
#ifdef __ANDROID__ |
|
|
|
#ifdef __ANDROID__ |
|
|
|
__android_log_print(ANDROID_LOG_INFO, "utun-chat", "invite ch=%llu node=%016llx addrs=%d", |
|
|
|
__android_log_print(ANDROID_LOG_INFO, "utun-chat", "invite ch=%llu node=%016llx addrs=%d pass=%s", |
|
|
|
(long long)channel_id, (long long)node_id, addr_count); |
|
|
|
(long long)channel_id, (long long)node_id, addr_count, password ? "yes" : "no"); |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
#ifndef __ANDROID__ |
|
|
|
#ifndef __ANDROID__ |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite start ch=%llu node=0x%016llx pubkey=%016llx... addrs=%d", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite start ch=%llu node=0x%016llx pubkey=%016llx... addrs=%d pass=%s", |
|
|
|
CS_ID, channel_id, node_id, *(const uint64_t*)pubkey_bin, addr_count); |
|
|
|
CS_ID, channel_id, node_id, *(const uint64_t*)pubkey_bin, addr_count, password && password[0] ? "yes" : "no"); |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
struct cm_invite_wrap { struct chat_invite inv; }* w = u_malloc(sizeof(struct cm_invite_wrap)); |
|
|
|
struct cm_invite_wrap { struct chat_invite inv; }* w = u_malloc(sizeof(struct cm_invite_wrap)); |
|
|
|
@ -825,8 +848,32 @@ static int _get_node_name(sqlite3* db, uint64_t node_id, char* out, size_t sz) { |
|
|
|
|
|
|
|
|
|
|
|
static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer, |
|
|
|
static void cs_handle_channel_info_req(struct chat_sync* cs, uint64_t peer, |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len) { |
|
|
|
const char* ch_id, const uint8_t* pl, size_t len) { |
|
|
|
(void)pl; (void)len; |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: INFO_REQ ch=%s from=%016llx len=%zu", CS_ID, ch_id, (unsigned long long)peer, len); |
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: INFO_REQ ch=%s from=%016llx", CS_ID, ch_id, (unsigned long long)peer); |
|
|
|
|
|
|
|
|
|
|
|
/* Parse password from request (v2 format: type + pass_len + optional_password) */ |
|
|
|
|
|
|
|
char req_password[128] = ""; |
|
|
|
|
|
|
|
if (len >= 1) { |
|
|
|
|
|
|
|
uint8_t plen2 = pl[0]; |
|
|
|
|
|
|
|
if (plen2 > 0 && plen2 <= len - 1) { |
|
|
|
|
|
|
|
if (plen2 > 127) plen2 = 127; |
|
|
|
|
|
|
|
memcpy(req_password, pl + 1, plen2); req_password[plen2] = '\0'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check password if server has one configured */ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
struct global_config *gcfg = &cs->inst->config->global; |
|
|
|
|
|
|
|
if (gcfg->chatserver_enabled && gcfg->chatserver_join_password[0]) { |
|
|
|
|
|
|
|
if (strcmp(req_password, gcfg->chatserver_join_password) != 0) { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INFO_REQ wrong password ch=%s from=%016llx (got '%s', expected '%s')", |
|
|
|
|
|
|
|
CS_ID, ch_id, (unsigned long long)peer, req_password, gcfg->chatserver_join_password); |
|
|
|
|
|
|
|
uint8_t err[2] = { CS_MSG_ERROR, 0x01 }; /* 0x01 = wrong password */ |
|
|
|
|
|
|
|
cs_send(cs, ch_id, peer, err, 2); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char name[128]; uint64_t owner; uint8_t x25519[32], ed_pub[32], ch_sig[64]; |
|
|
|
char name[128]; uint64_t owner; uint8_t x25519[32], ed_pub[32], ch_sig[64]; |
|
|
|
if (topo_node_sqlite_channel_get(cs->inst->topo_sqlite_db, |
|
|
|
if (topo_node_sqlite_channel_get(cs->inst->topo_sqlite_db, |
|
|
|
ch_id, name, (int)sizeof(name), &owner, x25519, ed_pub, ch_sig) != 0) { |
|
|
|
ch_id, name, (int)sizeof(name), &owner, x25519, ed_pub, ch_sig) != 0) { |
|
|
|
@ -1439,3 +1486,216 @@ static void cs_handle_peer_remove(struct chat_sync* cs, uint64_t peer, |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: PEER_REMOVE node=0x%016llx ch=%s", |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: PEER_REMOVE node=0x%016llx ch=%s", |
|
|
|
CS_ID, (unsigned long long)node_id, ch_id); |
|
|
|
CS_ID, (unsigned long long)node_id, ch_id); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ─── CHANNEL_INVITE (0x0F): inviter → joiner (push-приглашение в канал) ─── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void cs_handle_channel_invite(struct chat_sync* cs, uint64_t peer, |
|
|
|
|
|
|
|
const char* ch_id, const uint8_t* pl, size_t len) { |
|
|
|
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE ch=%s from=%016llx len=%zu", |
|
|
|
|
|
|
|
CS_ID, ch_id, (unsigned long long)peer, len); |
|
|
|
|
|
|
|
if (len < 1 + 8 + 32 + 32 + 64 + 1 + 64 + 8 + 1) { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE too short len=%zu peer=%016llx", CS_ID, len, (unsigned long long)peer); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/* Same payload format as CHANNEL_INFO_RESP */ |
|
|
|
|
|
|
|
const uint8_t* p = pl; |
|
|
|
|
|
|
|
uint8_t nl = *p++; |
|
|
|
|
|
|
|
if (1 + nl + 8 + 32 + 32 + 64 + 1 + 64 + 8 + 1 > (int)len) { DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE truncated", CS_ID); return; } |
|
|
|
|
|
|
|
char name[128]; memcpy(name, p, nl); name[nl] = '\0'; p += nl; |
|
|
|
|
|
|
|
uint64_t owner; memcpy(&owner, p, 8); p += 8; |
|
|
|
|
|
|
|
const uint8_t* x25519 = p; p += 32; |
|
|
|
|
|
|
|
const uint8_t* ed_pub = p; p += 32; |
|
|
|
|
|
|
|
const uint8_t* ch_sig = p; p += 64; |
|
|
|
|
|
|
|
uint8_t inv_flags = *p++; |
|
|
|
|
|
|
|
const uint8_t* inviter_join_sig = NULL; uint64_t inviter_join_ts = 0; |
|
|
|
|
|
|
|
if (inv_flags & PEERS_FLAG_HAS_JOIN) { |
|
|
|
|
|
|
|
if (p + 72 > pl + len) return; inviter_join_sig = p; p += 64; memcpy(&inviter_join_ts, p, 8); p += 8; } |
|
|
|
|
|
|
|
/* skip inviter_update_sig/ts/userinfo for now — verify channel sig only */ |
|
|
|
|
|
|
|
p += 64; uint64_t inviter_update_ts; memcpy(&inviter_update_ts, p, 8); p += 8; |
|
|
|
|
|
|
|
uint8_t userinfo_len = *p++; p += userinfo_len; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Verify channel signature */ |
|
|
|
|
|
|
|
uint8_t vmsg[1024]; size_t vlen = 0; |
|
|
|
|
|
|
|
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", ch_id) + 1; |
|
|
|
|
|
|
|
vlen += snprintf((char*)vmsg + vlen, sizeof(vmsg) - vlen, "%s", name) + 1; |
|
|
|
|
|
|
|
memcpy(vmsg + vlen, &owner, 8); vlen += 8; |
|
|
|
|
|
|
|
memcpy(vmsg + vlen, x25519, 32); vlen += 32; |
|
|
|
|
|
|
|
memcpy(vmsg + vlen, ed_pub, 32); vlen += 32; |
|
|
|
|
|
|
|
if (cs_ed25519_verify(ed_pub, vmsg, vlen, ch_sig) != 0) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE invalid ch_sig ch=%s from=%016llx", CS_ID, ch_id, (unsigned long long)peer); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check join_policy: server always autojoin, GUI uses chat_setting */ |
|
|
|
|
|
|
|
int policy; |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
struct global_config *gcfg = &cs->inst->config->global; |
|
|
|
|
|
|
|
policy = gcfg->chatserver_enabled ? 0 : chat_setting_get_int("join_policy", 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE policy=%d (chatserver=%d) ch=%s from=%016llx", |
|
|
|
|
|
|
|
CS_ID, policy, cs->inst->config->global.chatserver_enabled, ch_id, (unsigned long long)peer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (policy == 2) { /* deny */ |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE denied (policy=deny) ch=%s from=%016llx", |
|
|
|
|
|
|
|
CS_ID, ch_id, (unsigned long long)peer); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (policy == 0) { /* autojoin */ |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE autojoin ch=%s from=%016llx", |
|
|
|
|
|
|
|
CS_ID, ch_id, (unsigned long long)peer); |
|
|
|
|
|
|
|
/* Save channel and start join handshake */ |
|
|
|
|
|
|
|
topo_node_sqlite_channel_put(cs->inst->topo_sqlite_db, |
|
|
|
|
|
|
|
ch_id, name, owner, x25519, NULL, ed_pub, NULL, ch_sig); |
|
|
|
|
|
|
|
chat_core_ensure_channel_ready(ch_id); |
|
|
|
|
|
|
|
/* Initiate join: connect back and send CHANNEL_INFO_REQ */ |
|
|
|
|
|
|
|
chat_sync_join_channel(cs->inst, ch_id, peer); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* policy == ask — post event to GUI */ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
size_t inv_name_len = 0; char inv_name[128] = ""; |
|
|
|
|
|
|
|
{ sqlite3* vdb = cs->inst->topo_sqlite_db; |
|
|
|
|
|
|
|
sqlite3_stmt* st = NULL; |
|
|
|
|
|
|
|
sqlite3_prepare_v2(vdb, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL); |
|
|
|
|
|
|
|
if (st) { |
|
|
|
|
|
|
|
sqlite3_bind_int64(st, 1, (sqlite3_int64)peer); |
|
|
|
|
|
|
|
if (sqlite3_step(st) == SQLITE_ROW) { |
|
|
|
|
|
|
|
const unsigned char* nm = sqlite3_column_text(st, 0); |
|
|
|
|
|
|
|
if (nm) { snprintf(inv_name, sizeof(inv_name), "%s", nm); inv_name_len = strlen(inv_name); } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
sqlite3_finalize(st); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
uint8_t evt[512]; int epos = 0; |
|
|
|
|
|
|
|
uint8_t cl = (uint8_t)strlen(ch_id); evt[epos++] = cl; memcpy(evt + epos, ch_id, cl); epos += cl; |
|
|
|
|
|
|
|
evt[epos++] = nl; memcpy(evt + epos, name, nl); epos += nl; |
|
|
|
|
|
|
|
memcpy(evt + epos, &peer, 8); epos += 8; |
|
|
|
|
|
|
|
evt[epos++] = (uint8_t)inv_name_len; |
|
|
|
|
|
|
|
if (inv_name_len) { memcpy(evt + epos, inv_name, inv_name_len); epos += inv_name_len; } |
|
|
|
|
|
|
|
chat_event_post(CHAT_EVT_INVITE_RECEIVED, evt, epos); |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: CHANNEL_INVITE ask posted to GUI ch=%s name=%s from=%016llx (%s)", |
|
|
|
|
|
|
|
CS_ID, ch_id, name, (unsigned long long)peer, inv_name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ─── chat_sync_invite_to_channel: пригласить узел в наш канал ─── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void chat_sync_invite_to_channel(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id) { |
|
|
|
|
|
|
|
if (!g_cs || !inst || !ch_id || !ch_id[0]) return; |
|
|
|
|
|
|
|
if (target_node_id == 0 || target_node_id == inst->node_id) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t channel_id = strtoull(ch_id, NULL, 10); |
|
|
|
|
|
|
|
if (channel_id == 0) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_to_channel invalid ch_id='%s'", CS_ID, ch_id); return; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get channel data from DB */ |
|
|
|
|
|
|
|
char ch_name[128]; uint64_t owner; uint8_t ch_x25519[32], ch_ed[32], ch_sig[64]; |
|
|
|
|
|
|
|
if (topo_node_sqlite_channel_get(inst->topo_sqlite_db, |
|
|
|
|
|
|
|
ch_id, ch_name, (int)sizeof(ch_name), &owner, ch_x25519, ch_ed, ch_sig) != 0) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_to_channel channel not found: %s", CS_ID, ch_id); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if already connected */ |
|
|
|
|
|
|
|
struct ETCP_CONN* conn = cs_find_conn_for_node(inst, target_node_id); |
|
|
|
|
|
|
|
if (!conn || !conn->links_up || !conn->initialized) { |
|
|
|
|
|
|
|
/* Try to connect via conn_mgr */ |
|
|
|
|
|
|
|
if (inst->topo_groups) { |
|
|
|
|
|
|
|
struct TOPO_GROUP* g = topo_groups_find(inst->topo_groups, channel_id); |
|
|
|
|
|
|
|
if (g) { |
|
|
|
|
|
|
|
struct TOPO_GROUP_NODE* nq = topo_node_find_by_id(g, target_node_id); |
|
|
|
|
|
|
|
if (nq && nq->handle) { |
|
|
|
|
|
|
|
/* Already have a handle — connection in progress or established */ |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: invite_to_channel — existing handle for node 0x%016llx, waiting for conn_up", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)target_node_id); |
|
|
|
|
|
|
|
g_cs->pending_invite_ch_id = channel_id; |
|
|
|
|
|
|
|
g_cs->pending_invite_node_id = target_node_id; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/* Use conn_mgr_open_invite with channel group context */ |
|
|
|
|
|
|
|
struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni)); |
|
|
|
|
|
|
|
if (ni) { |
|
|
|
|
|
|
|
ni->node_id = target_node_id; |
|
|
|
|
|
|
|
/* Addresses loaded from node_addresses in DB by conn_mgr */ |
|
|
|
|
|
|
|
int r = conn_mgr_open_invite(inst, channel_id, ni, target_node_id, NULL, NULL, NULL); |
|
|
|
|
|
|
|
if (r < 0) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: invite_to_channel conn_mgr_open_invite failed rc=%d", CS_ID, r); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
u_free(ni); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "%s: invite_to_channel — no topo_groups, cannot connect", CS_ID); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/* Mark pending: after conn_up, we send CHANNEL_INVITE.
|
|
|
|
|
|
|
|
But we need to distinguish this from the joiner-side invite flow. |
|
|
|
|
|
|
|
Use a separate flag. */ |
|
|
|
|
|
|
|
g_cs->pending_invite_ch_id = channel_id; |
|
|
|
|
|
|
|
g_cs->pending_invite_node_id = target_node_id; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Already connected — send invite now */ |
|
|
|
|
|
|
|
uint8_t join_sig[64] = {0}; uint64_t join_ts = 0; |
|
|
|
|
|
|
|
{ sqlite3* vdb = inst->topo_sqlite_db; |
|
|
|
|
|
|
|
topo_node_sqlite_member_get_join(vdb, ch_id, inst->node_id, join_sig, &join_ts); } |
|
|
|
|
|
|
|
uint8_t upd_sig[64] = {0}; uint64_t upd_ts = (uint64_t)ntp_time_get_seconds(inst); |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : ""); |
|
|
|
|
|
|
|
uint8_t umsg[256]; size_t ulen = 0; |
|
|
|
|
|
|
|
memcpy(umsg + ulen, join_sig, 64); ulen += 64; |
|
|
|
|
|
|
|
memcpy(umsg + ulen, &upd_ts, 8); ulen += 8; |
|
|
|
|
|
|
|
size_t jl = strlen(juser); memcpy(umsg + ulen, juser, jl); umsg[ulen + jl] = '\0'; ulen += jl + 1; |
|
|
|
|
|
|
|
cs_ed25519_sign(inst->my_ed25519_privkey, umsg, ulen, upd_sig); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[1024]; size_t boff = 0; |
|
|
|
|
|
|
|
buf[boff++] = CS_MSG_CHANNEL_INVITE; |
|
|
|
|
|
|
|
uint8_t cnl = (uint8_t)strlen(ch_name); |
|
|
|
|
|
|
|
buf[boff++] = cnl; memcpy(buf + boff, ch_name, cnl); boff += cnl; |
|
|
|
|
|
|
|
memcpy(buf + boff, &owner, 8); boff += 8; |
|
|
|
|
|
|
|
memcpy(buf + boff, ch_x25519, 32); boff += 32; |
|
|
|
|
|
|
|
memcpy(buf + boff, ch_ed, 32); boff += 32; |
|
|
|
|
|
|
|
memcpy(buf + boff, ch_sig, 64); boff += 64; |
|
|
|
|
|
|
|
uint8_t inv_flags = (join_sig[0] || join_ts) ? PEERS_FLAG_HAS_JOIN : 0; |
|
|
|
|
|
|
|
buf[boff++] = inv_flags; |
|
|
|
|
|
|
|
if (inv_flags & PEERS_FLAG_HAS_JOIN) { |
|
|
|
|
|
|
|
memcpy(buf + boff, join_sig, 64); boff += 64; |
|
|
|
|
|
|
|
memcpy(buf + boff, &join_ts, 8); boff += 8; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
memcpy(buf + boff, upd_sig, 64); boff += 64; |
|
|
|
|
|
|
|
memcpy(buf + boff, &upd_ts, 8); boff += 8; |
|
|
|
|
|
|
|
char juser[256]; snprintf(juser, sizeof(juser), "{\"name\":\"%s\"}", inst->name[0] ? inst->name : ""); |
|
|
|
|
|
|
|
uint8_t il = (uint8_t)strlen(juser); |
|
|
|
|
|
|
|
buf[boff++] = il; memcpy(buf + boff, juser, il); boff += il; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cs_send(g_cs, ch_id, target_node_id, buf, boff); |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: sent CHANNEL_INVITE to 0x%016llx ch=%s", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)target_node_id, ch_id); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ─── accept/deny incoming invite ─── */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void chat_sync_accept_invite(struct UTUN_INSTANCE* inst, uint64_t channel_id, uint64_t inviter_node_id) { |
|
|
|
|
|
|
|
if (!g_cs || !inst) return; |
|
|
|
|
|
|
|
char ch_id[64]; snprintf(ch_id, sizeof(ch_id), "%llu", (unsigned long long)channel_id); |
|
|
|
|
|
|
|
if (channel_id == 0 || inviter_node_id == 0) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "%s: accept_invite invalid params ch=%llu node=%016llx", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: accept_invite ch=%llu inviter=0x%016llx", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id); |
|
|
|
|
|
|
|
chat_core_ensure_channel_ready(ch_id); |
|
|
|
|
|
|
|
chat_sync_join_channel(inst, ch_id, inviter_node_id); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void chat_sync_deny_invite(struct UTUN_INSTANCE* inst, uint64_t channel_id, uint64_t inviter_node_id) { |
|
|
|
|
|
|
|
(void)inst; |
|
|
|
|
|
|
|
if (!g_cs) return; |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "%s: deny_invite ch=%llu inviter=0x%016llx", |
|
|
|
|
|
|
|
CS_ID, (unsigned long long)channel_id, (unsigned long long)inviter_node_id); |
|
|
|
|
|
|
|
/* silently ignore — no response needed */ |
|
|
|
|
|
|
|
} |
|
|
|
|