1use mime::Mime;
10
11use std::error::Error;
12use std::io::{self, BufRead, Read};
13use std::{fmt, str};
14
15use std::sync::Arc;
16
17use super::httparse::{self, Error as HttparseError, Header, Status, EMPTY_HEADER};
18
19use self::ReadEntryResult::*;
20
21use super::save::SaveBuilder;
22
23const EMPTY_STR_HEADER: StrHeader<'static> = StrHeader { name: "", val: "" };
24
25macro_rules! invalid_cont_disp {
26 ($reason: expr, $cause: expr) => {
27 return Err(ParseHeaderError::InvalidContDisp(
28 $reason,
29 $cause.to_string(),
30 ));
31 };
32}
33
34#[derive(Copy, Clone, Debug)]
36pub struct StrHeader<'a> {
37 name: &'a str,
38 val: &'a str,
39}
40
41struct DisplayHeaders<'s, 'a: 's>(&'s [StrHeader<'a>]);
42
43impl<'s, 'a: 's> fmt::Display for DisplayHeaders<'s, 'a> {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 for hdr in self.0 {
46 writeln!(f, "{}: {}", hdr.name, hdr.val)?;
47 }
48
49 Ok(())
50 }
51}
52
53fn with_headers<R, F, Ret>(r: &mut R, closure: F) -> Result<Ret, ParseHeaderError>
54where
55 R: BufRead,
56 F: FnOnce(&[StrHeader]) -> Ret,
57{
58 const HEADER_LEN: usize = 4;
59
60 let consume;
61 let ret;
62
63 let mut last_len = 0;
64
65 loop {
66 let buf = r.fill_buf()?;
68
69 if buf.len() == last_len {
71 return Err(ParseHeaderError::TooLarge);
72 }
73
74 let mut raw_headers = [EMPTY_HEADER; HEADER_LEN];
75
76 match httparse::parse_headers(buf, &mut raw_headers)? {
77 Status::Partial => last_len = buf.len(),
79 Status::Complete((consume_, raw_headers)) => {
80 let mut headers = [EMPTY_STR_HEADER; HEADER_LEN];
81 let headers = copy_headers(raw_headers, &mut headers)?;
82 debug!("Parsed headers: {:?}", headers);
83 consume = consume_;
84 ret = closure(headers);
85 break;
86 }
87 }
88 }
89
90 r.consume(consume);
91 Ok(ret)
92}
93
94fn copy_headers<'h, 'b: 'h>(
95 raw: &[Header<'b>],
96 headers: &'h mut [StrHeader<'b>],
97) -> io::Result<&'h [StrHeader<'b>]> {
98 for (raw, header) in raw.iter().zip(&mut *headers) {
99 header.name = raw.name;
100 header.val = io_str_utf8(raw.value)?;
101 }
102
103 Ok(&headers[..raw.len()])
104}
105
106#[derive(Clone, Debug)]
112pub struct FieldHeaders {
113 pub name: Arc<str>,
115
116 pub filename: Option<String>,
119
120 pub content_type: Option<Mime>,
127}
128
129impl FieldHeaders {
130 fn read_from<R: BufRead>(r: &mut R) -> Result<Self, ParseHeaderError> {
132 with_headers(r, Self::parse)?
133 }
134
135 fn parse(headers: &[StrHeader]) -> Result<FieldHeaders, ParseHeaderError> {
136 let cont_disp = ContentDisp::parse_required(headers)?;
137
138 Ok(FieldHeaders {
139 name: cont_disp.field_name.into(),
140 filename: cont_disp.filename,
141 content_type: parse_content_type(headers)?,
142 })
143 }
144}
145
146struct ContentDisp {
148 field_name: String,
150 filename: Option<String>,
152}
153
154impl ContentDisp {
155 fn parse_required(headers: &[StrHeader]) -> Result<ContentDisp, ParseHeaderError> {
156 let header = if let Some(header) = find_header(headers, "Content-Disposition") {
157 header
158 } else {
159 return Err(ParseHeaderError::MissingContentDisposition(
160 DisplayHeaders(headers).to_string(),
161 ));
162 };
163
164 let after_disp_type = match split_once(header.val, ';') {
166 Some((disp_type, after_disp_type)) => {
167 if disp_type.trim() != "form-data" {
170 invalid_cont_disp!("unexpected Content-Disposition value", disp_type);
171 }
172 after_disp_type
173 }
174 None => invalid_cont_disp!(
175 "expected additional data after Content-Disposition type",
176 header.val
177 ),
178 };
179
180 let (field_name, filename) = match get_str_after("name=", ';', after_disp_type) {
182 None => invalid_cont_disp!(
183 "expected field name and maybe filename, got",
184 after_disp_type
185 ),
186 Some((field_name, after_field_name)) => {
188 let field_name = trim_quotes(field_name);
189 let filename = get_str_after("filename=", ';', after_field_name)
190 .map(|(filename, _)| trim_quotes(filename).to_owned());
191 (field_name, filename)
192 }
193 };
194
195 Ok(ContentDisp {
196 field_name: field_name.to_owned(),
197 filename,
198 })
199 }
200}
201
202fn parse_content_type(headers: &[StrHeader]) -> Result<Option<Mime>, ParseHeaderError> {
203 if let Some(header) = find_header(headers, "Content-Type") {
204 debug!("Found Content-Type: {:?}", header.val);
206 Ok(Some(header.val.parse::<Mime>().map_err(|_| {
207 ParseHeaderError::MimeError(header.val.into())
208 })?))
209 } else {
210 Ok(None)
211 }
212}
213
214#[derive(Debug)]
216pub struct MultipartField<M: ReadEntry> {
217 pub headers: FieldHeaders,
223
224 pub data: MultipartData<M>,
226}
227
228impl<M: ReadEntry> MultipartField<M> {
229 pub fn is_text(&self) -> bool {
239 self.headers
240 .content_type
241 .as_ref()
242 .map_or(true, |ct| ct.type_() == mime::TEXT)
243 }
244
245 pub fn next_entry(self) -> ReadEntryResult<M> {
247 self.data.into_inner().read_entry()
248 }
249
250 pub fn next_entry_inplace(&mut self) -> io::Result<Option<&mut Self>>
255 where
256 for<'a> &'a mut M: ReadEntry,
257 {
258 let multipart = self.data.take_inner();
259
260 match multipart.read_entry() {
261 Entry(entry) => {
262 *self = entry;
263 Ok(Some(self))
264 }
265 End(multipart) => {
266 self.data.give_inner(multipart);
267 Ok(None)
268 }
269 Error(multipart, err) => {
270 self.data.give_inner(multipart);
271 Err(err)
272 }
273 }
274 }
275}
276
277#[derive(Debug)]
281pub struct MultipartData<M> {
282 inner: Option<M>,
283}
284
285const DATA_INNER_ERR: &str = "MultipartFile::inner taken and not replaced; this is likely \
286 caused by a logic error in `multipart` or by resuming after \
287 a previously caught panic.\nPlease open an issue with the \
288 relevant backtrace and debug logs at \
289 https://github.com/abonander/multipart";
290
291impl<M> MultipartData<M>
292where
293 M: ReadEntry,
294{
295 pub fn save(&mut self) -> SaveBuilder<&mut Self> {
297 SaveBuilder::new(self)
298 }
299
300 pub fn into_inner(self) -> M {
302 self.inner.expect(DATA_INNER_ERR)
303 }
304
305 pub fn set_min_buf_size(&mut self, min_buf_size: usize) {
312 self.inner_mut().set_min_buf_size(min_buf_size)
313 }
314
315 fn inner_mut(&mut self) -> &mut M {
316 self.inner.as_mut().expect(DATA_INNER_ERR)
317 }
318
319 fn take_inner(&mut self) -> M {
320 self.inner.take().expect(DATA_INNER_ERR)
321 }
322
323 fn give_inner(&mut self, inner: M) {
324 self.inner = Some(inner);
325 }
326}
327
328impl<M: ReadEntry> Read for MultipartData<M> {
329 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
330 self.inner_mut().source_mut().read(buf)
331 }
332}
333
334impl<M: ReadEntry> BufRead for MultipartData<M> {
338 fn fill_buf(&mut self) -> io::Result<&[u8]> {
339 self.inner_mut().source_mut().fill_buf()
340 }
341
342 fn consume(&mut self, amt: usize) {
343 self.inner_mut().source_mut().consume(amt)
344 }
345}
346
347fn split_once(s: &str, delim: char) -> Option<(&str, &str)> {
348 s.find(delim).map(|idx| s.split_at(idx))
349}
350
351fn trim_quotes(s: &str) -> &str {
352 s.trim_matches('"')
353}
354
355fn get_str_after<'a>(
357 needle: &str,
358 end_val_delim: char,
359 haystack: &'a str,
360) -> Option<(&'a str, &'a str)> {
361 let val_start_idx = try_opt!(haystack.find(needle)) + needle.len();
362 let val_end_idx = haystack[val_start_idx..]
363 .find(end_val_delim)
364 .map_or(haystack.len(), |end_idx| end_idx + val_start_idx);
365 Some((
366 &haystack[val_start_idx..val_end_idx],
367 &haystack[val_end_idx..],
368 ))
369}
370
371fn io_str_utf8(buf: &[u8]) -> io::Result<&str> {
372 str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
373}
374
375fn find_header<'a, 'b>(headers: &'a [StrHeader<'b>], name: &str) -> Option<&'a StrHeader<'b>> {
376 headers
379 .iter()
380 .find(|header| header.name.eq_ignore_ascii_case(name))
381}
382
383pub trait ReadEntry: PrivReadEntry + Sized {
385 fn read_entry(mut self) -> ReadEntryResult<Self> {
387 self.set_min_buf_size(super::boundary::MIN_BUF_SIZE);
388
389 debug!("ReadEntry::read_entry()");
390
391 if !try_read_entry!(self; self.consume_boundary()) {
392 return End(self);
393 }
394
395 let field_headers: FieldHeaders = try_read_entry!(self; self.read_headers());
396
397 if let Some(ct) = field_headers.content_type.as_ref() {
398 if ct.type_() == mime::MULTIPART {
399 info!(
403 "Found nested multipart field: {:?}:\r\n\
404 Please report this client's User-Agent and any other available details \
405 at https://github.com/abonander/multipart/issues/56",
406 field_headers
407 );
408 }
409 }
410
411 Entry(MultipartField {
412 headers: field_headers,
413 data: MultipartData { inner: Some(self) },
414 })
415 }
416
417 fn read_entry_mut(&mut self) -> ReadEntryResult<&mut Self> {
419 ReadEntry::read_entry(self)
420 }
421}
422
423impl<T> ReadEntry for T where T: PrivReadEntry {}
424
425pub trait PrivReadEntry {
427 type Source: BufRead;
428
429 fn source_mut(&mut self) -> &mut Self::Source;
430
431 fn set_min_buf_size(&mut self, min_buf_size: usize);
432
433 fn consume_boundary(&mut self) -> io::Result<bool>;
436
437 fn read_headers(&mut self) -> Result<FieldHeaders, io::Error> {
438 FieldHeaders::read_from(self.source_mut())
439 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
440 }
441
442 fn read_to_string(&mut self) -> io::Result<String> {
443 let mut buf = String::new();
444
445 match self.source_mut().read_to_string(&mut buf) {
446 Ok(_) => Ok(buf),
447 Err(err) => Err(err),
448 }
449 }
450}
451
452impl<'a, M: ReadEntry> PrivReadEntry for &'a mut M {
453 type Source = M::Source;
454
455 fn source_mut(&mut self) -> &mut M::Source {
456 (**self).source_mut()
457 }
458
459 fn set_min_buf_size(&mut self, min_buf_size: usize) {
460 (**self).set_min_buf_size(min_buf_size)
461 }
462
463 fn consume_boundary(&mut self) -> io::Result<bool> {
464 (**self).consume_boundary()
465 }
466}
467
468pub enum ReadEntryResult<M: ReadEntry, Entry = MultipartField<M>> {
471 Entry(Entry),
473 End(M),
475 Error(M, io::Error),
477}
478
479impl<M: ReadEntry, Entry> ReadEntryResult<M, Entry> {
480 pub fn into_result(self) -> io::Result<Option<Entry>> {
486 match self {
487 ReadEntryResult::Entry(entry) => Ok(Some(entry)),
488 ReadEntryResult::End(_) => Ok(None),
489 ReadEntryResult::Error(_, err) => Err(err),
490 }
491 }
492
493 pub fn unwrap(self) -> Entry {
495 self.expect_alt(
496 "`ReadEntryResult::unwrap()` called on `End` value",
497 "`ReadEntryResult::unwrap()` called on `Error` value: {:?}",
498 )
499 }
500
501 pub fn expect(self, msg: &str) -> Entry {
504 self.expect_alt(msg, msg)
505 }
506
507 pub fn expect_alt(self, end_msg: &str, err_msg: &str) -> Entry {
511 match self {
512 Entry(entry) => entry,
513 End(_) => panic!("{}", end_msg),
514 Error(_, err) => panic!("{}: {:?}", err_msg, err),
515 }
516 }
517
518 pub fn unwrap_opt(self) -> Option<Entry> {
520 self.expect_opt("`ReadEntryResult::unwrap_opt()` called on `Error` value")
521 }
522
523 pub fn expect_opt(self, msg: &str) -> Option<Entry> {
526 match self {
527 Entry(entry) => Some(entry),
528 End(_) => None,
529 Error(_, err) => panic!("{}: {:?}", msg, err),
530 }
531 }
532}
533
534const GENERIC_PARSE_ERR: &str = "an error occurred while parsing field headers";
535
536quick_error! {
537 #[derive(Debug)]
538 enum ParseHeaderError {
539 MissingContentDisposition(headers: String) {
541 display(x) -> ("{}:\n{}", x.description(), headers)
542 description("\"Content-Disposition\" header not found in field headers")
543 }
544 InvalidContDisp(reason: &'static str, cause: String) {
545 display(x) -> ("{}: {}: {}", x.description(), reason, cause)
546 description("invalid \"Content-Disposition\" header")
547 }
548 TokenizeError(err: HttparseError) {
550 description(GENERIC_PARSE_ERR)
551 display(x) -> ("{}: {}", x.description(), err)
552 cause(err)
553 from()
554 }
555 MimeError(cont_type: String) {
556 description("Failed to parse Content-Type")
557 display(this) -> ("{}: {}", this.description(), cont_type)
558 }
559 TooLarge {
560 description("field headers section ridiculously long or missing trailing CRLF-CRLF")
561 }
562 Io(err: io::Error) {
564 description("an io error occurred while parsing the headers")
565 display(x) -> ("{}: {}", x.description(), err)
566 cause(err)
567 from()
568 }
569 }
570}
571
572#[test]
573fn test_find_header() {
574 let headers = [
575 StrHeader {
576 name: "Content-Type",
577 val: "text/plain",
578 },
579 StrHeader {
580 name: "Content-disposition",
581 val: "form-data",
582 },
583 StrHeader {
584 name: "content-transfer-encoding",
585 val: "binary",
586 },
587 ];
588
589 assert_eq!(
590 find_header(&headers, "Content-Type").unwrap().val,
591 "text/plain"
592 );
593 assert_eq!(
594 find_header(&headers, "Content-Disposition").unwrap().val,
595 "form-data"
596 );
597 assert_eq!(
598 find_header(&headers, "Content-Transfer-Encoding")
599 .unwrap()
600 .val,
601 "binary"
602 );
603}