mirror of
https://github.com/by-sonic/tglock.git
synced 2026-10-08 02:05:59 +03:00
fix: address routing, media, worker and platform issues
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# Android (experimental)
|
||||
|
||||
Issue #9 is implemented as a Tauri Android application sharing the current Rust
|
||||
proxy engine and UI with the desktop application. The old Android PR scaffold is
|
||||
retained, but its stale engine and desktop code are not imported.
|
||||
|
||||
## Install a test APK
|
||||
|
||||
Open this PR's **Android APK** check, then the workflow run's **Artifacts** section.
|
||||
Download `tglock-android-arm64-debug`, unzip it, and install the `.apk` on an
|
||||
ARM64 Android 7.0+ device. GitHub requires signing in to download CI artifacts.
|
||||
No compiler or Android Studio is needed on the phone. The artifact expires after
|
||||
14 days; maintainers can rerun the workflow to create a fresh build.
|
||||
|
||||
This is an automatically debug-signed test build, not a Play Store release.
|
||||
Different CI runs can use different debug signing keys: if Android rejects an
|
||||
update because the signatures differ, uninstall the previous test build first.
|
||||
Uninstalling deletes settings and changes the proxy secret, so reconnect Telegram
|
||||
with the new link. A future production release needs a stable signing key.
|
||||
|
||||
1. Open TGLock and press **Включить защиту**.
|
||||
2. Accept the proxy in Telegram when prompted. If opening Telegram fails, return
|
||||
to TGLock and use **Открыть Telegram** or **Скопировать ссылку**.
|
||||
3. Keep LAN access off when Telegram runs on this same phone (`127.0.0.1`).
|
||||
4. To stop, return through the ongoing notification and press **Выключить**.
|
||||
|
||||
## Lifecycle and limitations
|
||||
|
||||
The native foreground service starts only for an explicitly started proxy and
|
||||
stops when its Rust accept loop finishes, including a normal Stop action. It is
|
||||
not stopped by Activity destruction or rotation. Android 14+ declares the
|
||||
`specialUse` service type, its dedicated permission, and a subtype describing the
|
||||
user-controlled local proxy. Notification permission denial does not prevent the
|
||||
foreground service from running; Android still exposes it in its task manager.
|
||||
|
||||
The service uses `START_NOT_STICKY`: after Android kills the process or the user
|
||||
force-stops it, it does not restart a notification without a Rust engine. Open
|
||||
TGLock and enable protection again. No boot receiver or automatic background
|
||||
restart is installed. Vendor battery management and network changes may still
|
||||
interrupt connections. The app does not claim to be a device-wide VPN.
|
||||
|
||||
The proxy secret lives in the app's private configuration directory. Desktop
|
||||
installs migrate an existing valid legacy secret, preserving saved Telegram
|
||||
links. The link-copy control intentionally contains this secret; the public
|
||||
report-copy control includes only counters, port, route and mode.
|
||||
|
||||
## Build and verification
|
||||
|
||||
CI uses Java 17, Android SDK 36, NDK 28, Rust 1.88, and the locked npm/Rust
|
||||
manifests. `npm run tauri -- android build --debug --apk --target aarch64 --ci`
|
||||
bundles the frontend inside a signed APK; no development web server is required.
|
||||
CI verifies the signature and the presence of the ARM64 Rust library.
|
||||
|
||||
Checked-in `gen/android` contains the native source and Gradle wrapper. Tauri's
|
||||
machine-specific generated glue, SDK paths, native build output and signing files
|
||||
remain ignored. Run the same build command locally after installing Tauri's
|
||||
[Android prerequisites](https://v2.tauri.app/start/prerequisites/#android).
|
||||
|
||||
No physical-phone or Telegram end-to-end test has been performed by this change.
|
||||
Before promoting it beyond an experimental APK, test Android 13 notification
|
||||
permission grant/denial, Android 14+ service startup, Start/Stop/restart, switching
|
||||
to Telegram for at least 10 minutes, rotation, Activity recreation, process death,
|
||||
Wi-Fi/mobile-data handover, and restoration with the same persisted secret.
|
||||
|
||||
Native bridging follows [Tauri mobile plugins](https://v2.tauri.app/develop/plugins/develop-mobile/)
|
||||
and uses [Tauri opener](https://v2.tauri.app/plugin/opener/) for `tg://` links.
|
||||
+21
-9
@@ -201,22 +201,32 @@ Ping приходит в читающую половину, а отвечает
|
||||
|
||||
1. сохранённый успешный маршрут;
|
||||
2. точный Telegram IP с `kwsN` или `kwsN-1` в TLS SNI и WebSocket Host;
|
||||
3. дополнительный Telegram IP, если он определён;
|
||||
4. системный DNS;
|
||||
5. явно настроенный пользователем Cloudflare Worker.
|
||||
3. явно настроенный пользователем Cloudflare Worker;
|
||||
4. дополнительные Telegram IP и варианты хоста;
|
||||
5. системный DNS (кроме CDN DC203).
|
||||
|
||||
Поддерживаются DC1–5 и media/CDN DC203. DC203 использует WebSocket-host DC2,
|
||||
но подключается к собственному IP.
|
||||
но подключается к собственному IP. DNS fallback на DC2 для DC203 запрещён:
|
||||
успешный WebSocket handshake с другим DC не доставляет CDN-запрос в нужный
|
||||
датацентр. Worker также получает именно IP DC203.
|
||||
|
||||
После ошибки маршрут получает exponential cooldown от 30 секунд до 30 минут.
|
||||
Успешный маршрут становится первым для следующего соединения того же DC и
|
||||
типа трафика.
|
||||
типа трафика. Попытки запускаются с интервалом 250 мс, не больше трёх
|
||||
одновременно на соединение; остальные отменяются после первого успеха.
|
||||
Если все маршруты на паузе, возвращается причина и время до повторной
|
||||
попытки. Новое подключение клиента не обходит cooldown. Ошибка upstream
|
||||
после handshake также снимает предпочтение маршрута и добавляет cooldown.
|
||||
|
||||
## TLS policy
|
||||
|
||||
Проверка сертификатов и hostname никогда не отключается. При подключении к
|
||||
заданному Telegram IP TCP destination отделён от URI host: TLS продолжает
|
||||
проверять сертификат настоящего `kws*.web.telegram.org`.
|
||||
проверять сертификат настоящего `kws*.web.telegram.org`. Используется rustls
|
||||
с провайдером ring и встроенными WebPKI roots; системный OpenSSL для ядра
|
||||
и CLI не нужен. Собственные корневые сертификаты ОС автоматически не
|
||||
подхватываются. Тесты проверяют доверенный сертификат, SNI, неверное имя
|
||||
хоста и недоверенного издателя.
|
||||
|
||||
Cloudflare Worker принимается только как пользовательская настройка. TGLock
|
||||
не загружает и не скрывает публичные списки чужих доменов.
|
||||
@@ -229,9 +239,11 @@ Worker должен принимать WebSocket на:
|
||||
/apiws?dst=<telegram-ip>&dc=<dc-id>
|
||||
```
|
||||
|
||||
и проксировать бинарные frames в TCP `<telegram-ip>:443`. Рекомендуется
|
||||
добавить собственную авторизацию до стабильного релиза; поэтому Worker
|
||||
остаётся расширенной опцией alpha-версии.
|
||||
и проксировать бинарные frames в TCP `<telegram-ip>:443`. Ответ 101 отправляется
|
||||
после открытия upstream TCP; таймаут/отказ — HTTP 504/502. Список назначений
|
||||
фиксирован, текстовые сообщения отклоняются. Очередь входящих записей ограничена
|
||||
1 МиБ и 256 сообщениями. Авторизация токеном требует собственного клиента:
|
||||
штатный TGLock токен Worker пока не передаёт.
|
||||
|
||||
Запись в сокет Telegram обязана быть последовательной: следующий чанк уходит
|
||||
после того, как записан предыдущий, и только когда писатель к этому готов
|
||||
|
||||
@@ -37,11 +37,13 @@
|
||||
|
||||
### Проверка, что воркер жив
|
||||
|
||||
Открой в браузере `https://tglock.имя.workers.dev/apiws`. Должно вернуться `expected a websocket upgrade` — это правильный ответ: значит код развёрнут и работает, просто браузер пришёл обычным запросом.
|
||||
Открой в браузере `https://tglock.имя.workers.dev/apiws`. Должно вернуться `expected a websocket upgrade` (HTTP 426). Это проверяет только публикацию скрипта. Соединение Worker → Telegram обычный GET **не проверяет**.
|
||||
|
||||
Если вернулось `not found` — проверь, что путь именно `/apiws`. Если ошибка про `cloudflare:sockets` — у воркера слишком старая дата совместимости, поставь в **Settings → Compatibility date** сегодняшнюю.
|
||||
|
||||
> **Разворачивал воркер до 2.0.0-beta.14 — обнови скрипт.** В прежней версии запись в сокет Telegram шла без ожидания предыдущей и без backpressure. Пока в клиенте голодала отправка, через воркер не проходило настоящего потока вверх и это не проявлялось; после того как голодание починили в beta.12, поток появился.
|
||||
> **Обнови скрипт при переходе с beta.14.** Теперь Worker ждёт открытия TCP к Telegram перед ответом 101: отказ возвращает 502, таймаут за 3 секунды — 504. Очередь записи ограничена 1 МиБ / 256 сообщениями; переполнение закрывает соединение с кодом 1009, ошибки сокета — 1011. Старый Worker мог показать успешное соединение ещё до попытки подключения к Telegram и не ограничивал очередь сообщений.
|
||||
|
||||
Для установки через Wrangler есть [`worker/wrangler.toml`](../worker/wrangler.toml). Из каталога `worker` можно выполнить `npx wrangler deploy` в своём Cloudflare-аккаунте. Эта команда публикует Worker; локальные тесты ничего не публикуют.
|
||||
|
||||
## Подключение в TGLock
|
||||
|
||||
@@ -54,13 +56,13 @@ tglock-cli --worker tglock.имя.workers.dev
|
||||
tglock-cli --worker первый.workers.dev --worker второй.workers.dev
|
||||
```
|
||||
|
||||
Worker всегда пробуется **последним**, после всех маршрутов Telegram. Пока обычные маршруты живы, трафик через него не пойдёт, и это осознанно: чужая инфраструктура в цепочке — это лишнее звено, а не улучшение.
|
||||
Настроенный Worker участвует в ограниченном параллельном переборе маршрутов: его проверка начинается вслед за первым Telegram-маршрутом, без ожидания всех таймаутов. Успешный маршрут запоминается. При быстром ответе Telegram Worker не нужен; при медленном прямом маршруте может победить Worker. Поэтому добавляй только свой домен или домен доверенного оператора.
|
||||
|
||||
## Ограничение доступа
|
||||
|
||||
Адрес воркера сам по себе секрет, но лучше поставить токен: **Settings → Variables → Add variable**, имя `TGLOCK_TOKEN`, значение — любая длинная строка.
|
||||
Адрес Worker не является механизмом авторизации. Для своего клиента можно задать секрет `TGLOCK_TOKEN` в настройках Cloudflare и передавать его параметром `token`.
|
||||
|
||||
Пока переменная не задана, проверка токена выключена. Когда задана — воркер начнёт отвечать `403` без параметра `?token=`. Клиент TGLock этот параметр пока не отправляет, так что включать токен есть смысл, если ты правишь и сам скрипт, и адрес.
|
||||
Штатный клиент TGLock параметр `token` пока не отправляет. **Не задавай TGLOCK_TOKEN для штатного клиента:** это приведёт к HTTP 403. При отсутствии переменной любой, кто знает домен, может использовать Worker для разрешённых адресов Telegram и расходовать лимиты твоего аккаунта.
|
||||
|
||||
Независимо от токена воркер соединяется **только** с семью адресами Telegram, которые запрашивает TGLock. Любой другой `dst` получает `403`, так что открытым TCP-прокси он не станет.
|
||||
|
||||
@@ -76,6 +78,7 @@ Sec-WebSocket-Protocol: binary
|
||||
- `dst` — адрес Telegram, к которому нужно подключиться по TCP на порт 443;
|
||||
- `dc` — номер датацентра, для логов;
|
||||
- **подпротокол `binary` обязательно нужно подтвердить в ответе** — без этого клиент разорвёт рукопожатие;
|
||||
- ответ 101 означает, что TCP к разрешённому Telegram IP уже открыт; HTTP 502/504 означает отказ/таймаут этого подключения;
|
||||
- дальше бинарные frames пересылаются в обе стороны без изменений;
|
||||
- TLS до самого воркера обеспечивает Cloudflare.
|
||||
|
||||
@@ -85,4 +88,6 @@ Sec-WebSocket-Protocol: binary
|
||||
|
||||
Скрипт написан по контракту, вычитанному из исходников клиента, и путь с параметрами закреплён тестом `connects_through_the_documented_worker_contract` — он поднимает локальный сервер, который ведёт себя ровно так, как описано выше, и проверяет, что туннель через него поднимается и данные доходят в обе стороны.
|
||||
|
||||
Чего этот тест не проверяет: развёрнутый воркер в самом Cloudflare. Если что-то не сойдётся с их API — [открой issue](https://github.com/by-sonic/tglock/issues/new), поправлю.
|
||||
`npm run test:worker` дополнительно исполняет настоящий файл Worker с заменой платформенных API: проверяет готовность TCP, отказ, таймаут, обе стороны передачи, порядок записи, переполнение и закрытие сокета. API `opened` и `close()` описаны в [документации Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/).
|
||||
|
||||
Эти тесты не проверяют развёрнутый Worker в Cloudflare и доступность Telegram из конкретного региона. Если ошибка остаётся, приложи версию клиента, диагностическую строку и HTTP/close-код Worker. Секрет прокси и приватные адреса Worker перед публикацией убери.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Открытые issues: исправления и проверка, 19 сентября 2026
|
||||
|
||||
База: `8617d25`, версия `2.0.0-beta.14`. Проверены все семь открытых issues
|
||||
и существующие PR #36 (Android), #49 (секрет GUI). Эта работа готовится как PR,
|
||||
без слияния, публикации релиза и автоматического закрытия жалоб.
|
||||
|
||||
## Что изменено
|
||||
|
||||
| Issue | Подтверждённая проблема / выполненная работа | Проверка и предел вывода |
|
||||
|---|---|---|
|
||||
| #59 — 11 тысяч неудачных соединений | Все маршруты в cooldown раньше приводили к немедленной новой попытке. Теперь пауза соблюдается; зависшие соединения не блокируют резервные | Тесты времени, числа параллельных попыток, отмены проигравших. Доступность Telegram у автора issue не проверена |
|
||||
| #58 — Worker, туннели 0/1 | Worker отвечал 101 до открытия TCP; ошибки после handshake терялись | Worker 502/504 и диагностические WebSocket close-коды, Rust-тесты раннего закрытия и reset. HTTP 426 проверяет только публикацию Worker |
|
||||
| #50 — ни один маршрут не работает | Последовательные TCP/TLS таймауты задерживали резерв; кратковременные туннели ошибочно сохраняли предпочтение маршрута | Ограниченный параллелизм, cooldown после upstream failure, ошибки Worker. Нельзя обещать обход, когда недоступны и Telegram, и собственный Worker |
|
||||
| #42 — Android через LAN | Прежние duplex-исправления уже в main; потеря ошибок upstream скрывала дальнейший отказ | Независимый Android-compatible криптографический вектор, фрагментированные init и одновременная передача 128/256 КиБ, диагностика разрывов. Нужна проверка Play Market-клиента в сети репортёра |
|
||||
| #32 — медиа | DC203 fallback через DNS попадал в DC2, хотя CDN имеет свой DC и ключи | DC203 сохраняет IP CDN во всех маршрутах; тест Worker dst, двунаправленная передача. Реальные фотографии аккаунта репортёра не проверены |
|
||||
| #53 — статическая aarch64 | Системный OpenSSL мешал самостоятельной musl-сборке | rustls + ring, ARM64 CI с проверкой ELF и запуском CLI, артефакт и OpenWrt-инструкция. Физический NanoPi R4S не проверен |
|
||||
| #9 — Android | Старый PR отстал от main, сборка требовала ручной настройки; жизненный цикл сервиса мог останавливать прокси или оставлять ложное уведомление | Перенос актуального ядра, foreground service по состоянию прокси, APK CI, постоянный секрет в каталоге приложения. HyperOS и длительная фоновая работа требуют устройства |
|
||||
|
||||
Дополнительно: Unicode-секрет больше не вызывает panic; GUI сохраняет секрет
|
||||
в каталоге приложения с миграцией прежнего файла; npm lock обновляет уязвимый
|
||||
транзитивный `nanoid` без смены версии приложения.
|
||||
|
||||
## Протокол и доверие
|
||||
|
||||
- TLS проверяет имя из WebSocket URI даже при подключении к закреплённому IP.
|
||||
Переход на rustls использует встроенные WebPKI roots; системные пользовательские
|
||||
корневые сертификаты автоматически не импортируются.
|
||||
- [Telegram WebSocket](https://core.telegram.org/mtproto/transports#websocket)
|
||||
является потоком байтов. Границы TCP read и WS messages не обязаны совпадать
|
||||
с MTProto-пакетами; дополнительный парсер пакетов не добавлялся.
|
||||
- [CDN DC](https://core.telegram.org/cdn) требует соответствующего назначения;
|
||||
успешный handshake другого DC не доказывает работоспособность медиа.
|
||||
- Worker остаётся опциональной инфраструктурой пользователя. Домен не является
|
||||
авторизацией; `TGLOCK_TOKEN` не поддерживается штатным клиентом.
|
||||
|
||||
## Воспроизводимые проверки
|
||||
|
||||
```text
|
||||
npm ci
|
||||
npm run test:worker
|
||||
npm run build
|
||||
cargo fmt --check
|
||||
cargo clippy --locked --all-targets -- -D warnings
|
||||
cargo test --locked --all-targets
|
||||
cargo clippy --locked --no-default-features --features cli --all-targets -- -D warnings
|
||||
cargo test --locked --no-default-features --features cli --lib --bins
|
||||
```
|
||||
|
||||
CI дополнительно проверяет MSRV 1.88, состав macOS GUI bundle, статическую
|
||||
ARM64 musl-сборку и подпись/содержимое Android debug APK. Конкретные результаты
|
||||
и ссылки на прогоны фиксируются в описании PR после завершения CI.
|
||||
|
||||
Worker: `npm run test:worker` исполняет настоящий deployment-файл с подменой
|
||||
только Cloudflare API; `wrangler deploy --dry-run` проверяет сборку без публикации.
|
||||
Это не тест живого Cloudflare-аккаунта. Отдельный тест Rust поднимает локальный
|
||||
WebSocket и проверяет совместимость клиента с контрактом Worker.
|
||||
|
||||
## Что нужно проверить на устройствах
|
||||
|
||||
Android: установка APK, старт/стоп, ссылка в Telegram, сохранение секрета после
|
||||
перезапуска, передача текста/медиа, работа в фоне 15 минут и политика батареи.
|
||||
OpenWrt: запуск CLI на NanoPi R4S, подключение нескольких устройств по LAN,
|
||||
перезапуск сервиса с прежним секретом. Инструкции: [Android](ANDROID.md),
|
||||
[OpenWrt](OPENWRT.md).
|
||||
|
||||
Issues о пользовательских симптомах остаются открыты до этой проверки.
|
||||
Наличие исправленного дефекта и зелёных регрессий не доказывает, что в конкретной
|
||||
сети отсутствует дополнительная блокировка.
|
||||
@@ -1,5 +1,8 @@
|
||||
# TGLock 2.0 issue audit
|
||||
|
||||
Актуальный разбор семи открытых issues и границ проверки:
|
||||
[19 сентября 2026](ISSUES_2026-09-19.md). Ниже сохранены исторические записи.
|
||||
|
||||
Проверено 29 июля 2026 года: все 15 issues и 5 pull requests, существовавшие
|
||||
в репозитории на момент аудита.
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# CLI для ARM64 / OpenWrt
|
||||
|
||||
Цель сборки — `aarch64-unknown-linux-musl`: ARM64 Linux, статический бинарь без
|
||||
зависимости от glibc, OpenSSL или WebView. Это подходит для 64-битной прошивки
|
||||
NanoPi R4S и других ARM64-роутеров. MIPS и 32-битный ARM требуют другой сборки.
|
||||
Проверьте архитектуру прошивки командой `uname -m`: ожидается `aarch64`.
|
||||
|
||||
Workflow **Static ARM64 CLI** собирает и запускает тесты на ARM64 runner,
|
||||
проверяет ELF (нет `INTERP` и `NEEDED`), запуск CLI, завершение по SIGTERM и
|
||||
сохранение секрета между запусками. Артефакт содержит бинарь и SHA-256.
|
||||
Release workflow прикладывает такой же проверенный артефакт к будущим релизам.
|
||||
Наличие сборки в PR не означает, что уже опубликован новый релиз.
|
||||
|
||||
Это проверка ARM64 Linux, а не испытание конкретной прошивки OpenWrt или
|
||||
доступности Telegram через вашего провайдера.
|
||||
|
||||
## Установка
|
||||
|
||||
Скачайте артефакт успешного запуска workflow нужного PR либо файл
|
||||
`tglock-cli-aarch64-unknown-linux-musl` из релиза, если он там опубликован.
|
||||
Сверьте SHA-256, скопируйте бинарь на роутер и выполните:
|
||||
|
||||
```sh
|
||||
chmod 755 /usr/bin/tglock-cli
|
||||
/usr/bin/tglock-cli --version
|
||||
mkdir -p /etc/tglock
|
||||
chmod 700 /etc/tglock
|
||||
```
|
||||
|
||||
Создайте `/etc/tglock/tglock.toml`:
|
||||
|
||||
```toml
|
||||
port = 1080
|
||||
lan = true
|
||||
secret_file = "/etc/tglock/secret"
|
||||
# worker = ["your-name.workers.dev"]
|
||||
```
|
||||
|
||||
`lan = true` нужен для телефонов и компьютеров в домашней сети. По умолчанию
|
||||
CLI слушает только loopback. Секрет создаётся при первом запуске и сохраняется
|
||||
в указанном файле; он не должен теряться при перезагрузке или обновлении.
|
||||
|
||||
```sh
|
||||
chmod 600 /etc/tglock/tglock.toml
|
||||
/usr/bin/tglock-cli --config /etc/tglock/tglock.toml
|
||||
```
|
||||
|
||||
В Telegram выберите MTProto и используйте LAN-адрес роутера, порт `1080` и
|
||||
секрет из напечатанной ссылки. `127.0.0.1` на телефоне означает сам телефон.
|
||||
Разрешайте входящий TCP `1080` только из доверенной LAN; не публикуйте порт
|
||||
в WAN. Звонки через UDP эта сборка не реализует.
|
||||
|
||||
## Сервис procd
|
||||
|
||||
После проверки ручного запуска сохраните `/etc/init.d/tglock`:
|
||||
|
||||
```sh
|
||||
#!/bin/sh /etc/rc.common
|
||||
START=95
|
||||
STOP=10
|
||||
USE_PROCD=1
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/tglock-cli --config /etc/tglock/tglock.toml
|
||||
procd_set_param respawn 3600 5 5
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
chmod 755 /etc/init.d/tglock
|
||||
/etc/init.d/tglock enable
|
||||
/etc/init.d/tglock start
|
||||
logread -e tglock
|
||||
```
|
||||
|
||||
Обновление: остановите сервис, замените бинарь после проверки контрольной суммы,
|
||||
сохраните `/etc/tglock`, запустите сервис снова. Для отмены автозапуска используйте
|
||||
`/etc/init.d/tglock stop` и `/etc/init.d/tglock disable`.
|
||||
|
||||
## TLS и маршруты
|
||||
|
||||
Встроенный набор доверенных корневых сертификатов webpki обновляется вместе с
|
||||
бинарём. Проверка сертификата и имени включена: при подключении к закреплённому
|
||||
IP имя Telegram по-прежнему используется для SNI и проверки сертификата.
|
||||
На роутере должно быть установлено правильное время.
|
||||
|
||||
Если все Telegram IP недоступны, нужен доступный маршрут через собственный
|
||||
[Cloudflare Worker](CLOUDFLARE_WORKER.md). Статическая сборка сама по себе не
|
||||
устраняет блокировку всех внешних маршрутов.
|
||||
Reference in New Issue
Block a user