/**
 * Helper utilities for mailpouch
 */
import type { EmailAttachment } from "../types/index.js";
/**
 * Shared attachment caps. The SMTP send path (smtp-service.ts) and the IMAP
 * draft path (simple-imap-service.ts) MUST enforce the same limits — VALID-005
 * tracked the asymmetry where saveDraft mirrored sanitisation but not the caps.
 * Bytes match Proton's own 25 MB per-message limit.
 */
export declare const MAX_ATTACHMENT_COUNT = 20;
export declare const MAX_ATTACHMENT_BYTES: number;
export declare const MAX_TOTAL_ATTACHMENT_BYTES: number;
/**
 * Per-token cap for `parseEmails` (VALID-010). RFC 5321 caps a full address at
 * 320 chars; 1024 leaves generous room for a `Display Name <addr>` token while
 * keeping the regex off multi-kilobyte inputs.
 */
export declare const MAX_ADDRESS_TOKEN = 1024;
/**
 * Best-effort decoded byte size of an attachment `content` value.
 * base64 strings decode to ~3/4 of their character length; Buffers are exact.
 * Returns `null` for anything that is neither (e.g. a Readable stream), which
 * callers treat as "unsizable → reject".
 */
export declare function attachmentByteSize(content: unknown): number | null;
/**
 * Validate email address format.
 *
 * Enforces RFC 5321 length limits in addition to structural checks:
 *   • Total address: max 320 characters
 *   • Local part (before @): max 64 characters
 *   • Domain (after @): max 253 characters
 *
 * An unbounded regex check alone allowed multi-kilobyte "addresses" to pass,
 * risking header bloat and downstream OOM in MIME parsers.
 */
export declare function isValidEmail(email: string): boolean;
/**
 * Parse comma-separated email addresses, returning both the valid addresses and
 * the raw segments that were dropped as invalid.
 *
 * SMTP-014: the original `parseEmails` silently discarded malformed addresses,
 * so a caller submitting "alice@x.com, bogus, bob@y.com" proceeded with two
 * recipients and no signal that one was dropped. This detailed form lets the
 * SMTP layer surface partial-failure to the caller instead of quietly sending
 * to fewer recipients than intended.
 */
export declare function parseEmailsDetailed(emailString: string): {
    valid: string[];
    dropped: string[];
};
/**
 * Parse comma-separated email addresses.
 * Invalid or malformed addresses are skipped; a warning is logged for each
 * so that callers can detect misconfigured CC/BCC lists without hard-failing.
 */
export declare function parseEmails(emailString: string): string[];
/**
 * Format date to ISO string
 */
export declare function formatDate(date: Date): string;
/**
 * Parse date from string
 */
export declare function parseDate(dateString: string): Date;
/**
 * Format bytes to human-readable size
 */
export declare function formatBytes(bytes: number): string;
/**
 * Convert bytes to megabytes
 */
export declare function bytesToMB(bytes: number): number;
/**
 * Sanitize string for safe logging.
 *
 * Strips the full C0/C1 control-character set (U+0000–U+001F and U+007F)
 * before truncating.  Only stripping [\r\n\t] left 24 other control characters
 * (backspace, form-feed, vertical-tab, ESC, etc.) available for log injection
 * or terminal-escape attacks.
 */
export declare function sanitizeForLog(str: string, maxLength?: number): string;
/**
 * Extract email address from "Name <email@domain.com>" format
 */
export declare function extractEmailAddress(emailString: string): string;
/**
 * Extract name from "Name <email@domain.com>" format
 */
export declare function extractName(emailString: string): string | undefined;
/**
 * Sleep/delay function
 */
export declare function sleep(ms: number): Promise<void>;
/**
 * Retry function with exponential backoff
 */
export declare function retry<T>(fn: () => Promise<T>, maxRetries?: number, delayMs?: number): Promise<T>;
/**
 * Generate unique ID using cryptographically secure randomness
 */
export declare function generateId(): string;
/**
 * Validate a single IMAP name segment (a "leaf") before it is composed into a
 * path such as `Labels/<name>` or `Folders/<name>`. Shared by
 * `validateLabelName` and `validateFolderName` so the rules can't drift
 * (VALID-004 — the two were byte-identical duplicates).
 *
 * Returns `null` on success or an error message string on failure. Rules:
 *   - Must be a non-empty string after trimming
 *   - Must not contain `/` (path separator) or `..` (traversal)
 *   - Must not contain C0/C1 control characters or DEL
 *   - Must not exceed 255 characters
 */
export declare function validateLeafName(value: unknown, fieldName: string): string | null;
/**
 * Validate a label name before constructing an IMAP path (e.g. `Labels/<name>`).
 * Returns `null` on success or an error message string on failure.
 *
 * Note (VALID-019): a non-ASCII leaf like `Wörk` is accepted here but imapflow
 * encodes it to modified UTF-7 (`W&APY-rk`) on the wire, so the IMAP-stored
 * name differs from the in-memory key. Callers that use the label as a map key
 * must re-list to learn the encoded name.
 */
export declare function validateLabelName(label: unknown): string | null;
/**
 * Validate a folder name before constructing an IMAP path (e.g. `Folders/<name>`).
 * Returns `null` on success or an error message string on failure.
 * The folder name is the leaf segment only; the `Folders/` prefix is added by
 * the caller.
 */
export declare function validateFolderName(folder: unknown): string | null;
/**
 * Validate a `targetFolder` argument used as a direct IMAP path (not prefixed).
 *
 * Returns `null` on success or an error message string on failure.
 * Unlike validateLabelName/validateFolderName, a forward slash IS allowed here
 * since the full path may include separators (e.g. `Folders/Work`).
 * Rejects `..` (traversal) and C0 control characters.
 * Max length 1000 characters.
 */
export declare function validateTargetFolder(targetFolder: unknown): string | null;
/**
 * Like `validateTargetFolder` but REQUIRES a non-empty (after trim) name.
 *
 * VALID-011: folder-mutating tools (`create_folder`/`delete_folder`/
 * `rename_folder`) always need a concrete name, so the empty-string fall-back
 * semantics of `validateTargetFolder` are wrong for them. Each handler used to
 * repeat its own `!args.folderName || ...trim()` guard with subtly different
 * shapes; this centralises it. Returns the trimmed value on success or throws
 * `McpError(InvalidParams, …)`.
 */
export declare function validateRequiredTargetFolder(raw: unknown, fieldName?: string): string;
/**
 * Validate an IMAP folder *path* (full path, separators allowed) before it is
 * serialised into an IMAP command literal. Unlike the leaf-only
 * `validateFolderName`/`validateLabelName`, a forward slash IS permitted here
 * because the value is a complete path (e.g. `Folders/Work`).
 *
 * Returns `null` on success or an error message string on failure. Rules:
 *   - Must be a non-empty string after trimming
 *   - Must not contain `..` (path traversal)
 *   - Must not contain C0 control characters (U+0000–U+001F)
 *   - Must not exceed 1000 characters
 *
 * VALID-003: single source of truth for the full-path check. The IMAP service's
 * private `validateFolderName(name)` delegates here (rethrowing the message as
 * an Error) so the two no longer diverge.
 */
export declare function validateImapPath(path: unknown): string | null;
/**
 * Truncate text to a maximum length, appending "..." when truncated.
 *
 * @param text - The string to truncate.
 * @param maxLength - Maximum number of characters (including the 3-char ellipsis).
 *   Must be greater than 3 for the ellipsis to fit; strings shorter than or
 *   equal to maxLength are returned unchanged.
 */
export declare function truncate(text: string, maxLength: number): string;
/**
 * Assert that `raw` is a non-empty, all-digit string suitable for use as an
 * IMAP UID.  Returns the validated string on success or throws an
 * `McpError(InvalidParams, …)` on failure.
 *
 * Centralises the repeated guard pattern found across ~12 tool handlers:
 *
 * ```ts
 * if (!X || typeof X !== "string" || !/^\d+$/.test(X)) {
 *   throw new McpError(ErrorCode.InvalidParams, "emailId must be a non-empty numeric UID string.");
 * }
 * ```
 *
 * @param raw       - The raw argument value from the MCP tool call (type `unknown`).
 * @param fieldName - The argument field name used in the error message, e.g. `"emailId"`.
 *                    Defaults to `"emailId"`.
 * @returns The validated UID string.
 * @throws {McpError} with `ErrorCode.InvalidParams` when validation fails.
 */
export declare function requireNumericEmailId(raw: unknown, fieldName?: string): string;
/**
 * Validate an optional `folder` argument that flows from a tool handler into
 * `imapService.getEmailById` (or similar service methods that take a folder
 * hint). Returns the validated folder string when present; returns undefined
 * for `undefined`/`null`/empty-string; throws `McpError(InvalidParams, …)`
 * for non-strings or invalid IMAP paths (CRLF / control chars / `..`).
 *
 * Closes VALID-001 / VALID-009 from the 2026-05-28 audit — the by-id reading
 * tools (`get_email_by_id`, `get_thread`, `extract_action_items`,
 * `extract_meeting`, `reply_to_email`, `forward_email`) were forwarding
 * `args.folder` raw via `as string | undefined` casts.
 */
export declare function optionalFolderHint(raw: unknown, fieldName?: string): string | undefined;
/**
 * Validate an optional `sourceFolder` argument — the full IMAP path the
 * UID(s) live in (e.g. `Folders/Work`, `Labels/Foo`). Uses the full-path
 * validator (`validateTargetFolder`) so embedded `/` is allowed. Returns the
 * value when present, `undefined` for omitted/empty/null, and throws
 * `McpError(InvalidParams, …)` otherwise.
 *
 * VALID-021: previously defined byte-for-byte in both `tools/actions.ts` and
 * `tools/deletion.ts`; consolidated here so the two can't drift.
 */
export declare function optionalSourceFolder(raw: unknown): string | undefined;
/**
 * Coerce an optional numeric tool argument into a finite integer clamped to
 * `[min, max]`, returning `fallback` when the value is absent or non-finite.
 *
 * Centralises the numeric-hygiene pattern that several tool handlers got
 * subtly wrong (TOOL-003/004/005/006/009 in the 2026-05-28 audit):
 *   - `(args.x as number) || dflt` lets a truthy negative (`-50`) through.
 *   - `typeof x === "number"` passes `NaN`/`Infinity`, which then survive
 *     `Math.min(Math.max(1, NaN), cap)` as `NaN` and reach the service/wire.
 *
 * `NaN`, `Infinity`, `-Infinity`, and non-number types all collapse to
 * `fallback`; finite values are truncated toward zero, then clamped.
 *
 * @param raw      - Raw argument value from the MCP tool call (type `unknown`).
 * @param fallback - Value used when `raw` is absent or non-finite.
 * @param min      - Inclusive lower bound after coercion.
 * @param max      - Inclusive upper bound after coercion.
 */
export declare function clampOptionalInt(raw: unknown, fallback: number, min: number, max: number): number;
/**
 * Require a non-empty (after trim) string tool argument. Returns the trimmed
 * value on success or throws `McpError(InvalidParams, …)`. Used for fields
 * where an empty string would reach an upstream API and yield an opaque 4xx
 * (TOOL-007: `aliasPrefix`/`signedSuffix`; TOOL-002: `reason`).
 */
export declare function requireNonEmptyString(raw: unknown, fieldName: string): string;
/**
 * Validate the shape of an `attachments` argument before passing it to a service.
 *
 * Each element must be a plain object with:
 *   - `filename`: a non-empty string
 *   - `content`: a string (base64) or Buffer
 *   - `contentType`: optional — if present, must be a string
 *
 * The service layer performs deeper sanitization (MIME-type format, CRLF
 * stripping, size limits).  This guard only ensures the handler receives
 * structurally valid objects so that service-layer errors are not confusingly
 * attributed to malformed inputs.
 *
 * @param attachments - Raw value of `args.attachments` from the tool call.
 * @returns `null` on success, or an error message string describing the problem.
 */
export declare function validateAttachments(attachments: unknown): string | null;
/**
 * Service-layer attachment LIMIT check (count + per-file + total size) — shared
 * by the SMTP send path and the IMAP saveDraft path. Both enforce the caps even
 * though the tool layer's {@link validateAttachments} already did (VALID-005/006:
 * defense in depth, so an unbounded base64 payload can't OOM the process before
 * it reaches the wire). Operates on already-structured attachments (assumes the
 * shape {@link validateAttachments} guarantees); returns an error string or null.
 */
export declare function validateAttachmentLimits(attachments: ReadonlyArray<{
    filename?: string;
    content?: unknown;
}>): string | null;
/**
 * Coerce a raw `args.attachments` value into a clean `EmailAttachment[]` that
 * carries ONLY the known fields. VALID-015: tools previously did
 * `args.attachments as EmailAttachment[]`, which let attacker-controlled extra
 * keys (e.g. nodemailer's `path`, `href`, `raw`, `encoding`) ride through to
 * the mailer — `path` in particular makes nodemailer read a file from disk.
 *
 * Call AFTER `validateAttachments` has returned null. Returns `undefined` when
 * the input is absent so optional-attachment call sites stay unchanged.
 */
export declare function sanitizeAttachments(attachments: unknown): EmailAttachment[] | undefined;
//# sourceMappingURL=helpers.d.ts.map