Provide generation-bound DM device member snapshots
This commit is contained in:
1 parent
0fd4f47c84
commit
24e5baa8d4
3 files changed
+74
No files matched your search
@@ -97,6 +97,7 @@ extern "C" {
|
||||
#define CHAT_EVT_DEVGROUP_CHANGED 70 /* Обновление nodes/devgroup: перезагрузить снимки участников. */
|
||||
#define CHAT_EVT_NETWORK_STATS 69 /* Native POD снимок ETCP-подключений и линков, chat_network_stats.h. */
|
||||
#define CHAT_EVT_DM_START_RESULT 72 /* request/conv/generation:8 native + success:1; только результат конкретного запроса UI. */
|
||||
#define CHAT_EVT_DM_MEMBERS 73 /* conv_len:1,conv,generation:LE8,count:LE2,(side:1,member:wire260)*count. */
|
||||
|
||||
// ── Подключение получателя событий ──
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "dm_mailbox.h"
|
||||
#include "../chat/chat_core.h"
|
||||
#include "../chat/chat_event.h"
|
||||
#include "../chat/chat_member.h"
|
||||
#include "../member_files/protocol.h"
|
||||
#include "../chat/invite_build.h"
|
||||
#include "../chat/chat_join.h"
|
||||
#include "../utun_instance.h"
|
||||
@@ -1135,6 +1137,66 @@ static void json_escape(const char* src, char* dst, size_t dst_sz) {
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
int dm_members_snapshot(struct UTUN_INSTANCE* inst, const char* conv, uint64_t generation, uint8_t** out, size_t* size) {
|
||||
struct dm_state* dm = dm_of(inst); struct dm_conv c;
|
||||
if (out) *out = NULL;
|
||||
if (size) *size = 0;
|
||||
if (!dm || !conv || !conv[0] || strlen(conv) > 63 || !out || !size || !dm_history_current(inst, conv, generation)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DM, "DM members: invalid/stale request conv=%s generation=%llu", conv ? conv : "",
|
||||
(unsigned long long)generation); return -1;
|
||||
}
|
||||
if (dm_conv_load(dm, conv, &c) != DM_CONV_FOUND) return -1;
|
||||
OPENSSL_cleanse(c.content_key, sizeof(c.content_key));
|
||||
struct dm_target members[DM_TARGET_MAX]; size_t count = 0;
|
||||
if (!c.closed && c.mode == DM_MODE_GROUPS && dm_identity_members(inst, c.parties, members, DM_TARGET_MAX, &count)) return -1;
|
||||
size_t cl = strlen(conv), bytes = 11 + cl + count * DM_MEMBER_WIRE_SIZE;
|
||||
uint8_t* snapshot = u_calloc(1, bytes);
|
||||
if (!snapshot) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM members: allocation failed count=%zu", count); return -1; }
|
||||
snapshot[0] = (uint8_t)cl; memcpy(snapshot + 1, conv, cl);
|
||||
mf_put64(snapshot + 1 + cl, generation); mf_put16(snapshot + 9 + cl, (uint16_t)count);
|
||||
size_t pos = 11 + cl;
|
||||
for (unsigned side = 0; side < 2; side++) for (size_t i = 0; i < count; i++) {
|
||||
if (members[i].side != side) continue;
|
||||
struct chat_member_display member = {0}; uint64_t group = 0;
|
||||
if (!dm_find_route_group(inst, members[i].node, c.group_id, &group, 1)) dm_shared_group_id(dm, members[i].node, &group);
|
||||
if (group) {
|
||||
char channel[64]; snprintf(channel, sizeof(channel), "%llu", (unsigned long long)group);
|
||||
uint8_t wire[CHAT_MEMBER_DISPLAY_SIZE];
|
||||
if (chat_core_get_single_member(inst, channel, members[i].node, wire) || chat_core_deserialize_member(wire, &member)) goto fail;
|
||||
} else {
|
||||
/* Узел может быть известен без общей CHAT-группы: он остаётся в списке как offline. */
|
||||
sqlite3_stmt* st = NULL;
|
||||
int rc = sqlite3_prepare_v2(dm->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL);
|
||||
if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)members[i].node);
|
||||
if (rc == SQLITE_OK) rc = sqlite3_step(st);
|
||||
const char* name = rc == SQLITE_ROW ? (const char*)sqlite3_column_text(st, 0) : NULL;
|
||||
if (name && name[0]) snprintf(member.name, sizeof(member.name), "%s", name);
|
||||
else snprintf(member.name, sizeof(member.name), "0x%016llx", (unsigned long long)members[i].node);
|
||||
int final = sqlite3_finalize(st);
|
||||
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM members: node read failed rc=%d final=%d", rc, final); goto fail;
|
||||
}
|
||||
member.node_id = members[i].node; member.is_self = member.node_id == inst->node_id;
|
||||
member.online = member.is_self; member.rtt = UINT16_MAX; member.nat_type = 255;
|
||||
}
|
||||
snapshot[pos++] = (uint8_t)side; chat_core_serialize_member(&member, snapshot + pos); pos += CHAT_MEMBER_DISPLAY_SIZE;
|
||||
}
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DM, "DM members: snapshot conv=%s generation=%llu count=%zu closed=%d", conv,
|
||||
(unsigned long long)generation, count, c.closed);
|
||||
*out = snapshot; *size = bytes; return 0;
|
||||
fail:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM members: snapshot failed conv=%s", conv); u_free(snapshot); return -1;
|
||||
}
|
||||
|
||||
void dm_members_trampoline(void* arg) {
|
||||
struct dm_messages_req* req = arg;
|
||||
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM members: missing request"); return; }
|
||||
uint8_t* snapshot = NULL; size_t size = 0;
|
||||
if (!dm_members_snapshot(req->inst, req->conv_id, req->generation, &snapshot, &size))
|
||||
chat_event_post(req->inst, CHAT_EVT_DM_MEMBERS, snapshot, (int)size);
|
||||
u_free(snapshot); u_free(req);
|
||||
}
|
||||
|
||||
/* Список бесед в JSON. */
|
||||
int dm_list_conversations_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf_size, size_t* out_len) {
|
||||
struct dm_state* dm = dm_of(inst);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <string.h>
|
||||
#include "dm_media.h"
|
||||
#include "dm_protocol.h"
|
||||
#include "../chat/chat_member.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
@@ -136,6 +137,16 @@ void dm_cancel_pending_history(struct UTUN_INSTANCE* inst, const char* conv);
|
||||
* Имя из БД либо nodes нормализуется по правилам dm_start; снимок не меняет сохранённые строки. */
|
||||
int dm_list_conversations_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf_size, size_t* out_len);
|
||||
|
||||
#define DM_MEMBER_WIRE_SIZE (1 + CHAT_MEMBER_DISPLAY_SIZE)
|
||||
/* Только uasync. Текущие известные устройства закреплённых сторон GROUPS, включая offline.
|
||||
* [conv_len:1][conv:UTF8][generation:LE64][count:LE16][side:1,member:wire260]*count.
|
||||
* side=0 — свои,1 — собеседника. DEVICE/закрытая беседа возвращают пустой список.
|
||||
* Требуется текущее ненулевое generation; при ошибке out=NULL,size=0; успех0, ошибка-1.
|
||||
* Caller освобождает *out через u_free. Чтение не меняет снимки доставки сообщений. */
|
||||
int dm_members_snapshot(struct UTUN_INSTANCE* inst, const char* conv, uint64_t generation, uint8_t** out, size_t* size);
|
||||
/* Забирает u_calloc(dm_messages_req), отправляет CHAT_EVT_DM_MEMBERS только при успехе. */
|
||||
void dm_members_trampoline(void* arg);
|
||||
|
||||
/* Сообщения беседы в JSON: расшифрованное содержимое, статусы доставки и медиа.
|
||||
* count=1..200 задаёт предел; count<=0 или count>200 означает 200.
|
||||
* offset>=0 отсчитывается от новых сообщений; offset=0 выбирает последние count.
|
||||
|
||||
Reference in new issue
Block a user