/** * Reading an IMAP server's answers: header blocks, delivery evidence, and the * three FETCH/SEARCH response shapes the client asks for. * * Split out of `imap-client.ts` so both halves stay under the repository's * per-file line cap. Nothing here touches a socket, a clock or the filesystem, * every function takes text and returns data, which is what lets the * top-most-only rules below be tested without a server. */ import type { ImapEnvelopeBatch } from './imap-types.js'; /** * Which header field a delivery-evidence value was read from. * Both names are stamped by the final delivery agent, not by the sender, * that is what makes them evidence. `to-header` is deliberately absent from * this union: the To: header is sender-authored and is never evidence. */ export type DeliveryEvidenceSource = 'delivered-to' | 'x-original-to'; /** One delivery-evidence value with its provenance attached. */ export interface DeliveryEvidence { /** Normalized bare address, lowercased (angle-addr unwrapped when present). */ readonly address: string; /** The header value exactly as received, before normalization. */ readonly rawValue: string; /** The header field this came from. */ readonly source: DeliveryEvidenceSource; } /** First occurrence of a header, unfolded and trimmed. '' when absent. */ export declare function extractHeader(rawHeaders: string, name: string): string; /** * Extract delivery evidence from a raw header block. * * Only the TOP-MOST delivery header in the block is returned. A sender can put * their own `Delivered-To:`/`X-Original-To:` lines in the message they submit, * and those always land BELOW the line the receiving delivery agent prepends, * so every occurrence after the first is attacker-reachable and is discarded. * * Deliberately conservative: we do not additionally trust the second delivery * header even when it carries the other field name. If our delivery agent does * not stamp `X-Original-To`, then the top-most `X-Original-To` in a message * would be the sender's own, so "top-most per field name" would be forgeable. * The mailbox (`ImapEnvelope.mailbox`) remains the primary anchor. */ export declare function extractDeliveryEvidence(rawHeaders: string): DeliveryEvidence[]; /** * The top-most `Authentication-Results` header, or none. * * Same reasoning as `extractDeliveryEvidence`: a sender can embed their own * copy, and it lands below the receiving server's. Only the first is returned, * and a message with none yields an empty list rather than a default verdict. */ export declare function extractAuthenticationResults(rawHeaders: string): string[]; /** * The numbers in a `* SEARCH ...` response, in the order the server gave them. * * A server answers `SEARCH` with sequence numbers and `UID SEARCH` with UIDs, * in the same untagged `* SEARCH` line either way, the wire shape carries no * mark of which it is. The caller knows which command it sent, so the name * here says "numbers" rather than claiming one or the other. This client only * ever sends `UID SEARCH`. * * BOUNDED AT BOTH ENDS, and the upper bound is not decoration. Sequence * numbers and UIDs are both 32-bit (RFC 3501 ยง2.3.1.1); `parseInt` is not. * `parseInt('99999999999999999999', 10)` is `1e20`, `Number.isInteger(1e20)` * is true, and that value used to travel from this line straight into * `MailboxCursorStore.advance()`, where `Math.max` would pin the cursor above * every UID a server can ever issue with no way for a later pass to bring it * down. A token outside the protocol's range names no message this client can * fetch, so it is dropped here rather than carried on as a number that still * looks usable. */ export declare function parseSearchNumbers(searchResponse: readonly string[]): number[]; /** * Turn the lines of one envelope `UID FETCH` into envelopes, and say plainly * which responses could not be read. * * The second half is the point. A caller that advances a cursor has to tell * two things apart that used to look identical from here: * * - the server sent NO response for a UID, it was expunged between the * search and the fetch, and the cursor may move past it; * - the server sent a response for it and this client could not read the * answer, nothing is known about that message, and moving the cursor * would skip mail that is still sitting in the mailbox. * * So every response that arrives is accounted for: it becomes an envelope, or * it becomes an entry in `unreadable` with a plain-language reason. Nothing is * dropped on the floor, which is what `parseFetchHeaders` and `parseFetchUids` * did between them, a response whose UID could not be located was skipped in * silence, and read downstream as an expunge. */ export declare function readEnvelopeBatch(fetchLines: readonly string[], mailbox: string, wanted: readonly number[]): ImapEnvelopeBatch; /** * The capability atoms a server named, from anywhere it named them. * * Servers advertise in three places and no server uses all three: inside the * greeting as `* OK [CAPABILITY ...]`, as an untagged `* CAPABILITY ...` line, * and inside a tagged completion as `... OK [CAPABILITY ...]`. All three are * read here so a caller does not have to ask again for something the server * already volunteered. * * Atoms are upper-cased and de-duplicated, order preserved. An empty result * means the server said nothing, which is NOT the same as "supports nothing", * and callers must not read it that way. */ export declare function parseCapabilities(lines: readonly string[]): string[]; /** * What the server said about the mailbox when it was EXAMINEd. * * `uidValidity` is the field that matters most and is the one most easily * ignored: when it changes, every UID recorded under the old value names * nothing, because the mailbox was recreated. Anything keeping a UID across * connections has to store this alongside it. * * Every field is null when the server did not report it. Nothing here is * defaulted to zero, a missing UIDVALIDITY is not the same fact as * `UIDVALIDITY 0`, and a caller that has to tell the difference cannot if we * invent one. */ export interface ImapMailboxStatus { /** `* n EXISTS`, how many messages the mailbox holds. */ readonly exists: number | null; /** `* OK [UIDVALIDITY n]`, the generation the UIDs below belong to. */ readonly uidValidity: number | null; /** `* OK [UIDNEXT n]`, the UID the next arriving message will be given. */ readonly uidNext: number | null; /** True when the completion carried `[READ-ONLY]`, as EXAMINE's does. */ readonly readOnly: boolean; } /** Read an EXAMINE (or SELECT) response into its mailbox facts. */ export declare function parseMailboxStatus(lines: readonly string[]): ImapMailboxStatus; export declare function formatImapDate(date: Date): string; //# sourceMappingURL=imap-headers.d.ts.map