75.2 IMAP: новый сервер на Reactor Netty (STARTTLS, 993, IDLE, UIDPLUS, MOVE) #10

Merged
Liosha84 merged 1 commits from feat/75-2-imap into master 2026-11-05 20:59:35 +01:00
Owner

Чек-лист Jambotron v0.2.3, 75.2 — IMAP (и IMAP-часть 75.1: TLS).

Старый IMAP (выключен, ~20 .block() на сетевом потоке) удалён и написан заново по той же схеме, что SMTP.

Сервер

  • ImapServer (Reactor Netty): декодер собирает целые команды с литералами: {n} после + Ready, {n+} (LITERAL+) сразу, ограничение 50 МБ. Команды выполняются по одной на рабочем потоке, ответы уходят по порядку.
  • TLS: STARTTLS на IMAP_PORT (3143) и неявный TLS на IMAPS_PORT (993); сертификат тот же, что у SMTP. Вход только внутри TLS (LOGINDISABLED до него). Команды открытым текстом, присланные вместе со STARTTLS, не выполняются, соединение закрывается.
  • IDLE: проверка папки раз в 5 с, клиенту уходят EXISTS, EXPUNGE и FETCH FLAGS, пока не придёт DONE. Входящие команды читаются сразу, независимо от выполнения, поэтому DONE виден во время IDLE.

Протокол (ImapSession): IMAP4rev1, LITERAL+, SASL-IR, IDLE, UIDPLUS, MOVE, UNSELECT, NAMESPACE, SPECIAL-USE, ID.

  • FETCH: ENVELOPE, BODYSTRUCTURE, BODY[section]<partial> (HEADER, HEADER.FIELDS[.NOT], TEXT, MIME, вложенные message/rfc822).
  • Также SEARCH (все ключи RFC 3501), STORE, COPY/MOVE с COPYUID, APPEND с APPENDUID, EXPUNGE и UID EXPUNGE, LIST/LSUB/STATUS. CREATE, RENAME и DELETE работают с папками, имена в modified UTF-7.
  • MimeTree делит письмо на части по байтам, поэтому размеры в BODYSTRUCTURE совпадают с BODY[].

Данные (V4)

  • UID в папке выдаёт триггер в базе. Письмо, перенесённое из веба, по IMAP или через SMTP, получает новый, больший UID, и клиент его не пропустит. Сохранение строки без переноса UID не меняет.
  • Новые флаги \Answered и \Deleted.
  • message_sources хранит письмо ровно как пришло (SMTP, APPEND). Для писем из веб-почты MIME собирается один раз (MimeSource, граница и Message-ID зависят от id письма). Правка черновика или его вложений этот исходник сбрасывает.

Проверка

  • :backend:test 47/47. Новые тесты: настоящий клиент Angus IMAP (папки, письма, вложения через BODYSTRUCTURE и BODY[1]/[2], поиск, UID, APPEND/COPY/MOVE/EXPUNGE, папки с кириллицей); сырые литералы и LITERAL+; IDLE; подмешивание после STARTTLS; секции вложенного письма; пример mUTF-7 из RFC 3501.
  • Вживую на PostgreSQL со старой версией (V3) и письмами, затем обновление до V4: UID пронумерованы, uid_next выставлен верно. Python imaplib через STARTTLS и порт 993 проверил по 23 пунктам, ошибок 0: папки special-use, UTF-8 тема, BODYSTRUCTURE и вложение, поиск, \Seen после BODY[], черновик из веба (один размер дважды), APPEND, COPY, MOVE, EXPUNGE, STATUS, папка «Проекты», IDLE с письмом по SMTP во время ожидания. Триггер UID проверен напрямую.

После мержа на staging: загрузить ZIP и передеплоить (миграция V4 пройдёт сама), затем в Jambotron открыть внутренний TCP-порт mail:3143. Thunderbird на компьютере не в сети WireGuard, поэтому подключается через SSH-туннель к Contabo: ssh -L 3143:10.30.1.77:<порт> root@<contabo>, в Thunderbird — localhost:3143, STARTTLS, принять самоподписанный сертификат.

— α

🤖 Generated with Claude Code

Чек-лист Jambotron v0.2.3, **75.2 — IMAP** (и IMAP-часть 75.1: TLS). Старый IMAP (выключен, ~20 `.block()` на сетевом потоке) удалён и написан заново по той же схеме, что SMTP. **Сервер** - `ImapServer` (Reactor Netty): декодер собирает целые команды с литералами: `{n}` после `+ Ready`, `{n+}` (LITERAL+) сразу, ограничение 50 МБ. Команды выполняются по одной на рабочем потоке, ответы уходят по порядку. - **TLS:** STARTTLS на `IMAP_PORT` (3143) и неявный TLS на `IMAPS_PORT` (993); сертификат тот же, что у SMTP. Вход только внутри TLS (`LOGINDISABLED` до него). Команды открытым текстом, присланные вместе со STARTTLS, не выполняются, соединение закрывается. - **IDLE:** проверка папки раз в 5 с, клиенту уходят `EXISTS`, `EXPUNGE` и `FETCH FLAGS`, пока не придёт `DONE`. Входящие команды читаются сразу, независимо от выполнения, поэтому `DONE` виден во время IDLE. **Протокол** (`ImapSession`): IMAP4rev1, LITERAL+, SASL-IR, IDLE, UIDPLUS, MOVE, UNSELECT, NAMESPACE, SPECIAL-USE, ID. - FETCH: `ENVELOPE`, `BODYSTRUCTURE`, `BODY[section]<partial>` (HEADER, HEADER.FIELDS[.NOT], TEXT, MIME, вложенные `message/rfc822`). - Также SEARCH (все ключи RFC 3501), STORE, COPY/MOVE с `COPYUID`, APPEND с `APPENDUID`, EXPUNGE и UID EXPUNGE, LIST/LSUB/STATUS. CREATE, RENAME и DELETE работают с папками, имена в modified UTF-7. - `MimeTree` делит письмо на части по байтам, поэтому размеры в `BODYSTRUCTURE` совпадают с `BODY[]`. **Данные (V4)** - **UID в папке выдаёт триггер в базе.** Письмо, перенесённое из веба, по IMAP или через SMTP, получает новый, больший UID, и клиент его не пропустит. Сохранение строки без переноса UID не меняет. - Новые флаги `\Answered` и `\Deleted`. - `message_sources` хранит письмо ровно как пришло (SMTP, APPEND). Для писем из веб-почты MIME собирается один раз (`MimeSource`, граница и Message-ID зависят от id письма). Правка черновика или его вложений этот исходник сбрасывает. **Проверка** - `:backend:test` 47/47. Новые тесты: настоящий клиент Angus IMAP (папки, письма, вложения через BODYSTRUCTURE и BODY[1]/[2], поиск, UID, APPEND/COPY/MOVE/EXPUNGE, папки с кириллицей); сырые литералы и LITERAL+; IDLE; подмешивание после STARTTLS; секции вложенного письма; пример mUTF-7 из RFC 3501. - **Вживую на PostgreSQL** со старой версией (V3) и письмами, затем обновление до V4: UID пронумерованы, `uid_next` выставлен верно. Python `imaplib` через STARTTLS и порт 993 проверил по 23 пунктам, ошибок 0: папки special-use, UTF-8 тема, BODYSTRUCTURE и вложение, поиск, `\Seen` после BODY[], черновик из веба (один размер дважды), APPEND, COPY, MOVE, EXPUNGE, STATUS, папка «Проекты», IDLE с письмом по SMTP во время ожидания. Триггер UID проверен напрямую. **После мержа на staging:** загрузить ZIP и передеплоить (миграция V4 пройдёт сама), затем в Jambotron открыть внутренний TCP-порт `mail:3143`. Thunderbird на компьютере не в сети WireGuard, поэтому подключается через SSH-туннель к Contabo: `ssh -L 3143:10.30.1.77:<порт> root@<contabo>`, в Thunderbird — `localhost:3143`, STARTTLS, принять самоподписанный сертификат. — α 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Liosha84 added 1 commit 2026-11-05 20:45:35 +01:00
Старый IMAP (выключен, .block() на сетевом потоке) удалён, написан заново:
- ImapServer: декодер целых команд с литералами ({n} после "+ Ready", {n+}),
  команды по одной на рабочем потоке; STARTTLS (без выполнения открытого текста
  после него) и неявный TLS; IDLE с опросом раз в 5 с до DONE
- ImapSession: IMAP4rev1 + LITERAL+, SASL-IR, IDLE, UIDPLUS, MOVE, UNSELECT,
  NAMESPACE, SPECIAL-USE, ID; FETCH (ENVELOPE, BODYSTRUCTURE, BODY[section]<partial>),
  SEARCH, STORE, COPY/MOVE с COPYUID, APPEND с APPENDUID, EXPUNGE/UID EXPUNGE
- MimeTree: части письма по байтам — размеры BODYSTRUCTURE совпадают с BODY[]
- V4: UID на папку выдаёт триггер (перенос из веба тоже получает новый UID),
  флаги \Answered/\Deleted, message_sources — письмо как пришло (SMTP, APPEND)
  или собранное один раз (MimeSource); правка черновика сбрасывает его
- тесты: настоящий клиент Angus IMAP, сырые литералы/IDLE/подмешивание (47 всего)
- живая проверка: обновление V3 -> V4 на PostgreSQL с письмами, Python imaplib:
  STARTTLS, 993, папки, вложения, поиск, APPEND/COPY/MOVE/EXPUNGE, IDLE — 0 ошибок

α
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Liosha84 merged commit eaffffd4cc into master 2026-11-05 20:59:35 +01:00
Sign in to join this conversation.