Skip to content

Feat/api Add decoded partial BINARY FETCH to the modern IMAP API - #61

Merged
ralflang merged 2 commits into
horde:FRAMEWORK_6_0from
neuhaus:feat/modern-binary-fetch
Nov 2, 2026
Merged

ralflang merged 2 commits into
horde:FRAMEWORK_6_0from
neuhaus:feat/modern-binary-fetch

Conversation

@neuhaus

@neuhaus neuhaus commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

The modern API can tokenize binary literals but lacks public query builders for decoded MIME content and decoded sizes.

This adds RFC 3516 BINARY FETCH support:

$query = (new ImapFetchQuery())
    ->binaryBodyPart('1', start: 0, length: 1048576)
    ->binaryBodyPartSize('1');

foreach ($client->fetch('Objects', $client->getIdsOb([$uid]), $query) as $message) {
    $decoded = $message->getBinaryBodyPart('1');
    $totalDecodedBytes = $message->getBodyPartSize('1');
}

Behavior

  • Uses BINARY.PEEK by default, preserving the Seen flag.
  • Supports partial ranges measured in decoded bytes.
  • Parses BINARY.SIZE responses.
  • Keeps encoded BODY content distinct from decoded BINARY content.
  • Distinguishes NIL responses from valid empty decoded parts.
  • Validates section identifiers and partial ranges.
  • Requires server BINARY support and bypasses metadata cache reads for binary queries.
  • Propagates UNKNOWN-CTE errors without silently falling back to encoded BODY data.

Live testing also exposed an existing tokenizer issue: socket reads can return fewer bytes than requested. The tokenizer now reads until the literal’s announced length is reached and closes the connection if the literal is incomplete.

Memory behavior

The tokenizer still materializes each requested literal. Callers handling large objects should issue bounded partial FETCH requests and process each result before requesting the next range. An unrestricted FETCH is not memory-bounded by this change.

Validation

  • 19 new regression tests cover query syntax, binary payloads, decoded sizes, NIL/empty results, raw/decoded separation, capability checks, caching, UNKNOWN-CTE errors, short socket reads, and truncated literals.
  • Standalone branch unit suite: 1,640 tests, no failures or errors; 55 skipped, two incomplete, and one unrelated PHP 8.5 deprecation.
  • Combined with the separate modern APPEND stream patch, live 1 MiB and 512 MiB round trips used the modern API throughout and verified exact decoded lengths and SHA-256 hashes.
  • BINARY.PEEK preserved the Seen flag while the mailbox was open read-write.
  • The 512 MiB round trip used approximately 18 MiB peak PHP memory under a 64 MiB limit.

This branch is based directly on FRAMEWORK_6_0.

Reference: RFC 3516 §§4.2–4.3.

@what-the-diff

what-the-diff Bot commented Oct 24, 2026

Copy link
Copy Markdown

PR Summary

  • Added support for decoded BINARY FETCH requests. The client can now request decoded message sections, read them in partial ranges, and retrieve their decoded sizes. Requests use PEEK by default to avoid marking messages as read.
  • Improved reliability and response handling. The client checks server support before making BINARY requests, keeps decoded results separate from regular body data, and distinguishes between missing content and an empty response. It also safely handles data arriving in multiple pieces and closes the connection if a response is incomplete.
  • Documented the new API and added tests. A guide explains how to use BINARY FETCH and its limitations, with tests covering request construction, server support, caching, decoded content, sizes, and connection handling.

@ralflang
ralflang merged commit ad041e6 into horde:FRAMEWORK_6_0 Nov 2, 2026
@neuhaus
neuhaus deleted the feat/modern-binary-fetch branch November 2, 2026 07:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants