Sync configured chat names and embed the Windows application icon
This commit is contained in:
1 parent
7140775d8d
commit
50017b7f74
8 files changed
+91
-5
No files matched your search
@@ -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).
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 |
@@ -0,0 +1,2 @@
|
||||
// Значок EXE для меню «Пуск», ярлыков и Проводника; путь подставляет CMake.
|
||||
1 ICON "@CMAKE_CURRENT_SOURCE_DIR@/resources/icons/app_icon.ico"
|
||||
Reference in new issue
Block a user