diff --git a/doc/member_files.txt b/doc/member_files.txt index d70d7b46..fba60dbe 100644 --- a/doc/member_files.txt +++ b/doc/member_files.txt @@ -245,6 +245,9 @@ member_files_transfer_start(request, callback, arg) копирует request и отдельный mf_transfer* на каждый файл. upload задаёт направление, group/server/owner выбираются для каждой операции. id=0 выбирает случайный ID; id в callback — фактический. NULL означает отказ запуска без callback; причина пишется в лог. +max_bytes=0 оставляет прежний лимит; ненулевое значение ограничивает размер файла. +Upload проверяет размер источника до хеширования, download — ответ STAT до открытия +локального temp. STREAM привязан к проверенным размеру/version; превышение — INVALID_RANGE. callback(arg, mf_transfer_progress*) получает id/done/total/status/finished. Прогресс заимствован до возврата callback. done для отправителя означает байты, @@ -259,6 +262,65 @@ Stop сервиса/группы завершает все принадлежа тот же API и превращают callback в существующие события прогресса; Qt не делает I/O. Один локальный файл назначения не может одновременно принадлежать двум download. +Аватарки пользователей +--------------------- + +Относительный путь avatars/.png находится внутри обычного дерева UID: +<корень>///avatars/.png. Дополнительного UID в API/пути нет. +Аватарка по умолчанию копируется в каждую группу, где выбранный сервер имеет +подписанную storage-роль. Ссылка каждого канала публикуется только после COMMIT +соответствующей копии; отсутствующий сервер в другом канале не создаёт битую ссылку. +Новое членство обнаруживается через снимки участников и повторяет публикацию. +Между серверами автоматического копирования нет. + +userinfo содержит строковые avatar_server (16 hex цифр) и avatar_file (64 hex SHA256 + .png). +Оба поля отсутствуют при удалённой аватарке. Профиль подписан ключом участника; +смена имени/адресов сохраняет остальные плоские JSON-атрибуты. userinfo ограничен +1024 байтами UTF-8. Merkle, JOIN_INFO_RESP, CHANNEL_INVITE и JOIN_REQUEST используют +длину userinfo:u16 BE вместо прежнего u8. Bootstrap передаёт существующий подписанный +профиль инвайтера, не создавая более свежую запись только с именем. Обе стороны +нужно обновить: обратной совместимости нет. + +Снимок участника CHAT_MEMBER_DISPLAY_SIZE теперь 157 байт: прежние 79 байт, +avatar_server:8LE, avatar_file:69 (с NUL), storage_role:1. storage_role обозначает +подписанную роль сервера, независимо от существующей колонки storage — сетевого +приоритета, который может быть 0 у сервера за NAT. Потребители используют общий +chat_member.h; локальное изменение профиля также вызывает MEMBER_UPDATED. + +В Settings → Profile доступны Upload avatar, выбор storage-сервера, предпросмотр +и Remove avatar. Изменения применяются сразу, независимо от Save других настроек. +Worker проверяет размер входного изображения (до 16 мегапикселей), обрезает центральный +квадрат и сохраняет статический PNG 256×256. Файл ограничен 1 МиБ. Сервер проверяет +каноническое имя/SHA256, размер и запрещает замену под прежним именем и rename +в/из avatars. Файлы и каталог входят в обычные инкрементальные квоты/счётчики. +Прежние версии сохраняются на сервере; владелец может удалить их через файловый браузер. + +AvatarManager общий для списка участников и сообщений, максимум две операции +одновременно, одинаковые хеши не скачиваются параллельно. Изменение своего профиля +имеет приоритет перед фоновыми загрузками. Для DM используются ссылки из общих +каналов; при отсутствии доступной ссылки остаются инициалы. Онлайн-статус списка +отображается отдельной точкой поверх фотографии. Поздний результат старой ссылки +пополняет кеш, но не заменяет картинку новой ссылки. + +Кеш аккаунта: /avatar_cache//.png; без db_path +используется Qt AppLocalDataLocation. Проверяются SHA256 и квадратный PNG до 512×512; +превышение/битая картинка не публикуются. Публикация кеша атомарна через QSaveFile, +незавершённые download/part очищаются при старте. Готовые файлы не требуют TTL/STAT: +смена содержимого меняет имя. Дисковый LRU — 100 МиБ, кеш QImage — 32 МиБ; +собственная аватарка и источники незавершённых upload сохраняются при вытеснении. +Дисковое I/O и декодирование выполняются вне GUI/uasync. Повторы неудачных загрузок: +5/10/20/40/80/160 секунд; новая ссылка сбрасывает задержку. RTT/online не меняют ссылку +и не вызывают повторной загрузки. Желаемая собственная ссылка/удаление сохраняются +атомарно одной строкой ui_state.avatar_default, незавершённая публикация повторяется +после запуска при наличии локального источника. + +test_avatars проверяет нормализацию, хеш/размеры, очистку temp, настоящее storage +в двух каналах, публикацию, сохранение при смене имени, DM-кеш, restart без сети, +автоматический download после потери кеша и удаление ссылок. test_member_adapter +проверяет подписанные профили 191/255/256/512/1023/1024 байт вместе с adm_tags до 8192. +Категория media=debug показывает discovery, cache hit/eviction, направление, +group/owner/server/hash, ошибки проверки и retry; media=info — передачи/публикации. + Проверки и диагностика --------------------- diff --git a/src/chat/chat_join.h b/src/chat/chat_join.h index c9423ba8..4f3396c1 100644 --- a/src/chat/chat_join.h +++ b/src/chat/chat_join.h @@ -109,7 +109,7 @@ struct join_member_data { uint64_t join_ts; /* время подписи вступления, Unix seconds */ const uint8_t* update_sig; /* 64, может быть NULL */ uint64_t update_ts; /* версия подписанного блока участника */ - char userinfo[1025]; /* JSON до MS_USERINFO_MAX байт, null-terminated */ + char userinfo[MS_USERINFO_MAX + 1]; /* JSON, null-terminated */ }; /* Сериализовать member-блок (без join_key). Возвращает 0 и *out_len, -1 если не влез. */