Skip to main content

form_net/multipart/
parser.rs

1//! The sans-IO multipart parser: a state machine over the delimiter matcher
2//! and the header reader.
3
4use core::fmt;
5
6use crate::error::{EncodingError, FormError, Limit, Location, PolicyRefusal, SyntaxError};
7use crate::limits::{CHARSET_CAPTURE, EmptyFilePolicy, Limits, Policy, TokenPlacement};
8use crate::multipart::boundary::{Boundary, Hit};
9use crate::multipart::filename::FileName;
10use crate::multipart::header::{Areas, BlockScan, HeadFacts, Text, read_block};
11
12/// Whether a part is a text field or a file.
13///
14/// # Examples
15///
16/// ```
17/// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
18/// let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
19/// assert_eq!(mp.parts()[0].kind(), form_net::PartKind::Field);
20/// # Ok::<(), form_net::FormError>(())
21/// ```
22#[non_exhaustive]
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum PartKind {
25    /// A text field: no `filename` parameter.
26    Field,
27    /// A file: a `filename` parameter (even an empty one) or an accepted
28    /// `filename*`.
29    File,
30}
31
32/// The `charset` a part declared.
33///
34/// `Unspecified` means no `charset` parameter: UTF-8 is assumed unless a
35/// `_charset_` field before the part said otherwise. `Other` is any label
36/// that is not UTF-8 or US-ASCII; its text is refused by every text accessor.
37///
38/// # Examples
39///
40/// ```
41/// use form_net::Charset;
42///
43/// # let body = b"--X\r\nContent-Disposition: form-data; name=a\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n1\r\n--X--";
44/// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
45/// assert_eq!(mp.parts()[0].content_type().unwrap().charset(), Charset::Utf8);
46/// # Ok::<(), form_net::FormError>(())
47/// ```
48#[non_exhaustive]
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50pub enum Charset {
51    /// `utf-8`.
52    Utf8,
53    /// `us-ascii`.
54    Ascii,
55    /// No `charset` parameter.
56    Unspecified,
57    /// Any other label.
58    Other,
59}
60
61impl Charset {
62    pub(crate) fn from_label(label: &[u8]) -> Charset {
63        if label.eq_ignore_ascii_case(b"utf-8") {
64            Charset::Utf8
65        } else if label.eq_ignore_ascii_case(b"us-ascii") {
66            Charset::Ascii
67        } else {
68            Charset::Other
69        }
70    }
71}
72
73/// One step of the parser: how many input bytes it consumed, and what
74/// happened.
75#[non_exhaustive]
76#[derive(Debug)]
77pub struct Step<'p> {
78    /// Bytes of the input consumed; pass the rest (and later new bytes) to
79    /// the next call.
80    pub consumed: usize,
81    /// What happened.
82    pub event: Event<'p>,
83}
84
85/// What the parser found.
86///
87/// Per part the order is always `PartStart`, any number of `Data` (none for
88/// an empty part), `PartEnd`. A `Data` slice is never empty. After the last
89/// part comes `End`. `NeedInput` can appear anywhere between.
90///
91/// `Debug` prints the length of `Data`, never the bytes.
92#[non_exhaustive]
93pub enum Event<'p> {
94    /// A part's headers were read and checked.
95    PartStart(PartHead<'p>),
96    /// Bytes of the current part's data.
97    Data(&'p [u8]),
98    /// The current part ended.
99    PartEnd(PartSummary),
100    /// The close delimiter was read: the form is complete.
101    End,
102    /// Every byte given was consumed; give more.
103    NeedInput,
104}
105
106impl fmt::Debug for Event<'_> {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        match self {
109            Self::PartStart(head) => f.debug_tuple("PartStart").field(head).finish(),
110            Self::Data(bytes) => write!(f, "Data({} bytes)", bytes.len()),
111            Self::PartEnd(summary) => f.debug_tuple("PartEnd").field(summary).finish(),
112            Self::End => f.write_str("End"),
113            Self::NeedInput => f.write_str("NeedInput"),
114        }
115    }
116}
117
118/// What one part was.
119#[non_exhaustive]
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct PartSummary {
122    /// The part index, from 0.
123    pub index: u32,
124    /// Field or file.
125    pub kind: PartKind,
126    /// The length of the part's data.
127    pub bytes: u64,
128    /// Whether the part was skipped (by `skip_part` or an inspector).
129    pub skipped: bool,
130}
131
132/// The validated header block of one part, borrowed.
133///
134/// A head is only produced after every header budget and rule passed.
135#[derive(Clone, Copy)]
136pub struct PartHead<'p> {
137    index: u32,
138    block: &'p [u8],
139    name: &'p str,
140    name_raw: &'p [u8],
141    kind: PartKind,
142    filename: Option<FileName<'p>>,
143    content_type: Option<&'p [u8]>,
144    charset: Charset,
145}
146
147impl fmt::Debug for PartHead<'_> {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.debug_struct("PartHead")
150            .field("index", &self.index)
151            .field("name", &self.name)
152            .field("kind", &self.kind)
153            .field("filename", &self.filename)
154            .finish_non_exhaustive()
155    }
156}
157
158impl<'p> PartHead<'p> {
159    /// A head over bytes kept elsewhere (an owned copy).
160    #[cfg(feature = "alloc")]
161    #[allow(clippy::too_many_arguments)]
162    pub(crate) const fn from_parts(
163        index: u32,
164        block: &'p [u8],
165        name: &'p str,
166        name_raw: &'p [u8],
167        kind: PartKind,
168        filename: Option<FileName<'p>>,
169        content_type: Option<&'p [u8]>,
170        charset: Charset,
171    ) -> Self {
172        Self {
173            index,
174            block,
175            name,
176            name_raw,
177            kind,
178            filename,
179            content_type,
180            charset,
181        }
182    }
183
184    /// The header block, the final CRLF CRLF included.
185    #[cfg(feature = "alloc")]
186    pub(crate) const fn block(&self) -> &'p [u8] {
187        self.block
188    }
189
190    /// The part index, from 0, in body order.
191    #[must_use]
192    pub const fn index(&self) -> u32 {
193        self.index
194    }
195
196    /// The field name, with `%0A`, `%0D`, `%22` decoded to LF, CR, `"` (no
197    /// other escape is decoded). Always valid UTF-8 and never empty.
198    #[must_use]
199    pub const fn name(&self) -> &'p str {
200        self.name
201    }
202
203    /// The field name as sent.
204    #[must_use]
205    pub const fn name_raw(&self) -> &'p [u8] {
206        self.name_raw
207    }
208
209    /// Field or file.
210    #[must_use]
211    pub const fn kind(&self) -> PartKind {
212        self.kind
213    }
214
215    /// Whether this is a file part.
216    #[must_use]
217    pub const fn is_file(&self) -> bool {
218        matches!(self.kind, PartKind::File)
219    }
220
221    /// The client's file name, for file parts.
222    #[must_use]
223    pub const fn filename(&self) -> Option<FileName<'p>> {
224        self.filename
225    }
226
227    /// The declared `Content-Type`: a hint, never trusted.
228    #[must_use]
229    pub fn content_type(&self) -> Option<ContentType<'p>> {
230        self.content_type.and_then(ContentType::parse)
231    }
232
233    /// The part's own `charset` parameter (`Unspecified` when absent).
234    #[must_use]
235    pub const fn charset(&self) -> Charset {
236        self.charset
237    }
238
239    /// Every header line, in order, as raw `(name, value)` bytes.
240    #[must_use]
241    pub fn headers(&self) -> Headers<'p> {
242        Headers { rest: self.block }
243    }
244
245    /// The size of the header block, the final CRLF CRLF included.
246    #[must_use]
247    pub const fn header_bytes(&self) -> u32 {
248        self.block.len() as u32
249    }
250}
251
252/// A part's declared `Content-Type`, already checked. **A hint**: the client
253/// chose it.
254///
255/// # Examples
256///
257/// ```
258/// # let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.png\r\nContent-Type: Image/PNG; q=\"x y\"\r\n\r\n\r\n--X--";
259/// # let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", body)?;
260/// let ct = mp.file("f").unwrap().content_type().unwrap();
261/// assert!(ct.is("image", "png"));
262/// assert_eq!((ct.type_(), ct.subtype()), ("Image", "PNG"));
263/// assert_eq!(ct.parameter("Q"), Some(&b"x y"[..]));
264/// # Ok::<(), form_net::FormError>(())
265/// ```
266#[derive(Clone, Copy)]
267pub struct ContentType<'p> {
268    raw: &'p [u8],
269    media: parse_net::MediaType<'p>,
270}
271
272impl fmt::Debug for ContentType<'_> {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        f.debug_struct("ContentType")
275            .field("type", &self.type_())
276            .field("subtype", &self.subtype())
277            .finish_non_exhaustive()
278    }
279}
280
281impl<'p> ContentType<'p> {
282    pub(crate) fn parse(raw: &'p [u8]) -> Option<ContentType<'p>> {
283        parse_net::media::parse(raw, &crate::kind::HEADER_LIMITS)
284            .ok()
285            .map(|media| ContentType { raw, media })
286    }
287
288    /// The value as sent.
289    #[must_use]
290    pub const fn raw(&self) -> &'p [u8] {
291        self.raw
292    }
293
294    /// The type, as sent.
295    #[must_use]
296    pub fn type_(&self) -> &'p str {
297        core::str::from_utf8(self.media.type_()).unwrap_or("")
298    }
299
300    /// The subtype, as sent.
301    #[must_use]
302    pub fn subtype(&self) -> &'p str {
303        core::str::from_utf8(self.media.subtype()).unwrap_or("")
304    }
305
306    /// Whether the type and subtype are these, ASCII case-insensitively.
307    #[must_use]
308    pub fn is(&self, type_: &str, subtype: &str) -> bool {
309        self.media.is(type_, subtype)
310    }
311
312    /// A parameter's value by name (ASCII case-insensitive), without
313    /// surrounding quotes; backslashes are literal, as browsers send them.
314    #[must_use]
315    pub fn parameter(&self, name: &str) -> Option<&'p [u8]> {
316        self.media
317            .params()
318            .get(name)
319            .map(|w| crate::kind::word_raw(&w))
320    }
321
322    /// The `charset` parameter.
323    #[must_use]
324    pub fn charset(&self) -> Charset {
325        self.parameter("charset")
326            .map_or(Charset::Unspecified, Charset::from_label)
327    }
328}
329
330/// Every header line of a part, in order, as raw `(name, value)` bytes with
331/// the value trimmed.
332///
333/// `Debug` prints the header names only: values carry file names.
334#[derive(Clone)]
335pub struct Headers<'p> {
336    rest: &'p [u8],
337}
338
339impl fmt::Debug for Headers<'_> {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        let mut list = f.debug_list();
342        for (name, _) in self.clone() {
343            list.entry(&core::str::from_utf8(name).unwrap_or("?"));
344        }
345        list.finish()
346    }
347}
348
349impl<'p> Iterator for Headers<'p> {
350    type Item = (&'p [u8], &'p [u8]);
351
352    fn next(&mut self) -> Option<Self::Item> {
353        let lf = self.rest.iter().position(|&b| b == b'\n')?;
354        let line = self.rest.get(..lf.saturating_sub(1)).unwrap_or(&[]);
355        self.rest = self.rest.get(lf.wrapping_add(1)..).unwrap_or(&[]);
356        if line.is_empty() {
357            self.rest = &[];
358            return None;
359        }
360        let colon = line.iter().position(|&b| b == b':').unwrap_or(line.len());
361        let name = line.get(..colon).unwrap_or(&[]);
362        let value = line.get(colon.wrapping_add(1)..).unwrap_or(&[]);
363        Some((name, parse_net::text::trim_ows(value)))
364    }
365}
366
367#[derive(Clone, Copy, Debug, PartialEq, Eq)]
368enum State {
369    Preamble,
370    /// After a delimiter: expecting `--`, padding, or CRLF.
371    AfterDelimiter(After),
372    Headers,
373    Body,
374    /// The delimiter that ends the current part was found; `PartEnd` is due.
375    EndOfPart,
376    Epilogue,
377    Poisoned,
378}
379
380#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381enum After {
382    Start,
383    Dash,
384    Padding(u8),
385    Cr,
386}
387
388/// The token field's progress.
389#[derive(Clone, Copy, Debug, PartialEq, Eq)]
390enum Token {
391    NotSeen,
392    Capturing,
393    Captured,
394}
395
396const SKIPPING: u8 = 1;
397const IS_TOKEN: u8 = 2;
398const IS_CHARSET: u8 = 4;
399const DEFERRED: u8 = 8;
400
401/// Where a head's block is.
402#[derive(Clone, Copy, Debug)]
403pub(crate) enum Source {
404    /// In the input, from this index.
405    Input(usize),
406    /// In the parser buffer, from 0.
407    Buffer,
408}
409
410/// What a call to `advance` produced, as positions only.
411#[derive(Clone, Copy, Debug)]
412pub(crate) enum Outcome {
413    PartStart(HeadFacts, Source),
414    /// `input[start..end]`.
415    DataInput(usize, usize),
416    /// The first bytes of the delimiter.
417    DataDelimiter(usize),
418    PartEnd(PartSummary),
419    End,
420    NeedInput,
421}
422
423/// The sans-IO `multipart/form-data` parser.
424///
425/// Feed it bytes as they arrive, in chunks of any size split at any byte;
426/// it returns one [`Event`] per call and how many bytes it consumed. The
427/// events are identical for every split of the same body. It never
428/// allocates after construction and never rescans a byte: the delimiter
429/// search is a streaming Knuth-Morris-Pratt automaton with an eight-byte skip
430/// to the next CR.
431///
432/// The buffer holds the header block, the decoded field name, the decoded
433/// `filename*` (under `AcceptAlone`), the token, and the `_charset_` value:
434/// [`Limits::multipart_buffer_len`] bytes. It is wiped on `reset` and on
435/// drop.
436///
437/// # Examples
438///
439/// ```
440/// use form_net::{Event, FormKind, Limits, MultipartParser, Policy};
441///
442/// let content_type = b"multipart/form-data; boundary=X";
443/// let body = b"--X\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\nHoliday\r\n--X--\r\n";
444/// let FormKind::Multipart(boundary) = FormKind::detect(content_type)? else { unreachable!() };
445/// let mut parser = MultipartParser::new(boundary, &Limits::DEFAULT, &Policy::DEFAULT)?;
446///
447/// let mut events = Vec::new();
448/// let mut chunks = body.chunks(7).peekable();     // any split gives the same events
449/// while let Some(chunk) = chunks.next() {
450///     let last = chunks.peek().is_none();
451///     let mut rest = chunk;
452///     loop {
453///         let step = parser.parse(rest, last)?;
454///         rest = &rest[step.consumed..];
455///         match step.event {
456///             Event::PartStart(head) => events.push(format!("start {}", head.name())),
457///             Event::Data(bytes) => events.push(format!("data {}", bytes.len())),
458///             Event::PartEnd(summary) => events.push(format!("end {}", summary.bytes)),
459///             Event::End => break,
460///             Event::NeedInput => break,
461///             _ => {}
462///         }
463///     }
464/// }
465/// assert!(parser.is_done());
466/// assert_eq!(events.first().map(String::as_str), Some("start title"));
467/// assert_eq!(events.last().map(String::as_str), Some("end 7"));
468/// # Ok::<(), form_net::FormError>(())
469/// ```
470pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> {
471    boundary: Boundary,
472    limits: Limits,
473    policy: Policy,
474    buffer: B,
475    state: State,
476    /// Delimiter bytes matched and not yet known to be data.
477    k: u8,
478    /// Bytes of a lone CR or LF and `--boundary` matched (D-113); while
479    /// `k > 0` it follows from `k`.
480    bare: u8,
481    flags: u8,
482    token: Token,
483    token_len: u16,
484    charset_len: u8,
485    charset_done: bool,
486    scan: BlockScan,
487    /// Header bytes copied into the buffer.
488    copied: u32,
489    /// The longest header block read so far: every byte the parser writes
490    /// into the header, name, and file-name areas lies within this many
491    /// bytes of each area's start (decoding never lengthens), so the wipe
492    /// covers exactly what was written (D-103).
493    dirty: u32,
494    parts: u32,
495    files: u32,
496    fields: u32,
497    kind: PartKind,
498    part_bytes: u64,
499    text_total: u64,
500    data_start: u64,
501    preamble: u64,
502    epilogue: u64,
503    /// Where the current header block starts in the body.
504    head_start: u64,
505    /// Where the epilogue starts in the body.
506    epilogue_start: u64,
507    bytes_seen: u64,
508}
509
510impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for MultipartParser<B> {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        f.debug_struct("MultipartParser")
513            .field("boundary", &self.boundary)
514            .field("state", &self.state)
515            .field("bytes_seen", &self.bytes_seen)
516            .field("parts_seen", &self.parts)
517            .finish_non_exhaustive()
518    }
519}
520
521#[cfg(feature = "alloc")]
522impl MultipartParser<alloc::vec::Vec<u8>> {
523    /// A parser with its own buffer of
524    /// [`Limits::multipart_buffer_len`] bytes, allocated once.
525    ///
526    /// # Errors
527    ///
528    /// `InvalidLimits` from validating the limits with the policy.
529    ///
530    /// # Examples
531    ///
532    /// ```
533    /// use form_net::{Boundary, Limits, MultipartParser, Policy};
534    ///
535    /// let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
536    /// assert_eq!(parser.bytes_seen(), 0);
537    /// # Ok::<(), form_net::FormError>(())
538    /// ```
539    pub fn new(boundary: Boundary, limits: &Limits, policy: &Policy) -> Result<Self, FormError> {
540        limits.validate_with(policy)?;
541        let buffer = alloc::vec![0u8; limits.multipart_buffer_len(policy)];
542        Self::with_buffer(boundary, limits, policy, buffer)
543    }
544}
545
546impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
547    /// A parser over a buffer the caller provides; it never allocates.
548    ///
549    /// # Errors
550    ///
551    /// `InvalidLimits`; `BufferTooSmall` when `buffer` is shorter than
552    /// [`Limits::multipart_buffer_len`].
553    ///
554    /// # Examples
555    ///
556    /// ```
557    /// use form_net::{Boundary, Limits, MultipartParser, Policy};
558    ///
559    /// const LIMITS: Limits = Limits::DEFAULT.with_max_part_header_bytes(1024);
560    /// const LEN: usize = LIMITS.multipart_buffer_len(&Policy::DEFAULT);
561    /// let mut buffer = [0u8; LEN];
562    /// let parser = MultipartParser::with_buffer(Boundary::new(b"X")?, &LIMITS, &Policy::DEFAULT, &mut buffer[..])?;
563    /// # Ok::<(), form_net::FormError>(())
564    /// ```
565    pub fn with_buffer(
566        boundary: Boundary,
567        limits: &Limits,
568        policy: &Policy,
569        buffer: B,
570    ) -> Result<Self, FormError> {
571        limits.validate_with(policy)?;
572        let needed = limits.multipart_buffer_len(policy);
573        if buffer.as_ref().len() < needed {
574            return Err(FormError::BufferTooSmall { needed });
575        }
576        Ok(Self {
577            boundary,
578            limits: *limits,
579            policy: *policy,
580            buffer,
581            state: State::Preamble,
582            // The body may start with `--boundary`: start as if CRLF had
583            // been seen.
584            k: 2,
585            bare: 1,
586            flags: 0,
587            token: Token::NotSeen,
588            token_len: 0,
589            charset_len: 0,
590            charset_done: false,
591            scan: BlockScan::default(),
592            copied: 0,
593            dirty: 0,
594            parts: 0,
595            files: 0,
596            fields: 0,
597            kind: PartKind::Field,
598            part_bytes: 0,
599            text_total: 0,
600            data_start: 0,
601            preamble: 0,
602            epilogue: 0,
603            head_start: 0,
604            epilogue_start: 0,
605            bytes_seen: 0,
606        })
607    }
608
609    /// Consumes a prefix of `input` and returns one event. `eof = true` says
610    /// `input` ends the body.
611    ///
612    /// # Errors
613    ///
614    /// Every multipart error (see the crate's `docs/API.md`); `Poisoned`
615    /// after any error until [`reset`](Self::reset).
616    pub fn parse<'p>(&'p mut self, input: &'p [u8], eof: bool) -> Result<Step<'p>, FormError> {
617        let (consumed, outcome) = self.advance(input, eof)?;
618        Ok(Step {
619            consumed,
620            event: self.view(outcome, input),
621        })
622    }
623
624    /// Discards the rest of the current part: its bytes are consumed and
625    /// counted against the budgets but produce no `Data` events, and its
626    /// summary has `skipped = true`. Outside a part it does nothing.
627    pub fn skip_part(&mut self) {
628        if matches!(self.state, State::Body) {
629            self.flags |= SKIPPING;
630        }
631    }
632
633    /// Returns the parser to its initial state for a new body, wipes its
634    /// buffer, and clears the poisoned state. It never allocates.
635    pub fn reset(&mut self, boundary: Boundary) {
636        self.wipe_used();
637        let limits = self.limits;
638        let policy = self.policy;
639        self.boundary = boundary;
640        self.state = State::Preamble;
641        self.k = 2;
642        self.bare = 1;
643        self.flags = 0;
644        self.token = Token::NotSeen;
645        self.token_len = 0;
646        self.charset_len = 0;
647        self.charset_done = false;
648        self.scan = BlockScan::default();
649        self.copied = 0;
650        self.parts = 0;
651        self.files = 0;
652        self.fields = 0;
653        self.kind = PartKind::Field;
654        self.part_bytes = 0;
655        self.text_total = 0;
656        self.data_start = 0;
657        self.preamble = 0;
658        self.epilogue = 0;
659        self.head_start = 0;
660        self.epilogue_start = 0;
661        self.bytes_seen = 0;
662        self.limits = limits;
663        self.policy = policy;
664    }
665
666    /// Body bytes consumed so far.
667    #[must_use]
668    pub const fn bytes_seen(&self) -> u64 {
669        self.bytes_seen
670    }
671
672    /// Parts started so far.
673    #[must_use]
674    pub const fn parts_seen(&self) -> u32 {
675        self.parts
676    }
677
678    /// File parts counted so far.
679    #[must_use]
680    pub const fn files_seen(&self) -> u32 {
681        self.files
682    }
683
684    /// Whether the close delimiter was read.
685    #[must_use]
686    pub fn is_done(&self) -> bool {
687        self.state == State::Epilogue
688    }
689
690    /// The limits the parser was built with.
691    #[must_use]
692    pub const fn limits(&self) -> &Limits {
693        &self.limits
694    }
695
696    /// The policy the parser was built with.
697    #[must_use]
698    pub const fn policy(&self) -> &Policy {
699        &self.policy
700    }
701
702    /// The token field's value, once its part has ended.
703    #[must_use]
704    pub fn captured_token(&self) -> Option<&str> {
705        if self.token != Token::Captured {
706            return None;
707        }
708        let start = self.token_area();
709        let bytes = self
710            .buffer
711            .as_ref()
712            .get(start..start.saturating_add(usize::from(self.token_len)))?;
713        core::str::from_utf8(bytes).ok()
714    }
715
716    /// The value of the last complete `_charset_` field.
717    #[must_use]
718    pub fn declared_charset(&self) -> Option<&[u8]> {
719        if !self.charset_done {
720            return None;
721        }
722        let start = self.charset_area();
723        self.buffer
724            .as_ref()
725            .get(start..start.saturating_add(usize::from(self.charset_len)))
726    }
727
728    /// The decoded name of the last head, `len` bytes in the name area.
729    #[cfg(feature = "alloc")]
730    pub(crate) fn area_name(&self, len: u32) -> &str {
731        let start = self.name_area();
732        let bytes = self
733            .buffer
734            .as_ref()
735            .get(start..start.saturating_add(len as usize))
736            .unwrap_or(&[]);
737        core::str::from_utf8(bytes).unwrap_or("")
738    }
739
740    /// The decoded `filename*` of the last head, `len` bytes.
741    #[cfg(feature = "alloc")]
742    pub(crate) fn area_filename(&self, len: u32) -> &[u8] {
743        let start = self.filename_area();
744        self.buffer
745            .as_ref()
746            .get(start..start.saturating_add(len as usize))
747            .unwrap_or(&[])
748    }
749
750    /// Where the current part's data starts in the body.
751    #[cfg(feature = "alloc")]
752    pub(crate) const fn data_start(&self) -> u64 {
753        self.data_start
754    }
755
756    // Buffer layout: header block, name, filename (AcceptAlone), token, charset.
757
758    fn name_area(&self) -> usize {
759        self.limits.max_part_header_bytes as usize
760    }
761
762    fn filename_area(&self) -> usize {
763        self.name_area().saturating_add(self.limits.name() as usize)
764    }
765
766    fn token_area(&self) -> usize {
767        let filename = if matches!(
768            self.policy.extended_filename,
769            crate::ExtendedFilenamePolicy::AcceptAlone
770        ) {
771            self.limits.filename() as usize
772        } else {
773            0
774        };
775        self.filename_area().saturating_add(filename)
776    }
777
778    fn charset_area(&self) -> usize {
779        let token = if self.policy.token_field.is_some() {
780            usize::from(self.limits.token())
781        } else {
782            0
783        };
784        self.token_area().saturating_add(token)
785    }
786
787    /// The location of the current part's header block.
788    fn head_at(&self) -> Location {
789        Location::in_part(self.parts.checked_sub(1), self.head_start)
790    }
791
792    /// The location of the first data byte past a budget of `limit` of
793    /// which `used` is spent, in the current part.
794    fn excess(&self, used: u64, limit: u64) -> Location {
795        let into_part = self.part_bytes.saturating_add(limit.saturating_sub(used));
796        Location::in_part(
797            self.parts.checked_sub(1),
798            self.data_start.saturating_add(into_part),
799        )
800    }
801
802    fn at(&self, pos: usize) -> Location {
803        self.at_offset(self.bytes_seen.saturating_add(pos as u64))
804    }
805
806    /// As [`at`](Self::at), for an offset in the whole body.
807    fn at_offset(&self, offset: u64) -> Location {
808        let part = if matches!(self.state, State::Preamble | State::Epilogue) {
809            None
810        } else {
811            self.parts.checked_sub(1)
812        };
813        Location::in_part(part, offset)
814    }
815
816    /// Runs the state machine and returns positions only, so callers that
817    /// own the input for longer (the buffered parser) can slice it
818    /// themselves.
819    pub(crate) fn advance(
820        &mut self,
821        input: &[u8],
822        eof: bool,
823    ) -> Result<(usize, Outcome), FormError> {
824        if self.state == State::Poisoned {
825            return Err(FormError::Poisoned);
826        }
827        match self.run(input, eof) {
828            Ok((consumed, outcome)) => {
829                self.bytes_seen = self.bytes_seen.saturating_add(consumed as u64);
830                Ok((consumed, outcome))
831            }
832            Err(e) => {
833                self.state = State::Poisoned;
834                Err(e)
835            }
836        }
837    }
838
839    fn run(&mut self, input: &[u8], eof: bool) -> Result<(usize, Outcome), FormError> {
840        if self.bytes_seen.saturating_add(input.len() as u64) > self.limits.body() {
841            return Err(FormError::limit(
842                Limit::Body,
843                Location::at(self.limits.body()),
844            ));
845        }
846        let mut pos = 0usize;
847        loop {
848            let hay = input.get(pos..).unwrap_or(&[]);
849            match self.state {
850                State::Poisoned => return Err(FormError::Poisoned),
851                State::Preamble => {
852                    if let Some(after) = self.preamble_step(hay)? {
853                        pos = pos.wrapping_add(after);
854                        self.state = State::AfterDelimiter(After::Start);
855                        continue;
856                    }
857                    if eof {
858                        return Err(FormError::syntax(
859                            SyntaxError::NoInitialDelimiter,
860                            self.at(input.len()),
861                        ));
862                    }
863                    return Ok((input.len(), Outcome::NeedInput));
864                }
865                State::AfterDelimiter(sub) => {
866                    let Some(&c) = hay.first() else {
867                        return self.need_input(input.len(), eof);
868                    };
869                    pos = pos.wrapping_add(1);
870                    let at = self.at(pos);
871                    self.state = match (sub, c) {
872                        (After::Start, b'-') => State::AfterDelimiter(After::Dash),
873                        (After::Dash, b'-') => {
874                            self.state = State::Epilogue;
875                            self.epilogue_start = self.bytes_seen.saturating_add(pos as u64);
876                            return Ok((pos, Outcome::End));
877                        }
878                        (After::Start, b' ' | b'\t') => {
879                            if self.limits.max_padding_bytes == 0 {
880                                return Err(FormError::limit(Limit::Padding, at));
881                            }
882                            State::AfterDelimiter(After::Padding(1))
883                        }
884                        (After::Padding(n), b' ' | b'\t') => {
885                            if n >= self.limits.max_padding_bytes {
886                                return Err(FormError::limit(Limit::Padding, at));
887                            }
888                            State::AfterDelimiter(After::Padding(n.saturating_add(1)))
889                        }
890                        (After::Start | After::Padding(_), b'\r') => {
891                            State::AfterDelimiter(After::Cr)
892                        }
893                        (After::Cr, b'\n') => {
894                            self.parts = self.parts.saturating_add(1);
895                            if self.parts > self.limits.max_parts {
896                                return Err(FormError::limit(Limit::Parts, self.at(pos)));
897                            }
898                            self.scan = BlockScan::default();
899                            self.copied = 0;
900                            self.head_start = self.bytes_seen.saturating_add(pos as u64);
901                            State::Headers
902                        }
903                        (After::Start | After::Padding(_), b'\n') => {
904                            return Err(FormError::syntax(SyntaxError::BareLineFeed, at));
905                        }
906                        _ => return Err(FormError::syntax(SyntaxError::BoundaryInContent, at)),
907                    };
908                }
909                State::Headers => {
910                    let at = self.head_at();
911                    match self.scan.feed(hay, &self.limits, at)? {
912                        Some(end) => {
913                            let source = if self.copied == 0 {
914                                Source::Input(pos)
915                            } else {
916                                self.copy_header(hay.get(..end).unwrap_or(&[]), at)?;
917                                Source::Buffer
918                            };
919                            let block_start_in_input = pos;
920                            pos = pos.wrapping_add(end);
921                            let facts =
922                                self.read_head(input, source, block_start_in_input, end, at)?;
923                            let (is_token, is_charset) = {
924                                let block = self.block(&facts, source, input);
925                                let name = self.name_bytes(&facts, block);
926                                let token = self
927                                    .policy
928                                    .token_field
929                                    .is_some_and(|t| name == t.name().as_bytes());
930                                (token, name == b"_charset_")
931                            };
932                            self.start_part(&facts, is_token, is_charset, at)?;
933                            self.data_start = self.bytes_seen.saturating_add(pos as u64);
934                            return Ok((pos, Outcome::PartStart(facts, source)));
935                        }
936                        None => {
937                            self.copy_header(hay, at)?;
938                            return self.need_input(input.len(), eof);
939                        }
940                    }
941                }
942                State::Body => {
943                    if hay.is_empty() {
944                        return self.need_input(input.len(), eof);
945                    }
946                    match self.body_step(hay, pos, input.len(), eof)? {
947                        BodyStep::Emit(consumed, outcome) => return Ok((consumed, outcome)),
948                        BodyStep::Continue(next) => pos = next,
949                    }
950                }
951                State::EndOfPart => {
952                    let summary = self.end_part(self.at(pos))?;
953                    self.state = State::AfterDelimiter(After::Start);
954                    return Ok((pos, Outcome::PartEnd(summary)));
955                }
956                State::Epilogue => {
957                    self.epilogue = self.epilogue.saturating_add(hay.len() as u64);
958                    if self.epilogue > self.limits.epilogue() {
959                        let at = self.epilogue_start.saturating_add(self.limits.epilogue());
960                        return Err(FormError::limit(Limit::Epilogue, Location::at(at)));
961                    }
962                    return Ok((input.len(), Outcome::End));
963                }
964            }
965        }
966    }
967
968    fn need_input(&self, len: usize, eof: bool) -> Result<(usize, Outcome), FormError> {
969        if eof {
970            Err(FormError::syntax(SyntaxError::Truncated, self.at(len)))
971        } else {
972            Ok((len, Outcome::NeedInput))
973        }
974    }
975
976    /// The refusal for `--boundary` after a lone CR or LF, ending at `after`
977    /// in a `hay` that starts at `pos` in the input (D-113).
978    /// The location is the line break's, in whichever chunk it came.
979    fn bare_break(&self, after: usize, pos: usize) -> FormError {
980        let end = self
981            .bytes_seen
982            .saturating_add(pos.wrapping_add(after) as u64);
983        let bare = self.boundary.len().saturating_sub(1) as u64;
984        FormError::syntax(
985            SyntaxError::BoundaryInContent,
986            self.at_offset(end.saturating_sub(bare)),
987        )
988    }
989
990    /// Scans the preamble. Returns the index in `hay` just after the first
991    /// delimiter, or `None` when all of `hay` was consumed.
992    fn preamble_step(&mut self, hay: &[u8]) -> Result<Option<usize>, FormError> {
993        let len = self.boundary.len() as u64;
994        // A delimiter must end within max_preamble_bytes + its own length.
995        let budget = self.limits.preamble().saturating_add(len);
996        let room = usize::try_from(budget.saturating_sub(self.preamble)).unwrap_or(usize::MAX);
997        let window = hay.get(..room.min(hay.len())).unwrap_or(hay);
998        let (k, m, found) = self
999            .boundary
1000            .run(usize::from(self.k), usize::from(self.bare), window);
1001        self.k = k as u8;
1002        self.bare = m as u8;
1003        match found {
1004            Hit::BareBreak(after) => Err(self.bare_break(after, 0)),
1005            Hit::Delimiter(after) => {
1006                // Real bytes up to the end of the delimiter; a delimiter that
1007                // includes the two virtual bytes (CRLF) starts the body.
1008                let end = self.preamble.saturating_add(after as u64);
1009                let preamble = end.saturating_sub(len);
1010                if preamble > self.limits.preamble() {
1011                    return Err(FormError::limit(
1012                        Limit::Preamble,
1013                        Location::at(self.limits.preamble()),
1014                    ));
1015                }
1016                self.k = 0;
1017                self.bare = 0;
1018                Ok(Some(after))
1019            }
1020            Hit::None => {
1021                self.preamble = self.preamble.saturating_add(window.len() as u64);
1022                if window.len() < hay.len() || self.preamble >= budget {
1023                    return Err(FormError::limit(
1024                        Limit::Preamble,
1025                        Location::at(self.limits.preamble()),
1026                    ));
1027                }
1028                Ok(None)
1029            }
1030        }
1031    }
1032
1033    fn copy_header(&mut self, bytes: &[u8], at: Location) -> Result<(), FormError> {
1034        let start = self.copied as usize;
1035        let end = start.saturating_add(bytes.len());
1036        if end > self.limits.max_part_header_bytes as usize {
1037            return Err(FormError::limit(Limit::PartHeaderBytes, at));
1038        }
1039        match self.buffer.as_mut().get_mut(start..end) {
1040            Some(dst) => dst.copy_from_slice(bytes),
1041            None => return Err(FormError::limit(Limit::PartHeaderBytes, at)),
1042        }
1043        self.copied = end as u32;
1044        self.dirty = self.dirty.max(self.copied);
1045        Ok(())
1046    }
1047
1048    fn read_head(
1049        &mut self,
1050        input: &[u8],
1051        source: Source,
1052        start: usize,
1053        end: usize,
1054        at: Location,
1055    ) -> Result<HeadFacts, FormError> {
1056        let name_area = self.name_area();
1057        let filename_area = self.filename_area();
1058        let filename_end = self.token_area();
1059        let limits = self.limits;
1060        let policy = self.policy;
1061        let copied = self.copied as usize;
1062        let block_len = match source {
1063            Source::Input(_) => end,
1064            Source::Buffer => copied,
1065        };
1066        self.dirty = self.dirty.max(u32::try_from(block_len).unwrap_or(u32::MAX));
1067        let buf = self.buffer.as_mut();
1068        let (head, rest) = buf.split_at_mut(name_area.min(buf.len()));
1069        let (name, rest) =
1070            rest.split_at_mut(filename_area.saturating_sub(name_area).min(rest.len()));
1071        let filename = rest
1072            .get_mut(..filename_end.saturating_sub(filename_area))
1073            .unwrap_or(&mut []);
1074        let block = match source {
1075            Source::Input(_) => input.get(start..start.saturating_add(end)).unwrap_or(&[]),
1076            Source::Buffer => head.get(..copied).unwrap_or(&[]),
1077        };
1078        read_block(block, &limits, &policy, Areas { name, filename }, at)
1079    }
1080
1081    /// The budgets and rules that apply when a part starts.
1082    fn start_part(
1083        &mut self,
1084        facts: &HeadFacts,
1085        is_token: bool,
1086        is_charset: bool,
1087        at: Location,
1088    ) -> Result<(), FormError> {
1089        self.state = State::Body;
1090        self.k = 0;
1091        // The data starts after the blank line's LF: `--boundary` right
1092        // there reads as an empty part to an RFC 2046 reader (D-113).
1093        self.bare = 1;
1094        self.flags = 0;
1095        self.kind = facts.kind;
1096        self.part_bytes = 0;
1097        match facts.kind {
1098            PartKind::Field => {
1099                self.fields = self.fields.saturating_add(1);
1100                if self.fields > self.limits.max_fields {
1101                    return Err(FormError::limit(Limit::Fields, at));
1102                }
1103                if is_token {
1104                    if self.token != Token::NotSeen {
1105                        return Err(FormError::policy(PolicyRefusal::DuplicateToken, at));
1106                    }
1107                    self.token = Token::Capturing;
1108                    self.token_len = 0;
1109                    self.flags |= IS_TOKEN;
1110                }
1111                if is_charset {
1112                    self.flags |= IS_CHARSET;
1113                    self.charset_len = 0;
1114                    self.charset_done = false;
1115                }
1116            }
1117            PartKind::File => {
1118                let empty = matches!(facts.filename, Some((Text::Block(span), _)) if span.len == 0)
1119                    || matches!(facts.filename, Some((Text::Area(0), _)));
1120                if empty && self.policy.empty_files == EmptyFilePolicy::Skip {
1121                    self.flags |= DEFERRED;
1122                } else {
1123                    self.count_file(at)?;
1124                }
1125            }
1126        }
1127        Ok(())
1128    }
1129
1130    /// The block of a head, wherever it is.
1131    fn block<'a>(&'a self, facts: &HeadFacts, source: Source, input: &'a [u8]) -> &'a [u8] {
1132        let len = facts.block_len as usize;
1133        match source {
1134            Source::Input(start) => input.get(start..start.saturating_add(len)).unwrap_or(&[]),
1135            Source::Buffer => self.buffer.as_ref().get(..len).unwrap_or(&[]),
1136        }
1137    }
1138
1139    /// The decoded name of a head.
1140    fn name_bytes<'a>(&'a self, facts: &HeadFacts, block: &'a [u8]) -> &'a [u8] {
1141        match facts.name {
1142            Text::Block(span) => span.get(block),
1143            Text::Area(len) => {
1144                let start = self.name_area();
1145                self.buffer
1146                    .as_ref()
1147                    .get(start..start.saturating_add(len as usize))
1148                    .unwrap_or(&[])
1149            }
1150        }
1151    }
1152
1153    /// The borrowed view of a head.
1154    fn head_view<'p>(&'p self, facts: &HeadFacts, source: Source, input: &'p [u8]) -> PartHead<'p> {
1155        let block = self.block(facts, source, input);
1156        let name = core::str::from_utf8(self.name_bytes(facts, block)).unwrap_or("");
1157        let filename = facts.filename.map(|(text, origin)| {
1158            let raw = match text {
1159                Text::Block(span) => span.get(block),
1160                Text::Area(len) => {
1161                    let start = self.filename_area();
1162                    self.buffer
1163                        .as_ref()
1164                        .get(start..start.saturating_add(len as usize))
1165                        .unwrap_or(&[])
1166                }
1167            };
1168            FileName::new(raw, origin)
1169        });
1170        PartHead {
1171            index: self.parts.saturating_sub(1),
1172            block,
1173            name,
1174            name_raw: facts.name_raw.get(block),
1175            kind: facts.kind,
1176            filename,
1177            content_type: facts.content_type.map(|span| span.get(block)),
1178            charset: facts.charset,
1179        }
1180    }
1181
1182    /// Turns positions into borrowed events.
1183    fn view<'p>(&'p self, outcome: Outcome, input: &'p [u8]) -> Event<'p> {
1184        match outcome {
1185            Outcome::PartStart(facts, source) => {
1186                Event::PartStart(self.head_view(&facts, source, input))
1187            }
1188            Outcome::DataInput(start, end) => Event::Data(input.get(start..end).unwrap_or(&[])),
1189            Outcome::DataDelimiter(n) => {
1190                Event::Data(self.boundary.delimiter().get(..n).unwrap_or(&[]))
1191            }
1192            Outcome::PartEnd(summary) => Event::PartEnd(summary),
1193            Outcome::End => Event::End,
1194            Outcome::NeedInput => Event::NeedInput,
1195        }
1196    }
1197
1198    fn count_file(&mut self, at: Location) -> Result<(), FormError> {
1199        self.files = self.files.saturating_add(1);
1200        if self.files > self.limits.max_files {
1201            return Err(FormError::limit(Limit::Files, at));
1202        }
1203        if let Some(t) = self.policy.token_field {
1204            if t.placement() == TokenPlacement::BeforeFirstFile && self.token != Token::Captured {
1205                return Err(FormError::policy(PolicyRefusal::TokenFieldLate, at));
1206            }
1207        }
1208        Ok(())
1209    }
1210
1211    /// Checks budgets for `n` more data bytes of the current part, and
1212    /// captures the token or `_charset_` value.
1213    fn take_data(&mut self, bytes: &[u8]) -> Result<(), FormError> {
1214        let n = bytes.len() as u64;
1215        if n == 0 {
1216            return Ok(());
1217        }
1218        if self.flags & DEFERRED != 0 {
1219            self.flags &= !DEFERRED;
1220            let at = Location::in_part(self.parts.checked_sub(1), self.data_start);
1221            self.count_file(at)?;
1222        }
1223        let total = self.part_bytes.saturating_add(n);
1224        match self.kind {
1225            PartKind::File => {
1226                let limit = self.limits.file();
1227                if total > limit {
1228                    let at = self.excess(self.part_bytes, limit);
1229                    return Err(FormError::limit(Limit::FileSize, at));
1230                }
1231            }
1232            PartKind::Field => {
1233                let limit = u64::from(self.limits.value());
1234                if total > limit {
1235                    let at = self.excess(self.part_bytes, limit);
1236                    return Err(FormError::limit(Limit::ValueLength, at));
1237                }
1238                let text = self.text_total.saturating_add(n);
1239                let limit = self.limits.text();
1240                if text > limit {
1241                    let at = self.excess(self.text_total, limit);
1242                    return Err(FormError::limit(Limit::TextTotal, at));
1243                }
1244                self.text_total = text;
1245            }
1246        }
1247        if self.flags & IS_TOKEN != 0 {
1248            let start = self
1249                .token_area()
1250                .saturating_add(usize::from(self.token_len));
1251            let end = start.saturating_add(bytes.len());
1252            let limit = u64::from(self.limits.token());
1253            let at = self.excess(u64::from(self.token_len), limit);
1254            if end
1255                > self
1256                    .token_area()
1257                    .saturating_add(usize::from(self.limits.token()))
1258            {
1259                return Err(FormError::limit(Limit::TokenLength, at));
1260            }
1261            match self.buffer.as_mut().get_mut(start..end) {
1262                Some(dst) => dst.copy_from_slice(bytes),
1263                None => return Err(FormError::limit(Limit::TokenLength, at)),
1264            }
1265            self.token_len = self.token_len.saturating_add(bytes.len() as u16);
1266        }
1267        if self.flags & IS_CHARSET != 0 {
1268            let start = self
1269                .charset_area()
1270                .saturating_add(usize::from(self.charset_len));
1271            let end = start.saturating_add(bytes.len());
1272            let at = self.excess(u64::from(self.charset_len), CHARSET_CAPTURE as u64);
1273            if end > self.charset_area().saturating_add(CHARSET_CAPTURE) {
1274                return Err(FormError::encoding(EncodingError::UnsupportedCharset, at));
1275            }
1276            match self.buffer.as_mut().get_mut(start..end) {
1277                Some(dst) => dst.copy_from_slice(bytes),
1278                None => return Err(FormError::encoding(EncodingError::UnsupportedCharset, at)),
1279            }
1280            self.charset_len = self.charset_len.saturating_add(bytes.len() as u8);
1281        }
1282        self.part_bytes = total;
1283        Ok(())
1284    }
1285
1286    fn end_part(&mut self, at: Location) -> Result<PartSummary, FormError> {
1287        if self.flags & IS_TOKEN != 0 {
1288            let start = self.token_area();
1289            let bytes = self
1290                .buffer
1291                .as_ref()
1292                .get(start..start.saturating_add(usize::from(self.token_len)))
1293                .unwrap_or(&[]);
1294            if core::str::from_utf8(bytes).is_err() {
1295                return Err(FormError::encoding(EncodingError::InvalidUtf8, at));
1296            }
1297            self.token = Token::Captured;
1298        }
1299        if self.flags & IS_CHARSET != 0 {
1300            self.charset_done = true;
1301        }
1302        let summary = PartSummary {
1303            index: self.parts.saturating_sub(1),
1304            kind: self.kind,
1305            bytes: self.part_bytes,
1306            skipped: self.flags & SKIPPING != 0,
1307        };
1308        self.flags = 0;
1309        self.k = 0;
1310        Ok(summary)
1311    }
1312
1313    /// One step in a part's data. `pos` is where `hay` starts in the input.
1314    fn body_step(
1315        &mut self,
1316        hay: &[u8],
1317        pos: usize,
1318        input_len: usize,
1319        eof: bool,
1320    ) -> Result<BodyStep, FormError> {
1321        let len = self.boundary.len();
1322        let k0 = usize::from(self.k);
1323        if k0 > 0 {
1324            // Bytes from earlier chunks that matched the start of the
1325            // delimiter: resolve them before anything else. The delimiter
1326            // has one CR, so the match either goes on or all of it is over.
1327            let need = self.boundary.delimiter().get(k0..).unwrap_or(&[]);
1328            let (need_len, n) = (need.len(), super::boundary::shared(need, hay));
1329            if n == need_len {
1330                // The delimiter started inside the held bytes.
1331                self.k = 0;
1332                self.bare = 0;
1333                self.state = State::EndOfPart;
1334                return self.emit_delimiter_bytes(0, pos.wrapping_add(n));
1335            }
1336            if n < hay.len() {
1337                // Every held byte is data; scan the input again from 0,
1338                // with the bare-break state the held bytes leave: they
1339                // are a CR, a CR LF, or a CR LF and dashes (D-113).
1340                self.k = 0;
1341                self.bare = k0.max(2).wrapping_sub(1) as u8;
1342                return self.emit_delimiter_bytes(k0, pos);
1343            }
1344            // All of `hay` extended the partial match.
1345            if eof {
1346                return Err(FormError::syntax(
1347                    SyntaxError::Truncated,
1348                    self.at(input_len),
1349                ));
1350            }
1351            self.k = k0.saturating_add(n) as u8;
1352            return self.emit_delimiter_bytes(0, input_len);
1353        }
1354        let (k, m, found) = self.boundary.run(0, usize::from(self.bare), hay);
1355        match found {
1356            Hit::BareBreak(after) => Err(self.bare_break(after, pos)),
1357            Hit::Delimiter(after) => {
1358                let data = after.saturating_sub(len);
1359                self.k = 0;
1360                self.bare = 0;
1361                self.state = State::EndOfPart;
1362                let bytes = hay.get(..data).unwrap_or(&[]);
1363                self.emit_input_bytes(bytes, pos, pos.wrapping_add(data), pos.wrapping_add(after))
1364            }
1365            Hit::None => {
1366                if eof {
1367                    return Err(FormError::syntax(
1368                        SyntaxError::Truncated,
1369                        self.at(input_len),
1370                    ));
1371                }
1372                self.k = k as u8;
1373                self.bare = m as u8;
1374                let data_end = hay.len().saturating_sub(k);
1375                let bytes = hay.get(..data_end).unwrap_or(&[]);
1376                self.emit_input_bytes(bytes, pos, pos.wrapping_add(data_end), input_len)
1377            }
1378        }
1379    }
1380
1381    /// Emits `delimiter[..n]` as data (or skips it), consuming up to
1382    /// `consumed`.
1383    fn emit_delimiter_bytes(&mut self, n: usize, consumed: usize) -> Result<BodyStep, FormError> {
1384        let boundary = self.boundary;
1385        let bytes = boundary.delimiter().get(..n).unwrap_or(&[]);
1386        self.take_data(bytes)?;
1387        if n == 0 || self.flags & SKIPPING != 0 {
1388            return Ok(BodyStep::Continue(consumed));
1389        }
1390        Ok(BodyStep::Emit(consumed, Outcome::DataDelimiter(n)))
1391    }
1392
1393    /// Emits `input[start..end]` as data (or skips it), consuming up to
1394    /// `consumed`.
1395    fn emit_input_bytes(
1396        &mut self,
1397        bytes: &[u8],
1398        start: usize,
1399        end: usize,
1400        consumed: usize,
1401    ) -> Result<BodyStep, FormError> {
1402        self.take_data(bytes)?;
1403        if start == end || self.flags & SKIPPING != 0 {
1404            return Ok(BodyStep::Continue(consumed));
1405        }
1406        Ok(BodyStep::Emit(consumed, Outcome::DataInput(start, end)))
1407    }
1408}
1409
1410/// What a body step did.
1411enum BodyStep {
1412    Emit(usize, Outcome),
1413    Continue(usize),
1414}
1415
1416impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
1417    /// Wipes every byte the parser wrote into its buffer, and nothing more:
1418    /// a buffer of the default size is 9.6 KiB, and a login form writes a
1419    /// few hundred bytes of it (D-103).
1420    fn wipe_used(&mut self) {
1421        let used = self.dirty as usize;
1422        let regions = [
1423            (0, used.min(self.name_area())),
1424            (
1425                self.name_area(),
1426                used.min(self.filename_area().saturating_sub(self.name_area())),
1427            ),
1428            (
1429                self.filename_area(),
1430                used.min(self.token_area().saturating_sub(self.filename_area())),
1431            ),
1432            (self.token_area(), usize::from(self.token_len)),
1433            // The whole capture area: a second, shorter `_charset_` value
1434            // resets the length over the tail of the first (found by review).
1435            (self.charset_area(), CHARSET_CAPTURE),
1436        ];
1437        let buf = self.buffer.as_mut();
1438        for (start, len) in regions {
1439            if let Some(region) = buf.get_mut(start..start.saturating_add(len)) {
1440                crate::wipe::wipe(region);
1441            }
1442        }
1443        self.dirty = 0;
1444    }
1445}
1446
1447impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for MultipartParser<B> {
1448    fn drop(&mut self) {
1449        self.wipe_used();
1450    }
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455    use super::*;
1456
1457    /// D-103: the wipe covers every byte the parser wrote. With a caller
1458    /// buffer the result is visible: after a body that writes every area
1459    /// (header blocks, an escaped name, a decoded `filename*`, the token,
1460    /// `_charset_`), dropping or resetting leaves the buffer all zeros.
1461    #[test]
1462    fn test_wipe_covers_every_area_written() {
1463        let policy = Policy::DEFAULT
1464            .with_extended_filename(crate::ExtendedFilenamePolicy::AcceptAlone)
1465            .with_token_field(crate::TokenField::new("csrf"));
1466        let limits = Limits::DEFAULT.with_max_part_header_bytes(512);
1467        let long = "x".repeat(300);
1468        let body = alloc::format!(
1469            "--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nsecret-token-value\r\n\
1470             --X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nUTF-8\r\n\
1471             --X\r\nContent-Disposition: form-data; name=\"pass%22word\"\r\nX-Pad: {long}\r\n\r\nhunter2\r\n\
1472             --X\r\nContent-Disposition: form-data; name=f; filename*=UTF-8''caf%C3%A9.txt\r\n\r\nfile\r\n--X--"
1473        );
1474        // Two `_charset_` values, the second shorter: the first one's tail
1475        // must not survive (found by review).
1476        let body = body.replace(
1477            "name=_charset_\r\n\r\nUTF-8\r\n",
1478            "name=_charset_\r\n\r\nunicode-1-1-utf-8\r\n--X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nutf8\r\n",
1479        );
1480        assert!(body.contains("unicode-1-1-utf-8"));
1481        for split in [usize::MAX, 7, 1] {
1482            let mut buf = alloc::vec![0u8; limits.multipart_buffer_len(&policy)];
1483            {
1484                let mut p = MultipartParser::with_buffer(
1485                    Boundary::new(b"X").unwrap(),
1486                    &limits,
1487                    &policy,
1488                    &mut buf[..],
1489                )
1490                .unwrap();
1491                let bytes = body.as_bytes();
1492                let chunks: alloc::vec::Vec<&[u8]> = bytes.chunks(split.min(bytes.len())).collect();
1493                let mut ended = false;
1494                for (i, c) in chunks.iter().enumerate() {
1495                    let last = i + 1 == chunks.len();
1496                    let mut rest = *c;
1497                    loop {
1498                        let step = p.parse(rest, last).unwrap();
1499                        rest = &rest[step.consumed..];
1500                        match step.event {
1501                            Event::NeedInput => break,
1502                            Event::End => {
1503                                ended = true;
1504                                break;
1505                            }
1506                            _ => {}
1507                        }
1508                    }
1509                }
1510                assert!(ended);
1511                assert_eq!(p.captured_token(), Some("secret-token-value"));
1512            }
1513            assert!(
1514                buf.iter().all(|&b| b == 0),
1515                "split {split}: bytes left at {:?}",
1516                buf.iter().position(|&b| b != 0)
1517            );
1518        }
1519    }
1520
1521    /// Feeds `body` whole and returns the first error, or `None` at `End`.
1522    #[cfg(feature = "alloc")]
1523    fn run(p: &mut MultipartParser<alloc::vec::Vec<u8>>, body: &[u8]) -> Option<FormError> {
1524        let mut rest = body;
1525        loop {
1526            match p.parse(rest, true) {
1527                Err(e) => return Some(e),
1528                Ok(step) => {
1529                    rest = &rest[step.consumed..];
1530                    if matches!(step.event, Event::End) {
1531                        return None;
1532                    }
1533                }
1534            }
1535        }
1536    }
1537
1538    /// T-28, through the counter seam: counters set near `u64::MAX` never
1539    /// wrap. Under finite limits they still refuse; under `u64::MAX` limits
1540    /// they saturate and positions stay at the maximum.
1541    #[cfg(feature = "alloc")]
1542    #[test]
1543    fn test_attack_t28_counters_near_u64_max_never_wrap() {
1544        let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\nvalue\r\n--X--";
1545        let new = |limits: &Limits| {
1546            MultipartParser::new(Boundary::new(b"X").unwrap(), limits, &Policy::DEFAULT).unwrap()
1547        };
1548        let mut p = new(&Limits::DEFAULT);
1549        p.bytes_seen = u64::MAX - 4;
1550        assert!(matches!(
1551            run(&mut p, body),
1552            Some(FormError::Limit {
1553                limit: crate::error::Limit::Body,
1554                ..
1555            })
1556        ));
1557
1558        let mut p = new(&Limits::DEFAULT);
1559        p.text_total = u64::MAX - 2;
1560        assert!(matches!(
1561            run(&mut p, body),
1562            Some(FormError::Limit {
1563                limit: crate::error::Limit::TextTotal,
1564                ..
1565            })
1566        ));
1567
1568        let open = Limits::DEFAULT
1569            .with_max_body_bytes(u64::MAX)
1570            .with_max_text_bytes(u64::MAX);
1571        let mut p = new(&open);
1572        p.bytes_seen = u64::MAX - 4;
1573        assert!(run(&mut p, body).is_none());
1574        assert_eq!(p.bytes_seen, u64::MAX);
1575    }
1576
1577    #[test]
1578    fn test_parser_size_is_bounded() {
1579        // NFR-18 as measured: the boundary and its matcher table are 149 of
1580        // these bytes; the buffer is separate.
1581        assert!(core::mem::size_of::<MultipartParser<&mut [u8]>>() <= 384);
1582        assert!(core::mem::size_of::<Boundary>() <= 150);
1583    }
1584
1585    #[cfg(feature = "alloc")]
1586    #[test]
1587    fn test_debug_never_shows_data_or_file_names() {
1588        let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=\"secret.pdf\"\r\n\r\nhunter2\r\n--X--";
1589        let mut p = MultipartParser::new(
1590            Boundary::new(b"X").unwrap(),
1591            &Limits::DEFAULT,
1592            &Policy::DEFAULT,
1593        )
1594        .unwrap();
1595        let mut rest: &[u8] = body;
1596        let mut seen = alloc::string::String::new();
1597        loop {
1598            let step = p.parse(rest, true).unwrap();
1599            rest = &rest[step.consumed..];
1600            if let Event::PartStart(head) = &step.event {
1601                seen.push_str(&alloc::format!("{:?}", head.headers()));
1602            }
1603            seen.push_str(&alloc::format!("{step:?}"));
1604            if matches!(step.event, Event::End) {
1605                break;
1606            }
1607        }
1608        assert!(seen.contains("Data(7 bytes)"), "{seen}");
1609        assert!(
1610            !seen.contains("hunter2") && !seen.contains("secret"),
1611            "{seen}"
1612        );
1613    }
1614
1615    #[test]
1616    fn test_charset_labels() {
1617        assert_eq!(Charset::from_label(b"UTF-8"), Charset::Utf8);
1618        assert_eq!(Charset::from_label(b"us-ascii"), Charset::Ascii);
1619        assert_eq!(Charset::from_label(b"utf8"), Charset::Other);
1620    }
1621}