diff --git a/src/chat/chat_setting.c b/src/chat/chat_setting.c index 05bd637c..1f1364ff 100644 --- a/src/chat/chat_setting.c +++ b/src/chat/chat_setting.c @@ -86,29 +86,27 @@ typedef char chat_setting_capacity_check[CHAT_SETTING_COUNT <= CHAT_SETTING_MAX_ /* ─── state helpers ─── */ static void chat_setting_reset_defaults(struct chat_setting_state* s) { - for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) + memset(s, 0, sizeof(*s)); + for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) { s->values[i] = g_setting_defs[i].default_val; + snprintf(s->int_text[i], sizeof(s->int_text[i]), "%d", s->values[i]); + } s->initialized = 1; } static int chat_setting_find(const char* name) { + if (!name) return -1; for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) if (strcmp(g_setting_defs[i].name, name) == 0) return (int)i; return -1; } -static void chat_setting_apply(struct chat_setting_state* s) { - (void)s; - /* будет заполняться по мере появления runtime-setter'ов в модулях: - if (idx == ...) some_module_set_xxx(value); */ -} - /* ─── state-level API ─── */ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const char* value) { - if (!s || !name || !value || !*name || !*value) return -2; - - if (!s->initialized) chat_setting_reset_defaults(s); + if (!s || !name || !value || !*name) { + DEBUG_ERROR(CS_ID, "chat_setting: invalid state/name/value argument"); return -2; + } int idx = chat_setting_find(name); if (idx < 0) { @@ -122,18 +120,20 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const if (def->type == CHAT_SETTING_STRING) { if (strcmp(name, "call_video_bitrate_range_kbps") == 0) { uint32_t minimum, maximum; - if (call_video_rate_parse_range(value, &minimum, &maximum)) return -2; + if (call_video_rate_parse_range(value, &minimum, &maximum)) { + DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid bitrate range '%s'", name, value); return -2; + } } if (strlen(value) >= sizeof(s->string_val[0])) { DEBUG_ERROR(CS_ID, "chat_setting: '%s' string too long bytes=%zu limit=%zu", name, strlen(value), sizeof(s->string_val[0]) - 1); return -2; } + if (!s->initialized) chat_setting_reset_defaults(s); for (int i = 0; i < s->string_count; i++) { if (s->string_idx[i] == idx) { if (strcmp(s->string_val[i], value) == 0) return 0; snprintf(s->string_val[i], sizeof(s->string_val[i]), "%s", value); DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); - chat_setting_apply(s); return 0; } } @@ -145,7 +145,6 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const snprintf(s->string_val[s->string_count], sizeof(s->string_val[0]), "%s", value); s->string_count++; DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); - chat_setting_apply(s); return 0; } @@ -156,18 +155,19 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const } else { char* end = NULL; long lv = strtol(value, &end, 10); - if (!end || *end != '\0' || lv < def->min_val || lv > def->max_val) { + if (end == value || *end != '\0' || lv < def->min_val || lv > def->max_val) { DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid int '%s' (range %d..%d)", name, value, def->min_val, def->max_val); return -2; } v = (int)lv; } + if (!s->initialized) chat_setting_reset_defaults(s); if (s->values[idx] == v) return 0; s->values[idx] = v; + snprintf(s->int_text[idx], sizeof(s->int_text[idx]), "%d", v); DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value); - chat_setting_apply(s); return 0; } @@ -190,14 +190,13 @@ const char* chat_setting_get_state(const struct chat_setting_state* s, const cha return ""; } - snprintf((char*)s->int_buf, sizeof(s->int_buf), "%d", s->values[idx]); - return s->int_buf; + return s->int_text[idx]; } int chat_setting_get_int_state(const struct chat_setting_state* s, const char* name, int def) { if (!s || !s->initialized) return def; int idx = chat_setting_find(name); - return idx >= 0 ? s->values[idx] : def; + return idx >= 0 && g_setting_defs[idx].type != CHAT_SETTING_STRING ? s->values[idx] : def; } /* ─── instance-level API ─── */ @@ -208,12 +207,14 @@ static struct chat_setting_state* cs_state(struct UTUN_INSTANCE* inst) { int chat_setting_set(struct UTUN_INSTANCE* inst, const char* name, const char* value) { struct chat_setting_state* s = cs_state(inst); - if (!s) return -2; + if (!s) { DEBUG_ERROR(CS_ID, "chat_setting: instance is NULL"); return -2; } int result = chat_setting_set_state(s, name, value); if (!result && !strcmp(name, "incoming_call_policy")) call_set_phone_state(inst, inst->phone_state, (unsigned)chat_setting_get_int(inst, name, CALL_POLICY_PUBLISH_PHONE)); - if (!result && (!strcmp(name, "local_invites_enabled") || !strcmp(name, "local_broadcasts_enabled"))) - return chat_local_apply_settings(inst); + if (!result && (!strcmp(name, "local_invites_enabled") || !strcmp(name, "local_broadcasts_enabled")) && + chat_local_apply_settings(inst)) { + DEBUG_ERROR(CS_ID, "chat_setting: '%s' stored but LAN apply failed; retry setting to reapply", name); return -3; + } return result; } diff --git a/src/chat/chat_setting.h b/src/chat/chat_setting.h index 00078e51..d1401b54 100644 --- a/src/chat/chat_setting.h +++ b/src/chat/chat_setting.h @@ -2,12 +2,13 @@ * chat_setting.h — chat-related settings (key-value API) * * Один API для всех потребителей: парсер конфига (headless) и GUI (через bridge). - * chat_setting_set(name, value) — валидация + хранение + apply к модулям. - * Возвращает 0=OK, -1=неизвестная настройка, -2=невалидное значение. + * Нулевое состояние инициализируется значениями реестра при первом успешном set. + * Ошибка валидации не меняет состояние. Чтение const, не выделяет память и не пишет в хранилище. * * Состояние — per-instance. Конфиг-парсер заполняет struct chat_setting_state * из [chatserver] ещё до создания instance (state-уровень), затем runtime-модули - * читают через instance-уровень (chat_setting_get_int(inst, ...)). + * читают через instance-уровень (chat_setting_get_int(inst, ...)). State не синхронизирован: + * до старта им владеет парсер, runtime-вызовы выполняются в uasync-потоке instance. */ #ifndef CHAT_SETTING_H @@ -27,23 +28,34 @@ extern "C" { #define CHAT_SETTING_STRING_MAX 16 struct chat_setting_state { - int values[CHAT_SETTING_MAX_SETTINGS]; - int string_idx[CHAT_SETTING_STRING_MAX]; - char string_val[CHAT_SETTING_STRING_MAX][256]; - int string_count; - int initialized; - char int_buf[16]; /* буфер для chat_setting_get_state у int-настроек */ + int values[CHAT_SETTING_MAX_SETTINGS]; /* Числовые значения по индексу реестра. */ + int string_idx[CHAT_SETTING_STRING_MAX]; /* Индексы занятых строковых настроек. */ + char string_val[CHAT_SETTING_STRING_MAX][256]; /* NUL-строки, не более255 байт. */ + int string_count; /* Число занятых string_val. */ + int initialized; /* 0 до первого успешного set. */ + char int_text[CHAT_SETTING_MAX_SETTINGS][16]; /* Текст числа, обновляемый только setter. */ }; /* ─── state-уровень (конфиг-парсер, до создания instance) ─── */ +/* s изначально обнулён. name/value заимствованы до возврата; значение копируется. + * 0 — сохранено, -1 — неизвестное имя, -2 — неверные аргументы/значение/нет места. + * Числа — decimal в диапазоне реестра; bool — 0/1,no/yes,false/true; пути допускают пустую строку. */ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const char* value); +/* Заимствованная NUL-строка до изменения этой настройки/уничтожения s; другие getters её не меняют. + * NULL — неизвестное имя/NULL s/неинициализированное состояние. Для bitrate range даже до set — "300:4000". + * Незаполненный строковый путь после инициализации — "". */ const char* chat_setting_get_state(const struct chat_setting_state* s, const char* name); +/* Число/bool либо def для неизвестного/строкового имени, NULL s и неинициализированного состояния. */ int chat_setting_get_int_state(const struct chat_setting_state* s, const char* name, int def); /* ─── instance-уровень (runtime) ─── */ +/* Те же коды; NULL inst — -2. Большинство потребителей читают значение при следующем обращении/старте. + * incoming_call_policy сразу обновляет телефонную политику, local_* — LAN permissions. + * -3 — значение сохранено, применение LAN не удалось; повтор set того же значения повторяет apply. */ int chat_setting_set(struct UTUN_INSTANCE* inst, const char* name, const char* value); +/* Instance-варианты чтения с теми же lifetime/default; NULL inst даёт NULL/def. */ const char* chat_setting_get(struct UTUN_INSTANCE* inst, const char* name); int chat_setting_get_int(struct UTUN_INSTANCE* inst, const char* name, int def);