1use 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;
9use crate::multipart::filename::FileName;
10use crate::multipart::header::{Areas, BlockScan, HeadFacts, Text, read_block};
11
12#[non_exhaustive]
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum PartKind {
25 Field,
27 File,
30}
31
32#[non_exhaustive]
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50pub enum Charset {
51 Utf8,
53 Ascii,
55 Unspecified,
57 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#[non_exhaustive]
76#[derive(Debug)]
77pub struct Step<'p> {
78 pub consumed: usize,
81 pub event: Event<'p>,
83}
84
85#[non_exhaustive]
93pub enum Event<'p> {
94 PartStart(PartHead<'p>),
96 Data(&'p [u8]),
98 PartEnd(PartSummary),
100 End,
102 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#[non_exhaustive]
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct PartSummary {
122 pub index: u32,
124 pub kind: PartKind,
126 pub bytes: u64,
128 pub skipped: bool,
130}
131
132#[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 #[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 #[cfg(feature = "alloc")]
186 pub(crate) const fn block(&self) -> &'p [u8] {
187 self.block
188 }
189
190 #[must_use]
192 pub const fn index(&self) -> u32 {
193 self.index
194 }
195
196 #[must_use]
199 pub const fn name(&self) -> &'p str {
200 self.name
201 }
202
203 #[must_use]
205 pub const fn name_raw(&self) -> &'p [u8] {
206 self.name_raw
207 }
208
209 #[must_use]
211 pub const fn kind(&self) -> PartKind {
212 self.kind
213 }
214
215 #[must_use]
217 pub const fn is_file(&self) -> bool {
218 matches!(self.kind, PartKind::File)
219 }
220
221 #[must_use]
223 pub const fn filename(&self) -> Option<FileName<'p>> {
224 self.filename
225 }
226
227 #[must_use]
229 pub fn content_type(&self) -> Option<ContentType<'p>> {
230 self.content_type.and_then(ContentType::parse)
231 }
232
233 #[must_use]
235 pub const fn charset(&self) -> Charset {
236 self.charset
237 }
238
239 #[must_use]
241 pub fn headers(&self) -> Headers<'p> {
242 Headers { rest: self.block }
243 }
244
245 #[must_use]
247 pub const fn header_bytes(&self) -> u32 {
248 self.block.len() as u32
249 }
250}
251
252#[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 #[must_use]
290 pub const fn raw(&self) -> &'p [u8] {
291 self.raw
292 }
293
294 #[must_use]
296 pub fn type_(&self) -> &'p str {
297 core::str::from_utf8(self.media.type_()).unwrap_or("")
298 }
299
300 #[must_use]
302 pub fn subtype(&self) -> &'p str {
303 core::str::from_utf8(self.media.subtype()).unwrap_or("")
304 }
305
306 #[must_use]
308 pub fn is(&self, type_: &str, subtype: &str) -> bool {
309 self.media.is(type_, subtype)
310 }
311
312 #[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 #[must_use]
324 pub fn charset(&self) -> Charset {
325 self.parameter("charset")
326 .map_or(Charset::Unspecified, Charset::from_label)
327 }
328}
329
330#[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 AfterDelimiter(After),
372 Headers,
373 Body,
374 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#[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#[derive(Clone, Copy, Debug)]
403pub(crate) enum Source {
404 Input(usize),
406 Buffer,
408}
409
410#[derive(Clone, Copy, Debug)]
412pub(crate) enum Outcome {
413 PartStart(HeadFacts, Source),
414 DataInput(usize, usize),
416 DataDelimiter(usize),
418 PartEnd(PartSummary),
419 End,
420 NeedInput,
421}
422
423pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> {
471 boundary: Boundary,
472 limits: Limits,
473 policy: Policy,
474 buffer: B,
475 state: State,
476 k: u8,
478 flags: u8,
479 token: Token,
480 token_len: u16,
481 charset_len: u8,
482 charset_done: bool,
483 scan: BlockScan,
484 copied: u32,
486 parts: u32,
487 files: u32,
488 fields: u32,
489 kind: PartKind,
490 part_bytes: u64,
491 text_total: u64,
492 data_start: u64,
493 preamble: u64,
494 epilogue: u64,
495 head_start: u64,
497 epilogue_start: u64,
499 bytes_seen: u64,
500}
501
502impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for MultipartParser<B> {
503 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504 f.debug_struct("MultipartParser")
505 .field("boundary", &self.boundary)
506 .field("state", &self.state)
507 .field("bytes_seen", &self.bytes_seen)
508 .field("parts_seen", &self.parts)
509 .finish_non_exhaustive()
510 }
511}
512
513#[cfg(feature = "alloc")]
514impl MultipartParser<alloc::vec::Vec<u8>> {
515 pub fn new(boundary: Boundary, limits: &Limits, policy: &Policy) -> Result<Self, FormError> {
532 limits.validate_with(policy)?;
533 let buffer = alloc::vec![0u8; limits.multipart_buffer_len(policy)];
534 Self::with_buffer(boundary, limits, policy, buffer)
535 }
536}
537
538impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B> {
539 pub fn with_buffer(
558 boundary: Boundary,
559 limits: &Limits,
560 policy: &Policy,
561 buffer: B,
562 ) -> Result<Self, FormError> {
563 limits.validate_with(policy)?;
564 let needed = limits.multipart_buffer_len(policy);
565 if buffer.as_ref().len() < needed {
566 return Err(FormError::BufferTooSmall { needed });
567 }
568 Ok(Self {
569 boundary,
570 limits: *limits,
571 policy: *policy,
572 buffer,
573 state: State::Preamble,
574 k: 2,
577 flags: 0,
578 token: Token::NotSeen,
579 token_len: 0,
580 charset_len: 0,
581 charset_done: false,
582 scan: BlockScan::default(),
583 copied: 0,
584 parts: 0,
585 files: 0,
586 fields: 0,
587 kind: PartKind::Field,
588 part_bytes: 0,
589 text_total: 0,
590 data_start: 0,
591 preamble: 0,
592 epilogue: 0,
593 head_start: 0,
594 epilogue_start: 0,
595 bytes_seen: 0,
596 })
597 }
598
599 pub fn parse<'p>(&'p mut self, input: &'p [u8], eof: bool) -> Result<Step<'p>, FormError> {
607 let (consumed, outcome) = self.advance(input, eof)?;
608 Ok(Step {
609 consumed,
610 event: self.view(outcome, input),
611 })
612 }
613
614 pub fn skip_part(&mut self) {
618 if matches!(self.state, State::Body) {
619 self.flags |= SKIPPING;
620 }
621 }
622
623 pub fn reset(&mut self, boundary: Boundary) {
626 crate::wipe::wipe(self.buffer.as_mut());
627 let limits = self.limits;
628 let policy = self.policy;
629 self.boundary = boundary;
630 self.state = State::Preamble;
631 self.k = 2;
632 self.flags = 0;
633 self.token = Token::NotSeen;
634 self.token_len = 0;
635 self.charset_len = 0;
636 self.charset_done = false;
637 self.scan = BlockScan::default();
638 self.copied = 0;
639 self.parts = 0;
640 self.files = 0;
641 self.fields = 0;
642 self.kind = PartKind::Field;
643 self.part_bytes = 0;
644 self.text_total = 0;
645 self.data_start = 0;
646 self.preamble = 0;
647 self.epilogue = 0;
648 self.head_start = 0;
649 self.epilogue_start = 0;
650 self.bytes_seen = 0;
651 self.limits = limits;
652 self.policy = policy;
653 }
654
655 #[must_use]
657 pub const fn bytes_seen(&self) -> u64 {
658 self.bytes_seen
659 }
660
661 #[must_use]
663 pub const fn parts_seen(&self) -> u32 {
664 self.parts
665 }
666
667 #[must_use]
669 pub const fn files_seen(&self) -> u32 {
670 self.files
671 }
672
673 #[must_use]
675 pub fn is_done(&self) -> bool {
676 self.state == State::Epilogue
677 }
678
679 #[must_use]
681 pub const fn limits(&self) -> &Limits {
682 &self.limits
683 }
684
685 #[must_use]
687 pub const fn policy(&self) -> &Policy {
688 &self.policy
689 }
690
691 #[must_use]
693 pub fn captured_token(&self) -> Option<&str> {
694 if self.token != Token::Captured {
695 return None;
696 }
697 let start = self.token_area();
698 let bytes = self
699 .buffer
700 .as_ref()
701 .get(start..start.saturating_add(usize::from(self.token_len)))?;
702 core::str::from_utf8(bytes).ok()
703 }
704
705 #[must_use]
707 pub fn declared_charset(&self) -> Option<&[u8]> {
708 if !self.charset_done {
709 return None;
710 }
711 let start = self.charset_area();
712 self.buffer
713 .as_ref()
714 .get(start..start.saturating_add(usize::from(self.charset_len)))
715 }
716
717 #[cfg(feature = "alloc")]
719 pub(crate) fn area_name(&self, len: u32) -> &str {
720 let start = self.name_area();
721 let bytes = self
722 .buffer
723 .as_ref()
724 .get(start..start.saturating_add(len as usize))
725 .unwrap_or(&[]);
726 core::str::from_utf8(bytes).unwrap_or("")
727 }
728
729 #[cfg(feature = "alloc")]
731 pub(crate) fn area_filename(&self, len: u32) -> &[u8] {
732 let start = self.filename_area();
733 self.buffer
734 .as_ref()
735 .get(start..start.saturating_add(len as usize))
736 .unwrap_or(&[])
737 }
738
739 #[cfg(feature = "alloc")]
741 pub(crate) const fn data_start(&self) -> u64 {
742 self.data_start
743 }
744
745 fn name_area(&self) -> usize {
748 self.limits.max_part_header_bytes as usize
749 }
750
751 fn filename_area(&self) -> usize {
752 self.name_area().saturating_add(self.limits.name() as usize)
753 }
754
755 fn token_area(&self) -> usize {
756 let filename = if matches!(
757 self.policy.extended_filename,
758 crate::ExtendedFilenamePolicy::AcceptAlone
759 ) {
760 self.limits.filename() as usize
761 } else {
762 0
763 };
764 self.filename_area().saturating_add(filename)
765 }
766
767 fn charset_area(&self) -> usize {
768 let token = if self.policy.token_field.is_some() {
769 usize::from(self.limits.token())
770 } else {
771 0
772 };
773 self.token_area().saturating_add(token)
774 }
775
776 fn head_at(&self) -> Location {
778 Location::in_part(self.parts.checked_sub(1), self.head_start)
779 }
780
781 fn excess(&self, used: u64, limit: u64) -> Location {
784 let into_part = self.part_bytes.saturating_add(limit.saturating_sub(used));
785 Location::in_part(
786 self.parts.checked_sub(1),
787 self.data_start.saturating_add(into_part),
788 )
789 }
790
791 fn at(&self, pos: usize) -> Location {
792 let part = if matches!(self.state, State::Preamble | State::Epilogue) {
793 None
794 } else {
795 self.parts.checked_sub(1)
796 };
797 Location::in_part(part, self.bytes_seen.saturating_add(pos as u64))
798 }
799
800 pub(crate) fn advance(
804 &mut self,
805 input: &[u8],
806 eof: bool,
807 ) -> Result<(usize, Outcome), FormError> {
808 if self.state == State::Poisoned {
809 return Err(FormError::Poisoned);
810 }
811 match self.run(input, eof) {
812 Ok((consumed, outcome)) => {
813 self.bytes_seen = self.bytes_seen.saturating_add(consumed as u64);
814 Ok((consumed, outcome))
815 }
816 Err(e) => {
817 self.state = State::Poisoned;
818 Err(e)
819 }
820 }
821 }
822
823 fn run(&mut self, input: &[u8], eof: bool) -> Result<(usize, Outcome), FormError> {
824 if self.bytes_seen.saturating_add(input.len() as u64) > self.limits.body() {
825 return Err(FormError::limit(
826 Limit::Body,
827 Location::at(self.limits.body()),
828 ));
829 }
830 let mut pos = 0usize;
831 loop {
832 let hay = input.get(pos..).unwrap_or(&[]);
833 match self.state {
834 State::Poisoned => return Err(FormError::Poisoned),
835 State::Preamble => {
836 if let Some(after) = self.preamble_step(hay)? {
837 pos = pos.wrapping_add(after);
838 self.state = State::AfterDelimiter(After::Start);
839 continue;
840 }
841 if eof {
842 return Err(FormError::syntax(
843 SyntaxError::NoInitialDelimiter,
844 self.at(input.len()),
845 ));
846 }
847 return Ok((input.len(), Outcome::NeedInput));
848 }
849 State::AfterDelimiter(sub) => {
850 let Some(&c) = hay.first() else {
851 return self.need_input(input.len(), eof);
852 };
853 pos = pos.wrapping_add(1);
854 let at = self.at(pos);
855 self.state = match (sub, c) {
856 (After::Start, b'-') => State::AfterDelimiter(After::Dash),
857 (After::Dash, b'-') => {
858 self.state = State::Epilogue;
859 self.epilogue_start = self.bytes_seen.saturating_add(pos as u64);
860 return Ok((pos, Outcome::End));
861 }
862 (After::Start, b' ' | b'\t') => {
863 if self.limits.max_padding_bytes == 0 {
864 return Err(FormError::limit(Limit::Padding, at));
865 }
866 State::AfterDelimiter(After::Padding(1))
867 }
868 (After::Padding(n), b' ' | b'\t') => {
869 if n >= self.limits.max_padding_bytes {
870 return Err(FormError::limit(Limit::Padding, at));
871 }
872 State::AfterDelimiter(After::Padding(n.saturating_add(1)))
873 }
874 (After::Start | After::Padding(_), b'\r') => {
875 State::AfterDelimiter(After::Cr)
876 }
877 (After::Cr, b'\n') => {
878 self.parts = self.parts.saturating_add(1);
879 if self.parts > self.limits.max_parts {
880 return Err(FormError::limit(Limit::Parts, self.at(pos)));
881 }
882 self.scan = BlockScan::default();
883 self.copied = 0;
884 self.head_start = self.bytes_seen.saturating_add(pos as u64);
885 State::Headers
886 }
887 (After::Start | After::Padding(_), b'\n') => {
888 return Err(FormError::syntax(SyntaxError::BareLineFeed, at));
889 }
890 _ => return Err(FormError::syntax(SyntaxError::BoundaryInContent, at)),
891 };
892 }
893 State::Headers => {
894 let at = self.head_at();
895 match self.scan.feed(hay, &self.limits, at)? {
896 Some(end) => {
897 let source = if self.copied == 0 {
898 Source::Input(pos)
899 } else {
900 self.copy_header(hay.get(..end).unwrap_or(&[]), at)?;
901 Source::Buffer
902 };
903 let block_start_in_input = pos;
904 pos = pos.wrapping_add(end);
905 let facts =
906 self.read_head(input, source, block_start_in_input, end, at)?;
907 let (is_token, is_charset) = {
908 let block = self.block(&facts, source, input);
909 let name = self.name_bytes(&facts, block);
910 let token = self
911 .policy
912 .token_field
913 .is_some_and(|t| name == t.name().as_bytes());
914 (token, name == b"_charset_")
915 };
916 self.start_part(&facts, is_token, is_charset, at)?;
917 self.data_start = self.bytes_seen.saturating_add(pos as u64);
918 return Ok((pos, Outcome::PartStart(facts, source)));
919 }
920 None => {
921 self.copy_header(hay, at)?;
922 return self.need_input(input.len(), eof);
923 }
924 }
925 }
926 State::Body => {
927 if hay.is_empty() {
928 return self.need_input(input.len(), eof);
929 }
930 match self.body_step(hay, pos, input.len(), eof)? {
931 BodyStep::Emit(consumed, outcome) => return Ok((consumed, outcome)),
932 BodyStep::Continue(next) => pos = next,
933 }
934 }
935 State::EndOfPart => {
936 let summary = self.end_part(self.at(pos))?;
937 self.state = State::AfterDelimiter(After::Start);
938 return Ok((pos, Outcome::PartEnd(summary)));
939 }
940 State::Epilogue => {
941 self.epilogue = self.epilogue.saturating_add(hay.len() as u64);
942 if self.epilogue > self.limits.epilogue() {
943 let at = self.epilogue_start.saturating_add(self.limits.epilogue());
944 return Err(FormError::limit(Limit::Epilogue, Location::at(at)));
945 }
946 return Ok((input.len(), Outcome::End));
947 }
948 }
949 }
950 }
951
952 fn need_input(&self, len: usize, eof: bool) -> Result<(usize, Outcome), FormError> {
953 if eof {
954 Err(FormError::syntax(SyntaxError::Truncated, self.at(len)))
955 } else {
956 Ok((len, Outcome::NeedInput))
957 }
958 }
959
960 fn preamble_step(&mut self, hay: &[u8]) -> Result<Option<usize>, FormError> {
963 let len = self.boundary.len() as u64;
964 let budget = self.limits.preamble().saturating_add(len);
966 let room = usize::try_from(budget.saturating_sub(self.preamble)).unwrap_or(usize::MAX);
967 let window = hay.get(..room.min(hay.len())).unwrap_or(hay);
968 let (k, found) = self.boundary.run(usize::from(self.k), window);
969 self.k = k as u8;
970 match found {
971 Some(after) => {
972 let end = self.preamble.saturating_add(after as u64);
975 let preamble = end.saturating_sub(len);
976 if preamble > self.limits.preamble() {
977 return Err(FormError::limit(
978 Limit::Preamble,
979 Location::at(self.limits.preamble()),
980 ));
981 }
982 self.k = 0;
983 Ok(Some(after))
984 }
985 None => {
986 self.preamble = self.preamble.saturating_add(window.len() as u64);
987 if window.len() < hay.len() || self.preamble >= budget {
988 return Err(FormError::limit(
989 Limit::Preamble,
990 Location::at(self.limits.preamble()),
991 ));
992 }
993 Ok(None)
994 }
995 }
996 }
997
998 fn copy_header(&mut self, bytes: &[u8], at: Location) -> Result<(), FormError> {
999 let start = self.copied as usize;
1000 let end = start.saturating_add(bytes.len());
1001 if end > self.limits.max_part_header_bytes as usize {
1002 return Err(FormError::limit(Limit::PartHeaderBytes, at));
1003 }
1004 match self.buffer.as_mut().get_mut(start..end) {
1005 Some(dst) => dst.copy_from_slice(bytes),
1006 None => return Err(FormError::limit(Limit::PartHeaderBytes, at)),
1007 }
1008 self.copied = end as u32;
1009 Ok(())
1010 }
1011
1012 fn read_head(
1013 &mut self,
1014 input: &[u8],
1015 source: Source,
1016 start: usize,
1017 end: usize,
1018 at: Location,
1019 ) -> Result<HeadFacts, FormError> {
1020 let name_area = self.name_area();
1021 let filename_area = self.filename_area();
1022 let filename_end = self.token_area();
1023 let limits = self.limits;
1024 let policy = self.policy;
1025 let copied = self.copied as usize;
1026 let buf = self.buffer.as_mut();
1027 let (head, rest) = buf.split_at_mut(name_area.min(buf.len()));
1028 let (name, rest) =
1029 rest.split_at_mut(filename_area.saturating_sub(name_area).min(rest.len()));
1030 let filename = rest
1031 .get_mut(..filename_end.saturating_sub(filename_area))
1032 .unwrap_or(&mut []);
1033 let block = match source {
1034 Source::Input(_) => input.get(start..start.saturating_add(end)).unwrap_or(&[]),
1035 Source::Buffer => head.get(..copied).unwrap_or(&[]),
1036 };
1037 read_block(block, &limits, &policy, Areas { name, filename }, at)
1038 }
1039
1040 fn start_part(
1042 &mut self,
1043 facts: &HeadFacts,
1044 is_token: bool,
1045 is_charset: bool,
1046 at: Location,
1047 ) -> Result<(), FormError> {
1048 self.state = State::Body;
1049 self.k = 0;
1050 self.flags = 0;
1051 self.kind = facts.kind;
1052 self.part_bytes = 0;
1053 match facts.kind {
1054 PartKind::Field => {
1055 self.fields = self.fields.saturating_add(1);
1056 if self.fields > self.limits.max_fields {
1057 return Err(FormError::limit(Limit::Fields, at));
1058 }
1059 if is_token {
1060 if self.token != Token::NotSeen {
1061 return Err(FormError::policy(PolicyRefusal::DuplicateToken, at));
1062 }
1063 self.token = Token::Capturing;
1064 self.token_len = 0;
1065 self.flags |= IS_TOKEN;
1066 }
1067 if is_charset {
1068 self.flags |= IS_CHARSET;
1069 self.charset_len = 0;
1070 self.charset_done = false;
1071 }
1072 }
1073 PartKind::File => {
1074 let empty = matches!(facts.filename, Some((Text::Block(span), _)) if span.len == 0)
1075 || matches!(facts.filename, Some((Text::Area(0), _)));
1076 if empty && self.policy.empty_files == EmptyFilePolicy::Skip {
1077 self.flags |= DEFERRED;
1078 } else {
1079 self.count_file(at)?;
1080 }
1081 }
1082 }
1083 Ok(())
1084 }
1085
1086 fn block<'a>(&'a self, facts: &HeadFacts, source: Source, input: &'a [u8]) -> &'a [u8] {
1088 let len = facts.block_len as usize;
1089 match source {
1090 Source::Input(start) => input.get(start..start.saturating_add(len)).unwrap_or(&[]),
1091 Source::Buffer => self.buffer.as_ref().get(..len).unwrap_or(&[]),
1092 }
1093 }
1094
1095 fn name_bytes<'a>(&'a self, facts: &HeadFacts, block: &'a [u8]) -> &'a [u8] {
1097 match facts.name {
1098 Text::Block(span) => span.get(block),
1099 Text::Area(len) => {
1100 let start = self.name_area();
1101 self.buffer
1102 .as_ref()
1103 .get(start..start.saturating_add(len as usize))
1104 .unwrap_or(&[])
1105 }
1106 }
1107 }
1108
1109 fn head_view<'p>(&'p self, facts: &HeadFacts, source: Source, input: &'p [u8]) -> PartHead<'p> {
1111 let block = self.block(facts, source, input);
1112 let name = core::str::from_utf8(self.name_bytes(facts, block)).unwrap_or("");
1113 let filename = facts.filename.map(|(text, origin)| {
1114 let raw = match text {
1115 Text::Block(span) => span.get(block),
1116 Text::Area(len) => {
1117 let start = self.filename_area();
1118 self.buffer
1119 .as_ref()
1120 .get(start..start.saturating_add(len as usize))
1121 .unwrap_or(&[])
1122 }
1123 };
1124 FileName::new(raw, origin)
1125 });
1126 PartHead {
1127 index: self.parts.saturating_sub(1),
1128 block,
1129 name,
1130 name_raw: facts.name_raw.get(block),
1131 kind: facts.kind,
1132 filename,
1133 content_type: facts.content_type.map(|span| span.get(block)),
1134 charset: facts.charset,
1135 }
1136 }
1137
1138 fn view<'p>(&'p self, outcome: Outcome, input: &'p [u8]) -> Event<'p> {
1140 match outcome {
1141 Outcome::PartStart(facts, source) => {
1142 Event::PartStart(self.head_view(&facts, source, input))
1143 }
1144 Outcome::DataInput(start, end) => Event::Data(input.get(start..end).unwrap_or(&[])),
1145 Outcome::DataDelimiter(n) => {
1146 Event::Data(self.boundary.delimiter().get(..n).unwrap_or(&[]))
1147 }
1148 Outcome::PartEnd(summary) => Event::PartEnd(summary),
1149 Outcome::End => Event::End,
1150 Outcome::NeedInput => Event::NeedInput,
1151 }
1152 }
1153
1154 fn count_file(&mut self, at: Location) -> Result<(), FormError> {
1155 self.files = self.files.saturating_add(1);
1156 if self.files > self.limits.max_files {
1157 return Err(FormError::limit(Limit::Files, at));
1158 }
1159 if let Some(t) = self.policy.token_field {
1160 if t.placement() == TokenPlacement::BeforeFirstFile && self.token != Token::Captured {
1161 return Err(FormError::policy(PolicyRefusal::TokenFieldLate, at));
1162 }
1163 }
1164 Ok(())
1165 }
1166
1167 fn take_data(&mut self, bytes: &[u8]) -> Result<(), FormError> {
1170 let n = bytes.len() as u64;
1171 if n == 0 {
1172 return Ok(());
1173 }
1174 if self.flags & DEFERRED != 0 {
1175 self.flags &= !DEFERRED;
1176 let at = Location::in_part(self.parts.checked_sub(1), self.data_start);
1177 self.count_file(at)?;
1178 }
1179 let total = self.part_bytes.saturating_add(n);
1180 match self.kind {
1181 PartKind::File => {
1182 let limit = self.limits.file();
1183 if total > limit {
1184 let at = self.excess(self.part_bytes, limit);
1185 return Err(FormError::limit(Limit::FileSize, at));
1186 }
1187 }
1188 PartKind::Field => {
1189 let limit = u64::from(self.limits.value());
1190 if total > limit {
1191 let at = self.excess(self.part_bytes, limit);
1192 return Err(FormError::limit(Limit::ValueLength, at));
1193 }
1194 let text = self.text_total.saturating_add(n);
1195 let limit = self.limits.text();
1196 if text > limit {
1197 let at = self.excess(self.text_total, limit);
1198 return Err(FormError::limit(Limit::TextTotal, at));
1199 }
1200 self.text_total = text;
1201 }
1202 }
1203 if self.flags & IS_TOKEN != 0 {
1204 let start = self
1205 .token_area()
1206 .saturating_add(usize::from(self.token_len));
1207 let end = start.saturating_add(bytes.len());
1208 let limit = u64::from(self.limits.token());
1209 let at = self.excess(u64::from(self.token_len), limit);
1210 if end
1211 > self
1212 .token_area()
1213 .saturating_add(usize::from(self.limits.token()))
1214 {
1215 return Err(FormError::limit(Limit::TokenLength, at));
1216 }
1217 match self.buffer.as_mut().get_mut(start..end) {
1218 Some(dst) => dst.copy_from_slice(bytes),
1219 None => return Err(FormError::limit(Limit::TokenLength, at)),
1220 }
1221 self.token_len = self.token_len.saturating_add(bytes.len() as u16);
1222 }
1223 if self.flags & IS_CHARSET != 0 {
1224 let start = self
1225 .charset_area()
1226 .saturating_add(usize::from(self.charset_len));
1227 let end = start.saturating_add(bytes.len());
1228 let at = self.excess(u64::from(self.charset_len), CHARSET_CAPTURE as u64);
1229 if end > self.charset_area().saturating_add(CHARSET_CAPTURE) {
1230 return Err(FormError::encoding(EncodingError::UnsupportedCharset, at));
1231 }
1232 match self.buffer.as_mut().get_mut(start..end) {
1233 Some(dst) => dst.copy_from_slice(bytes),
1234 None => return Err(FormError::encoding(EncodingError::UnsupportedCharset, at)),
1235 }
1236 self.charset_len = self.charset_len.saturating_add(bytes.len() as u8);
1237 }
1238 self.part_bytes = total;
1239 Ok(())
1240 }
1241
1242 fn end_part(&mut self, at: Location) -> Result<PartSummary, FormError> {
1243 if self.flags & IS_TOKEN != 0 {
1244 let start = self.token_area();
1245 let bytes = self
1246 .buffer
1247 .as_ref()
1248 .get(start..start.saturating_add(usize::from(self.token_len)))
1249 .unwrap_or(&[]);
1250 if core::str::from_utf8(bytes).is_err() {
1251 return Err(FormError::encoding(EncodingError::InvalidUtf8, at));
1252 }
1253 self.token = Token::Captured;
1254 }
1255 if self.flags & IS_CHARSET != 0 {
1256 self.charset_done = true;
1257 }
1258 let summary = PartSummary {
1259 index: self.parts.saturating_sub(1),
1260 kind: self.kind,
1261 bytes: self.part_bytes,
1262 skipped: self.flags & SKIPPING != 0,
1263 };
1264 self.flags = 0;
1265 self.k = 0;
1266 Ok(summary)
1267 }
1268
1269 fn body_step(
1271 &mut self,
1272 hay: &[u8],
1273 pos: usize,
1274 input_len: usize,
1275 eof: bool,
1276 ) -> Result<BodyStep, FormError> {
1277 let len = self.boundary.len();
1278 let k0 = usize::from(self.k);
1279 if k0 > 0 {
1280 let mut k = k0;
1283 for (i, &c) in hay.iter().enumerate() {
1284 k = self.boundary.step(k, c);
1285 let n = i.wrapping_add(1);
1286 if k == len {
1287 let data = k0.saturating_add(n).saturating_sub(len);
1289 self.k = 0;
1290 self.state = State::EndOfPart;
1291 return self.emit_delimiter_bytes(data, pos.wrapping_add(n));
1292 }
1293 if k <= n {
1294 self.k = 0;
1296 return self.emit_delimiter_bytes(k0, pos);
1297 }
1298 }
1299 let n = hay.len();
1301 if eof {
1302 return Err(FormError::syntax(
1303 SyntaxError::Truncated,
1304 self.at(input_len),
1305 ));
1306 }
1307 let data = k0.saturating_add(n).saturating_sub(k);
1308 self.k = k as u8;
1309 return self.emit_delimiter_bytes(data, input_len);
1310 }
1311 let (k, found) = self.boundary.run(0, hay);
1312 match found {
1313 Some(after) => {
1314 let data = after.saturating_sub(len);
1315 self.k = 0;
1316 self.state = State::EndOfPart;
1317 let bytes = hay.get(..data).unwrap_or(&[]);
1318 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data), pos.wrapping_add(after))
1319 }
1320 None => {
1321 if eof {
1322 return Err(FormError::syntax(
1323 SyntaxError::Truncated,
1324 self.at(input_len),
1325 ));
1326 }
1327 self.k = k as u8;
1328 let data_end = hay.len().saturating_sub(k);
1329 let bytes = hay.get(..data_end).unwrap_or(&[]);
1330 self.emit_input_bytes(bytes, pos, pos.wrapping_add(data_end), input_len)
1331 }
1332 }
1333 }
1334
1335 fn emit_delimiter_bytes(&mut self, n: usize, consumed: usize) -> Result<BodyStep, FormError> {
1338 let boundary = self.boundary;
1339 let bytes = boundary.delimiter().get(..n).unwrap_or(&[]);
1340 self.take_data(bytes)?;
1341 if n == 0 || self.flags & SKIPPING != 0 {
1342 return Ok(BodyStep::Continue(consumed));
1343 }
1344 Ok(BodyStep::Emit(consumed, Outcome::DataDelimiter(n)))
1345 }
1346
1347 fn emit_input_bytes(
1350 &mut self,
1351 bytes: &[u8],
1352 start: usize,
1353 end: usize,
1354 consumed: usize,
1355 ) -> Result<BodyStep, FormError> {
1356 self.take_data(bytes)?;
1357 if start == end || self.flags & SKIPPING != 0 {
1358 return Ok(BodyStep::Continue(consumed));
1359 }
1360 Ok(BodyStep::Emit(consumed, Outcome::DataInput(start, end)))
1361 }
1362}
1363
1364enum BodyStep {
1366 Emit(usize, Outcome),
1367 Continue(usize),
1368}
1369
1370impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for MultipartParser<B> {
1371 fn drop(&mut self) {
1372 crate::wipe::wipe(self.buffer.as_mut());
1373 }
1374}
1375
1376#[cfg(test)]
1377mod tests {
1378 use super::*;
1379
1380 #[test]
1381 fn test_parser_size_is_bounded() {
1382 assert!(core::mem::size_of::<MultipartParser<&mut [u8]>>() <= 384);
1385 assert!(core::mem::size_of::<Boundary>() <= 150);
1386 }
1387
1388 #[cfg(feature = "alloc")]
1389 #[test]
1390 fn test_debug_never_shows_data_or_file_names() {
1391 let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=\"secret.pdf\"\r\n\r\nhunter2\r\n--X--";
1392 let mut p = MultipartParser::new(
1393 Boundary::new(b"X").unwrap(),
1394 &Limits::DEFAULT,
1395 &Policy::DEFAULT,
1396 )
1397 .unwrap();
1398 let mut rest: &[u8] = body;
1399 let mut seen = alloc::string::String::new();
1400 loop {
1401 let step = p.parse(rest, true).unwrap();
1402 rest = &rest[step.consumed..];
1403 if let Event::PartStart(head) = &step.event {
1404 seen.push_str(&alloc::format!("{:?}", head.headers()));
1405 }
1406 seen.push_str(&alloc::format!("{step:?}"));
1407 if matches!(step.event, Event::End) {
1408 break;
1409 }
1410 }
1411 assert!(seen.contains("Data(7 bytes)"), "{seen}");
1412 assert!(
1413 !seen.contains("hunter2") && !seen.contains("secret"),
1414 "{seen}"
1415 );
1416 }
1417
1418 #[test]
1419 fn test_charset_labels() {
1420 assert_eq!(Charset::from_label(b"UTF-8"), Charset::Utf8);
1421 assert_eq!(Charset::from_label(b"us-ascii"), Charset::Ascii);
1422 assert_eq!(Charset::from_label(b"utf8"), Charset::Other);
1423 }
1424}