From e642551dfe95dbc51ac7efe9d2b7cf8698d18958 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 12:38:11 +0200 Subject: [PATCH] Expose signed storage roles independently of route priority --- src/chat/chat_member.c | 14 ++++++++++++-- src/chat/chat_member.h | 6 ++++-- tools/chatgui/src/avatars/avatarmanager.cpp | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/chat/chat_member.c b/src/chat/chat_member.c index 1f05f177..ef5d558c 100644 --- a/src/chat/chat_member.c +++ b/src/chat/chat_member.c @@ -87,6 +87,7 @@ void chat_core_serialize_member(const struct chat_member_display* src, uint8_t* memcpy(dst + 15, src->name, 64); mf_put64(dst + 79, src->avatar_server); memcpy(dst + 87, src->avatar_file, 69); + dst[156] = src->storage_role; } int chat_core_deserialize_member(const uint8_t* src, struct chat_member_display* dst) { @@ -102,6 +103,7 @@ int chat_core_deserialize_member(const uint8_t* src, struct chat_member_display* dst->name[63] = '\0'; dst->avatar_server = mf_get64(src + 79); memcpy(dst->avatar_file, src + 87, 69); dst->avatar_file[68] = 0; + dst->storage_role = src[156]; return 0; } @@ -120,6 +122,12 @@ static void member_avatar(struct chat_member_display* m, const char* userinfo) { if (m->avatar_server) memcpy(m->avatar_file, file, 69); } +static int member_storage_role(const char* tags, sqlite3_stmt* st, int source, int signature, int deleted) { + char role[16]; + return !deleted && sqlite3_column_int(st, source) == 0 && sqlite3_column_bytes(st, signature) == 64 && + tags && json_flat_get(tags, "storage", role, sizeof(role)) == 0 && !strcmp(role, "yes"); +} + /* ─── запрос списка мемберов ─── */ int chat_core_get_member_list(struct UTUN_INSTANCE* inst, const char* ch_id, uint8_t** out, int* count) { @@ -140,7 +148,7 @@ int chat_core_get_member_list(struct UTUN_INSTANCE* inst, const char* ch_id, uin snprintf(sql, sizeof(sql), "SELECT p.node_id, COALESCE(n.name,'')," " COALESCE(p.local_nick,''), COALESCE(p.adm_tags,'')," - " COALESCE(p.storage,0), COALESCE(p.deleted,0), COALESCE(p.userinfo,'')" + " COALESCE(p.storage,0), COALESCE(p.deleted,0), COALESCE(p.userinfo,''),p.source,p.adm_tags_sig" " FROM \"%s\" p LEFT JOIN nodes n ON p.node_id=n.node_id" " ORDER BY p.node_id ASC", peers_tbl); @@ -168,6 +176,7 @@ int chat_core_get_member_list(struct UTUN_INSTANCE* inst, const char* ch_id, uin const char* local_nick = (const char*)sqlite3_column_text(st, 2); const char* adm_tags = (const char*)sqlite3_column_text(st, 3); m->storage = (uint8_t)sqlite3_column_int(st, 4); + m->storage_role = member_storage_role(adm_tags, st, 7, 8, sqlite3_column_int(st, 5)); m->is_self = (uint8_t)(m->node_id == my_id ? 1 : 0); m->flags = parse_adm_tags_flags(adm_tags) | member_radio_flag(inst, ch_id, m->node_id); if (sqlite3_column_int(st, 5)) m->flags |= CHAT_MEMBER_FLAG_DELETED; @@ -268,7 +277,7 @@ int chat_core_get_single_member(struct UTUN_INSTANCE* inst, const char* ch_id, u snprintf(sql, sizeof(sql), "SELECT COALESCE(n.name,'')," " COALESCE(p.local_nick,''), COALESCE(p.adm_tags,'')," - " COALESCE(p.storage,0), COALESCE(p.deleted,0), COALESCE(p.userinfo,'')" + " COALESCE(p.storage,0), COALESCE(p.deleted,0), COALESCE(p.userinfo,''),p.source,p.adm_tags_sig" " FROM \"%s\" p LEFT JOIN nodes n ON p.node_id=n.node_id" " WHERE p.node_id=?", peers_tbl); @@ -284,6 +293,7 @@ int chat_core_get_single_member(struct UTUN_INSTANCE* inst, const char* ch_id, u const char* local_nick = (const char*)sqlite3_column_text(st, 1); const char* adm_tags = (const char*)sqlite3_column_text(st, 2); m.storage = (uint8_t)sqlite3_column_int(st, 3); + m.storage_role = member_storage_role(adm_tags, st, 6, 7, sqlite3_column_int(st, 4)); m.is_self = (uint8_t)(node_id == my_id ? 1 : 0); m.flags = parse_adm_tags_flags(adm_tags) | member_radio_flag(inst, ch_id, node_id); if (sqlite3_column_int(st, 4)) m.flags |= CHAT_MEMBER_FLAG_DELETED; diff --git a/src/chat/chat_member.h b/src/chat/chat_member.h index b90161f9..c4509d42 100644 --- a/src/chat/chat_member.h +++ b/src/chat/chat_member.h @@ -5,7 +5,7 @@ * Live-снимки для всех потребителей формирует chat_core_get_member_list() в uasync-потоке. * Android дополняет ими свой JSON-список с расширенными свойствами участников. * - * Формат на проводе (ручная сериализация, фиксированный размер 156 байт): + * Формат на проводе (ручная сериализация, фиксированный размер 157 байт): * [0-7] node_id uint64_t LE * [8] online uint8_t * [9] is_self uint8_t @@ -16,6 +16,7 @@ * [15-78] name char[64] local_nick если задан, иначе node name. \0-padded * [79-86] avatar_server uint64_t LE; 0 = нет аватарки * [87-155] avatar_file char[69], sha256.png с NUL + * [156] storage_role uint8_t, подписанная роль, независимо от сетевого приоритета storage */ #ifndef CHAT_MEMBER_H @@ -34,7 +35,7 @@ extern "C" { #define CHAT_MEMBER_FLAG_DELETED 0x08 #define CHAT_MEMBER_FLAG_RADIO 0x10 -#define CHAT_MEMBER_DISPLAY_SIZE 156 +#define CHAT_MEMBER_DISPLAY_SIZE 157 /* Получить всех мемберов канала. out = u_malloc-массив, count = количество. */ int chat_core_get_member_list(struct UTUN_INSTANCE* inst, const char* ch_id, uint8_t** out, int* count); @@ -58,6 +59,7 @@ struct chat_member_display { char name[64]; uint64_t avatar_server; char avatar_file[69]; + uint8_t storage_role; }; void chat_core_serialize_member(const struct chat_member_display* src, uint8_t* dst); diff --git a/tools/chatgui/src/avatars/avatarmanager.cpp b/tools/chatgui/src/avatars/avatarmanager.cpp index 4af14e98..c6f184fc 100644 --- a/tools/chatgui/src/avatars/avatarmanager.cpp +++ b/tools/chatgui/src/avatars/avatarmanager.cpp @@ -89,7 +89,7 @@ void AvatarManager::onMembers(const QString& channel, int count, const uint8_t* chat_member_display member{}; chat_core_deserialize_member(data + i * CHAT_MEMBER_DISPLAY_SIZE, &member); const QString context = key(group, member.node_id); seen.insert(context); if (member.flags & CHAT_MEMBER_FLAG_DELETED) { removeMember(channel, member.node_id); continue; } - if (member.storage) m_servers[group][member.node_id] = QString::fromUtf8(member.name); + if (member.storage_role) m_servers[group][member.node_id] = QString::fromUtf8(member.name); else m_servers[group].remove(member.node_id); Ref ref{group, member.node_id, member.avatar_server, QString::fromLatin1(member.avatar_file).left(64)}; if (!ref.server || !mf_avatar_file_valid(member.avatar_file)) { ref.server = 0; ref.hash.clear(); }