Кратко

  • Номер сообщения назначался после открытия текущего maildrop и мог измениться в следующем сеансе. LAST хранил лишь наибольший ранее использованный номер, а не историю каждого элемента.
  • RFC 1725 удалила LAST и добавила необязательный UIDL. RFC 1939 потребовала сохранять UID между сеансами внутри одного maildrop, но RFC 2449 отдельно описала EXPIRE: срок хранения и срок жизни идентификатора оставались разными свидетельствами.

Письмо имеет тот же UID, что и вчера. Из этого следует, что сервер считает его тем же элементом внутри этого maildrop. Но отсюда не следует, что письмо будет находиться там завтра.

Оператор может применять квоту или политику хранения. Другой клиент может удалить сообщение. Даже объявление EXPIRE 30 не называет точный момент исчезновения конкретного письма: отсчёт может начинаться при поступлении, первом перечислении, извлечении или ином событии.

Эта разница между узнаваемостью и сохранностью появилась не как отвлечённая теория. Она стала необходима, когда клиенты начали оставлять уже загруженные письма на сервере и при следующем подключении должны были отличать старое от нового.

Номер существовал внутри текущего представления maildrop

RFC 1460 описывала POP3 для небольших рабочих станций и персональных компьютеров, которые не могли постоянно поддерживать собственную систему передачи почты. Сервер держал письма в maildrop, а клиент подключался время от времени, извлекал сообщения и обычно удалял их.

После открытия и разбора maildrop сервер присваивал первому сообщению номер 1, второму — 2 и так далее. Для LIST, RETR и DELE это был точный операнд текущей транзакции.

Но позиция не переживает изменение набора. Если более раннее письмо исчезает, все следующие номера сдвигаются. Элемент с номером 2 в понедельник может стать номером 1 во вторник. Оба номера правильны для своих сеансов и ни один не обещает долговременного имени.

В той версии существовала команда LAST. Сервер возвращал наибольший номер, к которому обращались в предыдущих транзакциях. Более высокие номера клиент мог считать ещё не просмотренными. RETR или DELE более высокого номера передвигали границу, а RSET сбрасывал её в ноль.

Одна граница описывает только непрерывный префикс. Она не выражает историю «1 и 3 сохранены, 2 нет», не разделяет память двух устройств и теряет смысл при перестройке позиций. Это был показатель продвижения по порядку, а не имя конкретного письма.

RFC 1725 явно перечислила два изменения: LAST удалена, необязательная UIDL добавлена. Документ не называл одно единственной причиной другого, но механизм перешёл от общего максимума к токену на элемент, из которого каждый клиент мог строить собственное множество уже обработанных сообщений.

UIDL связала сегодняшний номер со вчерашней записью

С номером сообщения команда UIDL возвращает текущий номер и unique-id. Без аргумента сервер выдаёт многострочный список для каждого сообщения, ещё не помеченного на удаление.

У двух столбцов разное время действия. Номер позволяет выполнить RETR или DELE сейчас. UID позволяет сравнить текущую строку с локальным реестром предыдущего сеанса.

RFC 1939 определяет UID как произвольную строку, заданную сервером, длиной от одного до семидесяти печатаемых символов. Она идентифицирует сообщение внутри одного maildrop и сохраняется между сеансами. Требование действует даже тогда, когда предыдущий сеанс завершился без перехода в состояние UPDATE.

Это важно при аварийном разрыве. Клиент может получить и сохранить письмо, а затем потерять соединение до QUIT. Без UPDATE сервер не имеет права удалять помеченные сообщения. Письмо появится снова. Если при этом изменится UID, уже сохранённый элемент будет выглядеть новым.

Сервер также не должен повторно использовать UID в том же maildrop, пока обозначенный им элемент существует. Иначе новая строка унаследует старую историю в локальном реестре клиента.

Клиент владел памятью, сервер — ограниченной непрерывностью

UIDL не записывает, что письмо прочитано, показано, архивировано или надёжно сохранено. Сервер выдаёт токен. Клиент определяет, какое действие считается завершённым, и хранит это решение локально.

Так POP3 сохранил простую центральную модель. Серверу не требовались отдельные флаги и папки для каждого устройства. Клиент после перерыва сначала сравнивал небольшой список, а затем извлекал только нужные тела.

Цена простоты — перенесённый риск. Потеря локальной базы UID вызывает повторную загрузку. Миграция, сохранившая тела и сменившая токены, делает старые письма новыми для всех клиентов. Запись «готово» до подтверждения локального хранения может привести к удалению единственной безопасной копии.

Поэтому uid_listed, retrieval_completed, stored_locally, delete_marked, update_committed и session_aborted должны оставаться отдельными состояниями. Успешный UIDL подтверждает отображение идентификатора, а не результат последующей обработки.

Уникальность имела границу и исключение для одинаковых копий

UID не является глобальным идентификатором. Та же строка в другой учётной записи, на другом сервере или в другом maildrop не имеет определённой связи. Это не заголовок Message-ID, не доказательство отправителя, доставки, целостности или подлинности.

RFC 1939 обычно предпочитает сохранённые произвольно назначенные UID, но разрешает вычислять их как хеш сообщения. Поэтому клиент обязан уметь обработать две одинаковые копии в одном maildrop с одним UID.

Локальная таблица «один UID — одна строка» способна тихо схлопнуть реальную копию. Для сверки нужно сохранять текущую кратность и номера, а не только токен. UID отвечает на ограниченный вопрос узнавания во времени и не всегда сообщает число неразличимых хранимых экземпляров.

Название механизма не расширяет его полномочия. Unique не значит «по всему миру», persistent не значит «навсегда», identity не значит «аутентичность». Достаточно было узнавать элементы одного maildrop при возвращении клиента.

Постоянство UID действовало только пока элемент существовал

RFC 1939 предупреждала, что режим «оставлять на сервере» может накопить сотни или тысячи уже прочитанных сообщений. Сайт вправе применять квоты и правила хранения, а также удалять письма вне команд POP3 при объявленной политике.

Следовательно, старый UID не является распиской о хранении. Если он исчез из списка, причиной может быть другой клиент, политика сайта, ошибка или неполный ответ. Отсутствие нужно сначала квалифицировать, и только потом превращать в решение о восстановлении или удалении.

RFC 2449 добавила CAPA, потому что необязательные команды раньше приходилось обнаруживать пробой или настройкой. Строка UIDL объявляет поддержку команды. Она не измеряет качество идентификаторов и не гарантирует срок хранения.

Та же RFC ввела EXPIRE. Сервер может объявить минимальное число дней, ноль или NEVER. Однако механизм намеренно не даёт точного времени истечения для отдельного сообщения: начальная точка счётчика зависит от политики реализации.

Даже EXPIRE NEVER — это объявленная способность и политика в текущем контексте, а не криптографическая гарантия, что никакой администратор, другой клиент, авария или изменение договора не удалит письмо. Для операционной уверенности нужны наблюдаемая политика и проверка текущего списка.

Три времени вместо одного ложного обещания

POP3 в итоге разделил три временные шкалы. Номер действовал в текущем сеансе. UID сохранялся для существующего элемента между сеансами. Политика EXPIRE описывала, как долго элемент может оставаться доступным.

Это разделение не сделало POP3 полноценной синхронизацией почтового ящика. Оно не добавило папки, общие флаги и серверную память обо всех устройствах. Зато клиент получил минимальный мост через разрыв, не принимая имя за договор хранения.

Исторический урок UIDL и EXPIRE состоит в дисциплине доказательств. Узнать тот же элемент, доказать его локальное сохранение и предсказать его серверную жизнь — разные задачи. Номер переживает только сеанс; UID переживает подключение; срок хранения остаётся отдельным решением оператора.

Источники