173 lines
14 KiB
C
173 lines
14 KiB
C
/*
|
||
* chat_member.h — единая структура мембера для отображения в GUI
|
||
*
|
||
* Используется chatgui (desktop), headless CLI, Android.
|
||
* Бинарные снимки для GUI/JNI формирует chat_core_get_member_list() в uasync-потоке.
|
||
* Расширенная JSON-интроспекция с ключами/адресами описана отдельно в chat_core.h.
|
||
* Снимок содержит свойства участника и состояние маршрута именно его CHAT-группы.
|
||
* Все вызовы — в uasync-потоке. ch_id — канонический десятичный ID1..INT64_MAX.
|
||
* В список входят self,placeholders и deleted;deleted показывается offline,self обычно online.
|
||
*
|
||
* Формат на проводе (ручная сериализация, фиксированный размер 164 байта):
|
||
* [0-7] node_id uint64_t LE
|
||
* [8] online uint8_t
|
||
* [9] is_self uint8_t
|
||
* [10] flags uint8_t 0x01=supernode 0x02=admin 0x04=moder 0x08=deleted 0x10=radio 0x20=local radio mute
|
||
* 0x40=phone status known, 0x80=busy on a call (live BGP hint)
|
||
* [11-12] rtt uint16_t LE (0,1 мс выбранного живого пути, 0xFFFF=нет данных)
|
||
* [13] nat_type uint8_t ADDR_TYPE_*
|
||
* [14] storage uint8_t
|
||
* [15-78] name char[64] local_nick если задан, иначе node name. \0-padded
|
||
* [79-86] avatar_server uint64_t LE; 0 = нет аватарки
|
||
* [87-156] avatar_file char[70], sha256.json с NUL
|
||
* [157] storage_role uint8_t, подписанная роль, независимо от сетевого приоритета storage
|
||
* [158] verified uint8_t, административный тег verified=yes
|
||
* [159] node_type uint8_t, вычисленный тип узла из peers
|
||
* [160] conn_presence uint8_t, известные виды подключения NCONN_*
|
||
* [161] conn_up uint8_t, работающие виды подключения NCONN_*
|
||
* [162-163] intermediaries uint16_t LE, число посредников выбранного пути
|
||
*/
|
||
|
||
#ifndef CHAT_MEMBER_H
|
||
#define CHAT_MEMBER_H
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
|
||
struct UTUN_INSTANCE;
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#define CHAT_MEMBER_FLAG_SUPERNODE 0x01
|
||
#define CHAT_MEMBER_FLAG_ADMIN 0x02
|
||
#define CHAT_MEMBER_FLAG_MODER 0x04
|
||
#define CHAT_MEMBER_FLAG_DELETED 0x08
|
||
#define CHAT_MEMBER_FLAG_RADIO 0x10
|
||
#define CHAT_MEMBER_FLAG_RADIO_MUTED 0x20 /* локальное отключение голоса участника */
|
||
#define CHAT_MEMBER_FLAG_PHONE_KNOWN 0x40 /* опубликовано состояние телефонной занятости */
|
||
#define CHAT_MEMBER_FLAG_PHONE_BUSY 0x80 /* занят звонком; цвет трубки в интерфейсе */
|
||
|
||
#define CHAT_MEMBER_DISPLAY_SIZE 164
|
||
|
||
/* Снять полный список:0 — успех,-1 — ошибка/более65535 записей. Обязательные out/count сбрасываются в NULL/0.
|
||
* out — u_malloc-массив count*CHAT_MEMBER_DISPLAY_SIZE байт,освобождает caller через u_free;пустой список — NULL/0.
|
||
* Порядок:online первыми,затем unsigned node_id по возрастанию. SQL/память не дают частичный успешный результат. */
|
||
int chat_core_get_member_list(struct UTUN_INSTANCE* inst, const char* ch_id, uint8_t** out, int* count);
|
||
|
||
/* Снять одного участника:0/-1 (ошибка/нет строки). out — обязательный буфер CHAT_MEMBER_DISPLAY_SIZE байт;
|
||
* при отказе не меняется. Формирование полей совпадает с полным списком. */
|
||
int chat_core_get_single_member(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t node_id, uint8_t* out);
|
||
|
||
/* Live-онлайн: маршрутизатор располагает живым путём в группе канала (self всегда онлайн).
|
||
Не читает nodes.online и не проверяет членство/deleted;это отдельная проверка транспорта,self всегда1. NULL/неверный ID дают0. */
|
||
int chat_core_member_online(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t node_id);
|
||
|
||
/* Внутренний снимок:поля далее кодируются вручную,struct-layout в wire не передаётся. */
|
||
struct chat_member_display {
|
||
uint64_t node_id; /* ID участника. */
|
||
uint8_t online; /* Живой путь/self,сброшен для deleted. */
|
||
uint8_t is_self; /* Совпадение с node_id instance. */
|
||
uint8_t flags; /* CHAT_MEMBER_FLAG_*;offline не несёт phone known/busy. */
|
||
uint16_t rtt; /* Суммарный RTT выбранного пути,0.1мс;UINT16_MAX — неизвестен/насыщен. */
|
||
uint8_t nat_type; /* Агрегированный ADDR_TYPE_* адресов узла;255 — адресов нет. */
|
||
uint8_t storage; /* Локальный приоритет хранения,отдельно от подписанной роли. */
|
||
char name[64]; /* Local nick,имя nodes или0xNNNN;до63 байт+NUL. */
|
||
uint64_t avatar_server; /* Сервер файлового аватара,0 — ссылки нет. */
|
||
char avatar_file[70];/* Проверенный sha256.json+NUL. */
|
||
uint8_t storage_role; /* source=0,не deleted,adm_tags.storage=yes и64-байтовая подпись owner-блока;доверие записи — у member_sync. */
|
||
uint8_t verified; /* административное подтверждение участника */
|
||
uint8_t node_type; /* классификация узла, не состояние маршрута */
|
||
uint8_t conn_presence; /* известные виды подключения NCONN_* */
|
||
uint8_t conn_up; /* работающие виды подключения NCONN_* */
|
||
uint16_t intermediaries; /* число посредников выбранного живого пути */
|
||
};
|
||
|
||
/* Копирование фиксированного wire164:src/dst обязательны,caller обеспечивает размеры.
|
||
* Serialize завершает/дополняет NUL поля name/avatar_file;NULL только логируется. */
|
||
void chat_core_serialize_member(const struct chat_member_display* src, uint8_t* dst);
|
||
/* Deserialize читает ровно164 доступных байта;0/-1 при NULL,длину/семантику полей проверяет caller. */
|
||
int chat_core_deserialize_member(const uint8_t* src, struct chat_member_display* dst);
|
||
|
||
/* Трамплин для gui_bridge, только uasync: принимает собственный u_malloc-блок arg и освобождает его.
|
||
* ch_id — канонический ID+NUL. Успех: CHAT_EVT_MEMBER_LIST с uint16 native count (0..65535), записи wire164 LE.
|
||
* Непредставимый count/ошибка чата/памяти логируются, события нет; ложный/усечённый снимок не отправляется. */
|
||
struct chat_member_list_req { struct UTUN_INSTANCE* inst; char ch_id[64]; };
|
||
void chat_core_request_member_list_trampoline(void* arg);
|
||
|
||
/* Владение/поток/аргумент как у list trampoline. Для каждого участника — один полный
|
||
* CHAT_EVT_MEMBER_UPDATED, включая остальные live-поля. Пустой список не даёт событий.
|
||
* Это снимок актуального состояния без запуска новых сетевых измерений; inst заимствован до выполнения. */
|
||
void chat_core_request_member_rtt_trampoline(void* arg);
|
||
|
||
/* Локальный переключатель рации другого участника:0 — committed,-1 — отказ,чужая транзакция запрещена.
|
||
* Снимок готовится до изменения;после radio commit обновлённый флаг приходит MEMBER_UPDATED без нового SQL-чтения.
|
||
* Ошибка сохраняет настройку/кэш/звук и не выдаёт успешное событие. */
|
||
int chat_core_toggle_member_radio_mute(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t node_id);
|
||
struct chat_member_radio_mute_req { struct UTUN_INSTANCE* inst; char ch_id[64]; uint64_t node_id; };
|
||
/* Забирает u_malloc-запрос;inst заимствован. Ответ — RADIO_MUTE_RESULT(group,node,success),включая отказ. */
|
||
void chat_core_toggle_member_radio_mute_trampoline(void* arg);
|
||
|
||
/* Подписки свойств/маршрутов → MEMBER_UPDATED; вызываются в uasync при init/destroy чата.
|
||
* Свойства публикуются синхронно. Маршруты объединяются по (канал,узел) до следующего posted callback:
|
||
* UI получает свежий снимок вне topo dispatch и может удалить канал, не повредив его group/node.
|
||
* Ожидающие route-события отменяются при destroy;ошибка памяти логируется без частичного события. */
|
||
/* Установить обе подписки0/-1;при отказе частичная регистрация снята. Вызывается один раз из chat_core_init. */
|
||
int chat_member_init(struct UTUN_INSTANCE* inst);
|
||
void chat_member_destroy(struct UTUN_INSTANCE* inst);
|
||
|
||
/* Дополнительная диагностика карточки; online/RTT берутся только из chat_member_display.
|
||
* Запрос выделяет GUI через u_calloc и передаёт в uasync; trampoline освобождает его.
|
||
* Ответ CHAT_EVT_MEMBER_DETAIL всегда повторяет request_id/group_id/node_id, включая ошибки. */
|
||
struct chat_member_detail_req {
|
||
struct UTUN_INSTANCE* inst; /* ядро, в uasync которого выполняется сбор */
|
||
uint64_t group_id; /* конкретная CHAT-группа */
|
||
uint64_t node_id; /* выбранный участник */
|
||
uint64_t request_id; /* ненулевой ID запроса, не повторяется при смене выбора */
|
||
};
|
||
|
||
/* Wire LE: request:8, group:8, node:8, result:1, transport_flags:1, own_tcp:2,
|
||
* hop_count:1, link_count:1, sock_count:1; далее hops:8*, links:94*, socks:48*.
|
||
* transport_flags: bit0=conn_present, bit1=links_up, bit2=initialized; не доступность в группе.
|
||
* hops — список выбранного пути от назначения к первому посреднику; у offline/self пустой.
|
||
* Ошибка содержит только заголовок;INVALID допускает нулевые поля неверного исходного запроса.
|
||
* Счётчики own_tcp и ссылок сокета насыщаются на65535;списки ограничены16 элементами каждый.
|
||
* link: direction:1 (0=unknown,1=out,2=in),state:1,status:1,has_ip:1,local_family:1,local_addr:16,local_port:2,
|
||
* remote_family:1,remote_addr:16,remote_port:2,socket_name:20,protocol:32. Не-IP не имеет адресов/портов.
|
||
* sock: id:4,port:2,links_up:2,links_down:2,ifname:38. Строки завершаются NUL. */
|
||
#define CHAT_MEMBER_DETAIL_HEADER_SIZE 31
|
||
#define CHAT_MEMBER_DETAIL_MAX_HOPS 16
|
||
#define CHAT_MEMBER_DETAIL_MAX_LINKS 16
|
||
#define CHAT_MEMBER_DETAIL_MAX_SOCKS 16
|
||
#define CHAT_MEMBER_DETAIL_LINK_SIZE 94
|
||
#define CHAT_MEMBER_DETAIL_SOCK_SIZE 48
|
||
#define CHAT_MEMBER_DETAIL_MAX_SIZE (CHAT_MEMBER_DETAIL_HEADER_SIZE + 8 * CHAT_MEMBER_DETAIL_MAX_HOPS + \
|
||
CHAT_MEMBER_DETAIL_LINK_SIZE * CHAT_MEMBER_DETAIL_MAX_LINKS + CHAT_MEMBER_DETAIL_SOCK_SIZE * CHAT_MEMBER_DETAIL_MAX_SOCKS)
|
||
|
||
enum chat_member_detail_result {
|
||
CHAT_MEMBER_DETAIL_OK = 0,
|
||
CHAT_MEMBER_DETAIL_UNAVAILABLE = 1, /* чат/группа остановлены */
|
||
CHAT_MEMBER_DETAIL_NOT_MEMBER = 2, /* запись участника отсутствует */
|
||
CHAT_MEMBER_DETAIL_INVALID = 3, /* неверные аргументы запроса */
|
||
CHAT_MEMBER_DETAIL_ERROR = 4 /* ошибка чтения данных участника */
|
||
};
|
||
|
||
/* Проверенный view заимствует массивы из входного пакета; живёт не дольше data. */
|
||
struct chat_member_detail_view {
|
||
uint64_t request_id, group_id, node_id; /* адресат ответа */
|
||
uint8_t result, transport_flags; /* результат и состояние физического транспорта */
|
||
uint16_t own_tcp_active; /* TCP-соединения self */
|
||
uint8_t hop_count, link_count, sock_count; /* длины массивов */
|
||
const uint8_t *hops, *links, *socks; /* неизменяемые wire-массивы */
|
||
};
|
||
|
||
/* Проверить весь пакет до применения: 0=успех, -1=ошибка с логом; out при ошибке не меняется. */
|
||
int chat_core_parse_member_detail(const uint8_t* data, size_t len, struct chat_member_detail_view* out);
|
||
void chat_core_collect_member_detail_trampoline(void* arg);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
#endif /* CHAT_MEMBER_H */
|