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