Sync configured chat names and embed the Windows application icon

This commit is contained in:
evgeny committed 2026-10-19 09:54:42 +02:00
1 parent 7140775d8d
commit 50017b7f74
8 files changed
+91 -5

No files matched your search

+6
View File
@@ -4,6 +4,12 @@
## Текущие задачи
[ ] **Разобрать нестабильное ожидание ACK в test_local_transport** — 19.10.2026:
Общий check.sh: 161/162, chat_adapter упал на строке 173 при ожидании LOCAL_DELIVERY_FORBIDDEN для request=508.
В логе получатель дважды отклонил приглашение и отправил ACK; отправитель до проверки получил только late ACK request=507.
Отдельный повтор прошёл. Причина не установлена; проверить доставку UDP и шаги ускоренных часов под параллельной нагрузкой.
Логи: /tmp/config-name-local-transport-failure.log, /tmp/config-name-local-transport-retry.log.
[ ] **Разобрать нестабильную проверку аудиопауз desktop-трансляции** — 17.10.2026:
При проверке зума test_stream_view::realH264RenderingAndClose получил silenceGaps=3 вместо gapsBefore=2.
В логе есть desktop audio gap cause=pcm_short, got=96/480; видео продолжает выводиться (50 кадров к stop).
+1 -1
View File
@@ -145,7 +145,7 @@ struct update_my_name_arg { struct UTUN_INSTANCE* inst; char name[128]; };
void chat_core_update_my_name_trampoline(void* arg);
/* Обновить адреса topo_node и собственные member-записи каналов. */
void chat_core_sync_my_addresses(struct UTUN_INSTANCE* inst);
/* Обновить своё userinfo во всех каналах: увеличить update_ts, подписать и запустить sync.
/* При изменении своего userinfo в каналах увеличить update_ts, подписать и запустить sync.
* Адреса обновляются отдельно через topo_node; подпись member-блока их не содержит. */
void chat_core_update_my_member(struct UTUN_INSTANCE* inst);
+8 -4
View File
@@ -143,7 +143,7 @@ static int profile_update_channel(struct UTUN_INSTANCE* inst, const char* ch, in
if (profile_pair(&profile, "name", inst->name) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "profile: display name exceeds profile limit channel=%s", ch); return -1;
}
char query[256]; snprintf(query, sizeof(query), "SELECT update_ts,userinfo FROM \"%s\" WHERE node_id=?", tbl);
char query[256]; snprintf(query, sizeof(query), "SELECT update_ts,userinfo,update_sig FROM \"%s\" WHERE node_id=?", tbl);
sqlite3_stmt* st = NULL;
if (sqlite3_prepare_v2(cc->db, query, -1, &st, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "profile: query failed channel=%s: %s", ch, sqlite3_errmsg(cc->db)); return -1;
@@ -154,6 +154,8 @@ static int profile_update_channel(struct UTUN_INSTANCE* inst, const char* ch, in
sqlite3_finalize(st); return -1;
}
uint64_t local_uts = (uint64_t)sqlite3_column_int64(st, 0);
int signed_profile = local_uts && sqlite3_column_bytes(st, 2) == 64
&& memcmp(sqlite3_column_blob(st, 2), (uint8_t[64]){0}, 64);
const char* old = (const char*)sqlite3_column_text(st, 1);
char before[MS_USERINFO_MAX + 1] = "";
if (old && strlen(old) > MS_USERINFO_MAX) {
@@ -182,8 +184,9 @@ static int profile_update_channel(struct UTUN_INSTANCE* inst, const char* ch, in
DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "profile: attributes exceed profile limit channel=%s", ch); return -1;
}
profile.text[profile.used++] = '}'; profile.text[profile.used] = 0;
if (option_count && !strcmp(before, profile.text)) {
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT, "profile: options unchanged channel=%s", ch); return 0;
if (signed_profile && !strcmp(before, profile.text)) {
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT, "profile: unchanged channel=%s name=%s ts=%llu", ch, inst->name,
(unsigned long long)local_uts); return 0;
}
if (local_uts >= INT64_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "profile: version exhausted channel=%s", ch); return -1; }
time_t seconds = ntp_time_get_seconds(inst);
@@ -198,7 +201,8 @@ static int profile_update_channel(struct UTUN_INSTANCE* inst, const char* ch, in
int rc = member_sync_put(inst, ch, myid, inst->my_keys.public_key, inst->my_ed25519_pubkey, join_sig, join_ts,
update_sig, update_ts, profile.text, NULL, NULL, 0, 0, NULL);
if (rc < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "profile: update failed channel=%s result=%d", ch, rc); return -1; }
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: update my member ch=%s ts=%llu", CC_ID, ch, (unsigned long long)update_ts);
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: update my member ch=%s name=%s ts=%llu", CC_ID, ch, inst->name,
(unsigned long long)update_ts);
return 0;
}
+2
View File
@@ -791,6 +791,8 @@ int chat_service_start(struct UTUN_INSTANCE* instance) {
if (db_sync_enable(instance) < 0) return -1;
topo_group_invite_init(instance);
if (chat_core_init(instance, db_file) < 0 || chat_sync_init(instance) < 0 || stream_service_init(instance) < 0) goto fail;
/* Имя из конфига публикуем подписанным member-блоком после восстановления Merkle. */
if (instance->config->global.chatserver_enabled) chat_core_update_my_member(instance);
instance->media_async = media_async_create();
if (!instance->media_async || media_delivery_init(instance) < 0) goto fail;
if (chat_attachment_init(instance) < 0) goto fail;
+62
View File
@@ -14,8 +14,11 @@
#include "node_conn_direct.h"
#include "chat/chat_core.h"
#include "chat/db_sync.h"
#include "chat/member_sync.h"
#include "media_async/media_async.h"
#include "../lib/debug_config.h"
#include "../lib/json_flat.h"
#include "test_utils.h"
struct job { int worked, done, error; };
static void work(void* arg) { ((struct job*)arg)->worked = 1; }
@@ -57,10 +60,69 @@ static struct TOPO_GROUP* chat_group(struct UTUN_INSTANCE* inst) {
return NULL;
}
/* Полный restart с той же БД: новое имя подписано во всех каналах, повторный старт не меняет версии. */
static void check_config_name(struct UASYNC* ua) {
char directory[512] = "/tmp/utun-config-name-XXXXXX";
assert(test_mkdtemp(directory) == 0);
char channels[2][64], config[2048];
uint8_t previous_hash[2][32], previous_signature[2][64];
uint64_t previous_version[2] = {0};
const char* names[] = {"Old server", "New \"server\"", "New \"server\"", "Other name"};
for (unsigned run = 0; run < 4; run++) {
snprintf(config, sizeof(config), "[global]\nmy_node_name=%s\ndb_path=%s\n"
"my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n"
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n"
"[server: udp]\naddr=127.0.0.1:0\ntype=public\n%s", names[run], directory,
run < 3 ? "[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\n" : "");
struct UTUN_INSTANCE* inst = utun_instance_create_from_str(ua, config); assert(inst);
assert(utun_core_start(inst) == 0 && chat_service_start(inst) == 0);
if (!run) {
chat_core_create_channel_auto(inst, "config name 1");
chat_core_create_channel_auto(inst, "config name 2");
unsigned count = 0;
for (struct ll_entry* e = inst->topo_groups->group_list->head; e; e = e->next) {
struct TOPO_GROUP* group = (struct TOPO_GROUP*)e;
assert(group->group_type == TOPO_GROUP_TYPE_CHAT && count < 2);
snprintf(channels[count], sizeof(channels[count]), "%s", group->channel_id);
struct chat_profile_option option = {"extra", "retained"};
assert(chat_core_set_my_profile_options(inst, channels[count], &option, 1) == 0);
count++;
}
assert(count == 2 && strcmp(channels[0], channels[1]));
}
for (unsigned i = 0; i < 2; i++) {
char profile[MS_USERINFO_MAX + 1], value[128];
uint8_t signature[64], join[64], hash[32], message[MS_USERINFO_MAX + 73];
uint64_t version, join_ts;
assert(chat_core_get_my_profile(inst, channels[i], profile, sizeof(profile), signature, &version) == 0);
assert(json_flat_get(profile, "name", value, sizeof(value)) == 0 && !strcmp(value, names[run == 3 ? 2 : run]));
assert(json_flat_get(profile, "extra", value, sizeof(value)) == 0 && !strcmp(value, "retained"));
assert(topo_node_sqlite_member_get_join(inst->topo_sqlite_db, channels[i], inst->node_id, join, &join_ts) == 0);
int length = member_sync_build_update_msg(join, version, profile, message, sizeof(message)); assert(length > 0);
assert(sc_ed25519_verify(inst->my_ed25519_pubkey, message, length, signature) == SC_OK);
assert(merkle_sync_read_hash(inst, MT_DATA_MEMBERS, channels[i], 0, 0, hash) == 0);
if (run == 1) {
assert(version > previous_version[i] && memcmp(signature, previous_signature[i], 64));
assert(memcmp(hash, previous_hash[i], 32));
} else if (run > 1) {
assert(version == previous_version[i] && !memcmp(signature, previous_signature[i], 64));
assert(!memcmp(hash, previous_hash[i], 32));
}
previous_version[i] = version;
memcpy(previous_hash[i], hash, 32); memcpy(previous_signature[i], signature, 64);
}
utun_instance_destroy(inst); uasync_poll(ua, 0);
}
char path[1024]; snprintf(path, sizeof(path), "%s/chats.db", directory); test_unlink(path); test_rmdir(directory);
DEBUG_INFO(DEBUG_CATEGORY_CHAT,
"[PASS] config name: restart updates signed profiles and Merkle in all channels; unchanged/no chatserver keeps versions");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
struct UASYNC* ua = uasync_create(); assert(ua);
check_client_activity(ua);
check_config_name(ua);
struct UTUN_INSTANCE* inst = utun_instance_create_from_str(ua,
"[global]\nmy_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n"
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n"
+10
View File
@@ -341,6 +341,16 @@ add_executable(vibechat WIN32
)
add_dependencies(vibechat chatgui_translations)
# Проводник и ярлыки Windows читают значок из EXE до запуска Qt.
if(WIN32)
enable_language(RC)
configure_file(resources/vibechat.rc.in "${CMAKE_CURRENT_BINARY_DIR}/vibechat.rc" @ONLY)
target_sources(vibechat PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/vibechat.rc")
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/vibechat.rc" PROPERTIES
OBJECT_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/resources/icons/app_icon.ico")
message(STATUS "Windows executable icon: resources/icons/app_icon.ico")
endif()
target_include_directories(vibechat PRIVATE ${CMAKE_SOURCE_DIR}/../../lib ${CMAKE_SOURCE_DIR}/../../src ${CMAKE_SOURCE_DIR}/db ${CMAKE_SOURCE_DIR}/../../lib/libopus/include ${FFMPEG_INCLUDE_DIRS})
target_compile_definitions(vibechat PRIVATE SQLITE_THREADSAFE=1 USE_SQLITE VIBECHAT_VERSION="${PROJECT_VERSION}")
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+2
View File
@@ -0,0 +1,2 @@
// Значок EXE для меню «Пуск», ярлыков и Проводника; путь подставляет CMake.
1 ICON "@CMAKE_CURRENT_SOURCE_DIR@/resources/icons/app_icon.ico"