diff --git a/src/dm/dm_core.c b/src/dm/dm_core.c index 952d45495..6dd777085 100644 --- a/src/dm/dm_core.c +++ b/src/dm/dm_core.c @@ -33,6 +33,7 @@ #include "../../lib/debug_config.h" #include "../../lib/platform_compat.h" #include "../../lib/strbuf.h" +#include "../../lib/json_flat.h" #include #include @@ -153,6 +154,41 @@ int dm_route_group(struct UTUN_INSTANCE* inst, uint64_t peer, uint64_t preferred return dm_find_route_group(inst, peer, preferred, group_id, 0); } +int dm_peer_role(struct UTUN_INSTANCE* inst, uint64_t peer, const char* role) { + if (!inst || !inst->topo_sqlite_db || !peer || !role || !role[0]) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM role: invalid lookup peer=%llu", (unsigned long long)peer); return 0; + } + uint64_t* groups = NULL; + int count = 0, allowed = 0; + if (topo_node_sqlite_get_member_channels(inst->topo_sqlite_db, inst->node_id, &groups, &count)) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM role: membership lookup failed peer=%llu", (unsigned long long)peer); return 0; + } + for (int i = 0; i < count; i++) { + if (!topo_group_chat_allowed(inst, groups[i], peer)) continue; + char sql[192], value[16]; + snprintf(sql, sizeof(sql), "SELECT adm_tags FROM \"peers_%llu\"" + " WHERE node_id=? AND source=0 AND deleted=0 AND admitted=1", (unsigned long long)groups[i]); + sqlite3_stmt* st = NULL; + int rc = sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)peer); + if (rc == SQLITE_OK) rc = sqlite3_step(st); + const char* tags = rc == SQLITE_ROW ? (const char*)sqlite3_column_text(st, 0) : NULL; + allowed = tags && !json_flat_get(tags, role, value, sizeof(value)) && !strcmp(value, "yes"); + int final = sqlite3_finalize(st); + if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM role: lookup failed peer=%llu group=%llu role=%s rc=%d final=%d", + (unsigned long long)peer, (unsigned long long)groups[i], role, rc, final); + allowed = 0; break; + } + if (allowed) { + DEBUG_TRACE(DEBUG_CATEGORY_DM, "DM role: admitted peer=%llu role=%s group=%llu", + (unsigned long long)peer, role, (unsigned long long)groups[i]); break; + } + } + u_free(groups); + return allowed; +} + /* Router владеет entry после вызова, в том числе при ошибке. */ static int dm_route_send(struct dm_state* dm, uint64_t group_id, uint64_t dst, uint8_t subcmd, const uint8_t* body, size_t body_len) { diff --git a/src/dm/dm_core.h b/src/dm/dm_core.h index f0a81d853..340d00f46 100644 --- a/src/dm/dm_core.h +++ b/src/dm/dm_core.h @@ -62,6 +62,11 @@ static inline int dm_msg_len(const uint8_t* msg, size_t avail, size_t* out_len) * 1 и *group_id при успехе; 0 при отсутствии пути/ошибке, выход не меняется. * group_id обязателен; наличие пути не гарантирует успешной последующей отправки. */ int dm_route_group(struct UTUN_INSTANCE* inst, uint64_t peer, uint64_t preferred, uint64_t* group_id); +/* Проверить role=yes у peer в любой живой общей CHAT-группе self/peer. + * Группа объявления роли не ограничивает маршрут личной передачи. peer=self проверяет локальную роль. + * Учитывает только source=0, admitted=1, deleted=0 и действующий канал; путь не требуется. + * 1=роль найдена, 0=нет роли/ошибка с диагностикой. role — непустое имя adm_tags, только uasync. */ +int dm_peer_role(struct UTUN_INSTANCE* inst, uint64_t peer, const char* role); /* Проверить точную длину msg/len, conv/author/recipient/seq, descriptor и подпись автора из nodes. * 0=корректно, -1=отказ. Нужна SQLite, владелец dm_core не требуется. * Не расшифровывает тело и не проверяет членство/роль router-отправителя: это обязанность вызывающего. */