/** * Reading one whole message: BODYSTRUCTURE, MIME part selection, and decoding. * * Why a structure parser at all * ───────────────────────────── * `ImapClient.fetchMessage` returns the text of a message and a LIST of its * attachments, never their bytes. That distinction is the reason this file * exists: the server describes every part of a message in its BODYSTRUCTURE * reply (type, subtype, encoding, size, filename), so a client can report what * is attached, and then fetch ONLY the sections it wants to read. Fetching * `BODY[]` would have been three lines of code and would have pulled every * attachment, a scanned PDF, an archive, whatever a stranger chose to send, * down the wire and into memory on every read. * * Everything here is text in, data out: no socket, no clock, no filesystem. * * Defensive by construction * ───────────────────────── * Every function in this file takes attacker-authored input, because a message * body and its structure are written by whoever sent the mail. Nothing throws: * a malformed structure yields no parts (and therefore no attachments), an * unterminated string yields what was readable, and nesting is bounded. The * caller's contract, empty rather than an exception, depends on that holding * for every input, not for the inputs we thought of. */ import type { ImapAttachmentInfo } from './imap-client.js'; /** One leaf MIME part, as the server described it in BODYSTRUCTURE. */ export interface ImapBodyPart { /** IMAP section specifier, e.g. `1`, `2`, `1.2`, what BODY.PEEK[..] takes. */ readonly section: string; /** Lowercased MIME type, e.g. `text`. */ readonly type: string; /** Lowercased MIME subtype, e.g. `plain`. */ readonly subtype: string; /** Lowercased content-transfer-encoding, e.g. `base64`. '' when unstated. */ readonly encoding: string; /** Lowercased charset parameter. '' when unstated. */ readonly charset: string; /** Filename from the disposition, or the content-type `name`. '' when none. */ readonly filename: string; /** Size in bytes as the server reported it. 0 when unstated. */ readonly sizeBytes: number; /** * True when this part is something the reader should be TOLD about rather * than shown: an explicit `attachment` disposition, a filename, or any * non-text type. The two body parts are chosen from what is left. */ readonly isAttachment: boolean; } /** * Parse a BODYSTRUCTURE expression into its leaf parts. * Returns an empty list for anything it cannot read, a caller that gets no * parts reports no attachments, which is the honest answer when the server's * description of the message was not readable. */ export declare function parseBodyStructure(raw: string): ImapBodyPart[]; /** The first part that should be shown as the message body, or null. */ export declare function selectBodyPart(parts: readonly ImapBodyPart[], subtype: 'plain' | 'html'): ImapBodyPart | null; /** Attachment metadata, names, types and sizes only, never content. */ export declare function attachmentsFromParts(parts: readonly ImapBodyPart[]): ImapAttachmentInfo[]; /** * Turn one fetched section into readable text. * * Handles base64 and quoted-printable, which is what mail actually arrives as; * 7bit/8bit/binary sections are already text by the time the socket has decoded * them. Never throws, an encoding we cannot undo yields the raw section, which * is worse to read than the real thing and better than nothing. */ export declare function decodeTextPart(raw: string, encoding: string, charset: string): string; /** True when the server actually returned a message for the fetch. */ export declare function hasFetchResponse(lines: readonly string[]): boolean; /** * Pull the section payload out of a single-message FETCH response. * * The session inlines a `{n}` literal into the line that announced it, so the * payload usually arrives as the tail of the `* n FETCH (BODY[..] ` line; * short sections may instead arrive as a quoted string, and an absent one as * NIL. Returns null when there was no FETCH response at all, which is how a * UID that no longer exists is told apart from a section that is empty. */ export declare function extractFetchSection(lines: readonly string[]): string | null; /** * Pull the BODYSTRUCTURE expression out of a FETCH response. * Lines are joined first, because a literal inside the structure (a filename, * typically) fragments the response across several of them. */ export declare function extractBodyStructure(lines: readonly string[]): string; //# sourceMappingURL=imap-bodystructure.d.ts.map