multipart2/server/
save.rs

1// Copyright 2016 `multipart` Crate Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7//! Utilities for saving request entries to the filesystem.
8
9pub use server::buffer_redux::BufReader;
10
11pub use tempfile::TempDir;
12
13use std::collections::HashMap;
14use std::fs::{self, File, OpenOptions};
15use std::io::prelude::*;
16use std::path::{Path, PathBuf};
17use std::sync::Arc;
18use std::{cmp, env, io, mem, str, u32, u64};
19use tempfile;
20
21use server::field::{FieldHeaders, MultipartData, MultipartField, ReadEntry, ReadEntryResult};
22
23use self::PartialReason::*;
24use self::SaveResult::*;
25use self::TextPolicy::*;
26
27const RANDOM_FILENAME_LEN: usize = 12;
28
29fn rand_filename() -> String {
30    ::random_alphanumeric(RANDOM_FILENAME_LEN)
31}
32
33macro_rules! try_start (
34    ($try:expr) => (
35        match $try {
36            Ok(val) => val,
37            Err(e) => return Error(e),
38        }
39    )
40);
41
42macro_rules! try_full (
43    ($try:expr) => {
44        match $try {
45            Full(full) => full,
46            other => return other,
47        }
48    }
49);
50
51macro_rules! try_partial (
52    ($try:expr) => {
53        match $try {
54            Full(full) => return Full(full.into()),
55            Partial(partial, reason) => (partial, reason),
56            Error(e) => return Error(e),
57        }
58    }
59);
60
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62enum TextPolicy {
63    /// Attempt to read a text field as text, falling back to binary on error
64    Try,
65    /// Attempt to read a text field as text, returning any errors
66    Force,
67    /// Don't try to read text
68    Ignore,
69}
70
71/// A builder for saving a file or files to the local filesystem.
72///
73/// ### `OpenOptions`
74/// This builder holds an instance of `std::fs::OpenOptions` which is used
75/// when creating the new file(s).
76///
77/// By default, the open options are set with `.write(true).create_new(true)`,
78/// so if the file already exists then an error will be thrown. This is to avoid accidentally
79/// overwriting files from other requests.
80///
81/// If you want to modify the options used to open the save file, you can use
82/// `mod_open_opts()`.
83///
84/// ### File Size and Count Limits
85/// You can set a size limit for individual fields with `size_limit()`, which takes either `u64`
86/// or `Option<u64>`.
87///
88/// You can also set the maximum number of fields to process with `count_limit()`, which
89/// takes either `u32` or `Option<u32>`. This only has an effect when using
90/// `SaveBuilder<[&mut] Multipart>`.
91///
92/// By default, these limits are set conservatively to limit the maximum memory and disk space
93/// usage of a single request. You should set `count_limit` specifically for each request endpoint
94/// based on the number of fields you're expecting (exactly to that number if you're not expecting
95/// duplicate fields).
96///
97/// ### Memory Threshold and Text Policy
98/// By default, small fields (a few kilobytes or smaller) will be read directly to memory
99/// without creating a file. This behavior is controlled by the `memory_threshold()` setter. You can
100/// *roughly* tune the maximum memory a single request uses by tuning
101/// `count_limit * memory_threshold`
102///
103/// If a field appears to contain text data (its content-type is `text/*` or it doesn't declare
104/// one), `SaveBuilder` can read it to a string instead of saving the raw bytes as long as it falls
105/// below the set `memory_threshold`.
106///
107/// By default, the behavior is to attempt to validate the data as UTF-8, falling back to saving
108/// just the bytes if the validation fails at any point. You can restore/ensure this behavior
109/// with the `try_text()` modifier.
110///
111/// Alternatively, you can use the `force_text()` modifier to make the save operation return
112/// an error when UTF-8 decoding fails, though this only holds true while the size is below
113/// `memory_threshold`. The `ignore_text()` modifier turns off UTF-8 validation altogether.
114///
115/// UTF-8 validation is performed incrementally (after every `BufRead::fill_buf()` call)
116/// to hopefully maximize throughput, instead of blocking while the field is read to completion
117/// and performing validation over the entire result at the end. (RFC: this could be a lot of
118/// unnecessary work if most fields end up being written to the filesystem, however, but this
119/// can be turned off with `ignore_text()` if it fits the use-case.)
120///
121/// ### Warning: Do **not** trust user input!
122/// It is a serious security risk to create files or directories with paths based on user input.
123/// A malicious user could craft a path which can be used to overwrite important files, such as
124/// web templates, static assets, Javascript files, database files, configuration files, etc.,
125/// if they are writable by the server process.
126///
127/// This can be mitigated somewhat by setting filesystem permissions as
128/// conservatively as possible and running the server under its own user with restricted
129/// permissions, but you should still not use user input directly as filesystem paths.
130/// If it is truly necessary, you should sanitize user input such that it cannot cause a path to be
131/// misinterpreted by the OS. Such functionality is outside the scope of this crate.
132#[must_use = "nothing saved to the filesystem yet"]
133pub struct SaveBuilder<S> {
134    savable: S,
135    open_opts: OpenOptions,
136    size_limit: u64,
137    count_limit: u32,
138    memory_threshold: u64,
139    text_policy: TextPolicy,
140}
141
142/// Common methods for whole requests as well as individual fields.
143impl<S> SaveBuilder<S> {
144    /// Implementation detail but not problematic to have accessible.
145    #[doc(hidden)]
146    pub fn new(savable: S) -> SaveBuilder<S> {
147        let mut open_opts = OpenOptions::new();
148        open_opts.write(true).create_new(true);
149
150        SaveBuilder {
151            savable,
152            open_opts,
153            // 8 MiB, on the conservative end compared to most frameworks
154            size_limit: 8 * 1024 * 1024,
155            // Arbitrary, I have no empirical data for this
156            count_limit: 256,
157            // 10KiB, used by Apache Commons
158            // https://commons.apache.org/proper/commons-fileupload/apidocs/org/apache/commons/fileupload/disk/DiskFileItemFactory.html
159            memory_threshold: 10 * 1024,
160            text_policy: TextPolicy::Try,
161        }
162    }
163
164    /// Set the maximum number of bytes to write out *per file*.
165    ///
166    /// Can be `u64` or `Option<u64>`. If `None` or `u64::MAX`, clears the limit.
167    pub fn size_limit<L: Into<Option<u64>>>(mut self, limit: L) -> Self {
168        self.size_limit = limit.into().unwrap_or(u64::MAX);
169        self
170    }
171
172    /// Modify the `OpenOptions` used to open any files for writing.
173    ///
174    /// The `write` flag will be reset to `true` after the closure returns. (It'd be pretty
175    /// pointless otherwise, right?)
176    pub fn mod_open_opts<F: FnOnce(&mut OpenOptions)>(mut self, opts_fn: F) -> Self {
177        opts_fn(&mut self.open_opts);
178        self.open_opts.write(true);
179        self
180    }
181
182    /// Set the threshold at which to switch from copying a field into memory to copying
183    /// it to disk.
184    ///
185    /// If `0`, forces fields to save directly to the filesystem.
186    /// If `u64::MAX`, effectively forces fields to always save to memory.
187    pub fn memory_threshold(self, memory_threshold: u64) -> Self {
188        Self {
189            memory_threshold,
190            ..self
191        }
192    }
193
194    /// When encountering a field that is apparently text, try to read it to a string or fall
195    /// back to binary otherwise.
196    ///
197    /// If set for an individual field (`SaveBuilder<&mut MultipartData<_>>`), will
198    /// always attempt to decode text regardless of the field's `Content-Type`.
199    ///
200    /// Has no effect once `memory_threshold` has been reached.
201    pub fn try_text(self) -> Self {
202        Self {
203            text_policy: TextPolicy::Try,
204            ..self
205        }
206    }
207
208    /// When encountering a field that is apparently text, read it to a string or return an error.
209    ///
210    /// If set for an individual field (`SaveBuilder<&mut MultipartData<_>>`), will
211    /// always attempt to decode text regardless of the field's `Content-Type`.
212    ///
213    /// (RFC: should this continue to validate UTF-8 when writing to the filesystem?)
214    pub fn force_text(self) -> Self {
215        Self {
216            text_policy: TextPolicy::Force,
217            ..self
218        }
219    }
220
221    /// Don't try to read or validate any field data as UTF-8.
222    pub fn ignore_text(self) -> Self {
223        Self {
224            text_policy: TextPolicy::Ignore,
225            ..self
226        }
227    }
228}
229
230/// Save API for whole multipart requests.
231impl<M> SaveBuilder<M>
232where
233    M: ReadEntry,
234{
235    /// Set the maximum number of fields to process.
236    ///
237    /// Can be `u32` or `Option<u32>`. If `None` or `u32::MAX`, clears the limit.
238    pub fn count_limit<L: Into<Option<u32>>>(mut self, count_limit: L) -> Self {
239        self.count_limit = count_limit.into().unwrap_or(u32::MAX);
240        self
241    }
242
243    /// Save all fields in the request using a new temporary directory prefixed with
244    /// `multipart-rs` in the OS temporary directory.
245    ///
246    /// For more options, create a `TempDir` yourself and pass it to `with_temp_dir()` instead.
247    ///
248    /// See `with_entries()` for more info.
249    ///
250    /// ### Note: Temporary
251    /// See `SaveDir` for more info (the type of `Entries::save_dir`).
252    pub fn temp(self) -> EntriesSaveResult<M> {
253        self.temp_with_prefix("multipart-rs")
254    }
255
256    /// Save all fields in the request using a new temporary directory with the given string
257    /// as a prefix in the OS temporary directory.
258    ///
259    /// For more options, create a `TempDir` yourself and pass it to `with_temp_dir()` instead.
260    ///
261    /// See `with_entries()` for more info.
262    ///
263    /// ### Note: Temporary
264    /// See `SaveDir` for more info (the type of `Entries::save_dir`).
265    pub fn temp_with_prefix(self, prefix: &str) -> EntriesSaveResult<M> {
266        match tempfile::Builder::new().prefix(prefix).tempdir() {
267            Ok(tempdir) => self.with_temp_dir(tempdir),
268            Err(e) => SaveResult::Error(e),
269        }
270    }
271
272    /// Save all fields in the request using the given `TempDir`.
273    ///
274    /// See `with_entries()` for more info.
275    ///
276    /// The `TempDir` is returned in the result under `Entries::save_dir`.
277    pub fn with_temp_dir(self, tempdir: TempDir) -> EntriesSaveResult<M> {
278        self.with_entries(Entries::new(SaveDir::Temp(tempdir)))
279    }
280
281    /// Save the file fields in the request to a new permanent directory with the given path.
282    ///
283    /// Any nonexistent directories in the path will be created.
284    ///
285    /// See `with_entries()` for more info.
286    pub fn with_dir<P: Into<PathBuf>>(self, dir: P) -> EntriesSaveResult<M> {
287        let dir = dir.into();
288
289        try_start!(create_dir_all(&dir));
290
291        self.with_entries(Entries::new(SaveDir::Perm(dir)))
292    }
293
294    /// Commence the save operation using the existing `Entries` instance.
295    ///
296    /// May be used to resume a saving operation after handling an error.
297    ///
298    /// If `count_limit` is set, only reads that many fields before returning an error.
299    /// If you wish to resume from `PartialReason::CountLimit`, simply remove some entries.
300    ///
301    /// Note that `PartialReason::CountLimit` will still be returned if the number of fields
302    /// reaches `u32::MAX`, but this would be an extremely degenerate case.
303    pub fn with_entries(self, mut entries: Entries) -> EntriesSaveResult<M> {
304        let SaveBuilder {
305            savable,
306            open_opts,
307            count_limit,
308            size_limit,
309            memory_threshold,
310            text_policy,
311        } = self;
312
313        let mut res = ReadEntry::read_entry(savable);
314
315        let _ = entries.recount_fields();
316
317        let save_field = |field: &mut MultipartField<M>, entries: &Entries| {
318            let text_policy = if field.is_text() { text_policy } else { Ignore };
319
320            let mut saver = SaveBuilder {
321                savable: &mut field.data,
322                open_opts: open_opts.clone(),
323                count_limit,
324                size_limit,
325                memory_threshold,
326                text_policy,
327            };
328
329            saver.with_dir(entries.save_dir.as_path())
330        };
331
332        while entries.fields_count < count_limit {
333            let mut field: MultipartField<M> = match res {
334                ReadEntryResult::Entry(field) => field,
335                ReadEntryResult::End(_) => return Full(entries), // normal exit point
336                ReadEntryResult::Error(_, e) => {
337                    return Partial(
338                        PartialEntries {
339                            entries,
340                            partial: None,
341                        },
342                        e.into(),
343                    )
344                }
345            };
346
347            let (dest, reason) = match save_field(&mut field, &entries) {
348                Full(saved) => {
349                    entries.push_field(field.headers, saved);
350                    res = ReadEntry::read_entry(field.data.into_inner());
351                    continue;
352                }
353                Partial(saved, reason) => (Some(saved), reason),
354                Error(error) => (None, PartialReason::IoError(error)),
355            };
356
357            return Partial(
358                PartialEntries {
359                    entries,
360                    partial: Some(PartialSavedField {
361                        source: field,
362                        dest,
363                    }),
364                },
365                reason,
366            );
367        }
368
369        Partial(
370            PartialEntries {
371                entries,
372                partial: None,
373            },
374            PartialReason::CountLimit,
375        )
376    }
377}
378
379/// Save API for individual fields.
380impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData<M>>
381where
382    MultipartData<M>: BufRead,
383{
384    /// Save the field data, potentially using a file with a random name in the
385    /// OS temporary directory.
386    ///
387    /// See `with_path()` for more details.
388    pub fn temp(&mut self) -> FieldSaveResult {
389        let path = env::temp_dir().join(rand_filename());
390        self.with_path(path)
391    }
392
393    /// Save the field data, potentially using a file with the given name in
394    /// the OS temporary directory.
395    ///
396    /// See `with_path()` for more details.
397    pub fn with_filename(&mut self, filename: &str) -> FieldSaveResult {
398        let mut tempdir = env::temp_dir();
399        tempdir.set_file_name(filename);
400
401        self.with_path(tempdir)
402    }
403
404    /// Save the field data, potentially using a file with a random alphanumeric name
405    /// in the given directory.
406    ///
407    /// See `with_path()` for more details.
408    pub fn with_dir<P: AsRef<Path>>(&mut self, dir: P) -> FieldSaveResult {
409        let path = dir.as_ref().join(rand_filename());
410        self.with_path(path)
411    }
412
413    /// Save the field data, potentially using a file with the given path.
414    ///
415    /// Creates any missing directories in the path (RFC: skip this step?).
416    /// Uses the contained `OpenOptions` to create the file.
417    /// Truncates the file to the given `size_limit`, if set.
418    ///
419    /// The no directories or files will be created until the set `memory_threshold` is reached.
420    /// If `size_limit` is set and less than or equal to `memory_threshold`,
421    /// then the disk will never be touched.
422    pub fn with_path<P: Into<PathBuf>>(&mut self, path: P) -> FieldSaveResult {
423        let bytes = if self.text_policy != Ignore {
424            let (text, reason) = try_partial!(self.save_text());
425            match reason {
426                SizeLimit if !self.cmp_size_limit(text.len()) => text.into_bytes(),
427                Utf8Error(_) if self.text_policy != Force => text.into_bytes(),
428                other => return Partial(text.into(), other),
429            }
430        } else {
431            Vec::new()
432        };
433
434        let (bytes, reason) = try_partial!(self.save_mem(bytes));
435
436        match reason {
437            SizeLimit if !self.cmp_size_limit(bytes.len()) => (),
438            other => return Partial(bytes.into(), other),
439        }
440
441        let path = path.into();
442
443        let mut file = match create_dir_all(&path).and_then(|_| self.open_opts.open(&path)) {
444            Ok(file) => file,
445            Err(e) => return Error(e),
446        };
447
448        let data =
449            try_full!(try_write_all(&bytes, &mut file)
450                .map(move |size| SavedData::File(path, size as u64)));
451
452        self.write_to(file)
453            .map(move |written| data.add_size(written))
454    }
455
456    /// Write out the field data to `dest`, truncating if a limit was set.
457    ///
458    /// Returns the number of bytes copied, and whether or not the limit was reached
459    /// (tested by `MultipartFile::fill_buf().is_empty()` so no bytes are consumed).
460    ///
461    /// Retries on interrupts.
462    pub fn write_to<W: Write>(&mut self, mut dest: W) -> SaveResult<u64, u64> {
463        if self.size_limit < u64::MAX {
464            try_copy_limited(
465                &mut self.savable,
466                |buf| try_write_all(buf, &mut dest),
467                self.size_limit,
468            )
469        } else {
470            try_read_buf(&mut self.savable, |buf| try_write_all(buf, &mut dest))
471        }
472    }
473
474    fn save_mem(&mut self, mut bytes: Vec<u8>) -> SaveResult<Vec<u8>, Vec<u8>> {
475        let pre_read = bytes.len() as u64;
476        match self.read_mem(
477            |buf| {
478                bytes.extend_from_slice(buf);
479                Full(buf.len())
480            },
481            pre_read,
482        ) {
483            Full(_) => Full(bytes),
484            Partial(_, reason) => Partial(bytes, reason),
485            Error(e) => {
486                if !bytes.is_empty() {
487                    Partial(bytes, e.into())
488                } else {
489                    Error(e)
490                }
491            }
492        }
493    }
494
495    fn save_text(&mut self) -> SaveResult<String, String> {
496        let mut string = String::new();
497
498        // incrementally validate UTF-8 to do as much work as possible during network activity
499        let res = self.read_mem(
500            |buf| {
501                match str::from_utf8(buf) {
502                    Ok(s) => {
503                        string.push_str(s);
504                        Full(buf.len())
505                    }
506                    // buffer should always be bigger
507                    Err(e) => {
508                        if buf.len() < 4 {
509                            Partial(0, e.into())
510                        } else {
511                            string.push_str(str::from_utf8(&buf[..e.valid_up_to()]).unwrap());
512                            Full(e.valid_up_to())
513                        }
514                    }
515                }
516            },
517            0,
518        );
519
520        match res {
521            Full(_) => Full(string),
522            Partial(_, reason) => Partial(string, reason),
523            Error(e) => Error(e),
524        }
525    }
526
527    fn read_mem<Wb: FnMut(&[u8]) -> SaveResult<usize, usize>>(
528        &mut self,
529        with_buf: Wb,
530        pre_read: u64,
531    ) -> SaveResult<u64, u64> {
532        let limit = cmp::min(self.size_limit, self.memory_threshold).saturating_sub(pre_read);
533        try_copy_limited(&mut self.savable, with_buf, limit)
534    }
535
536    fn cmp_size_limit(&self, size: usize) -> bool {
537        size as u64 >= self.size_limit
538    }
539}
540
541/// A field that has been saved (to memory or disk) from a multipart request.
542#[derive(Debug)]
543pub struct SavedField {
544    /// The headers of the field that was saved.
545    pub headers: FieldHeaders,
546    /// The data of the field which may reside in memory or on disk.
547    pub data: SavedData,
548}
549
550/// A saved field's data container (in memory or on disk)
551#[derive(Debug)]
552pub enum SavedData {
553    /// Validated UTF-8 text data.
554    Text(String),
555    /// Binary data.
556    Bytes(Vec<u8>),
557    /// A path to a file on the filesystem and its size as written by `multipart`.
558    File(PathBuf, u64),
559}
560
561impl SavedData {
562    /// Get an adapter for this data which implements `Read`.
563    ///
564    /// If the data is in a file, the file is opened in read-only mode.
565    pub fn readable(&self) -> io::Result<DataReader> {
566        use self::SavedData::*;
567
568        match *self {
569            Text(ref text) => Ok(DataReader::Bytes(text.as_ref())),
570            Bytes(ref bytes) => Ok(DataReader::Bytes(bytes)),
571            File(ref path, _) => Ok(DataReader::File(BufReader::new(fs::File::open(path)?))),
572        }
573    }
574
575    /// Get the size of the data, in memory or on disk.
576    ///
577    /// #### Note
578    /// The size on disk may not match the size of the file if it is externally modified.
579    pub fn size(&self) -> u64 {
580        use self::SavedData::*;
581
582        match *self {
583            Text(ref text) => text.len() as u64,
584            Bytes(ref bytes) => bytes.len() as u64,
585            File(_, size) => size,
586        }
587    }
588
589    /// Returns `true` if the data is known to be in memory (`Text | Bytes`)
590    pub fn is_memory(&self) -> bool {
591        use self::SavedData::*;
592
593        match *self {
594            Text(_) | Bytes(_) => true,
595            File(_, _) => false,
596        }
597    }
598
599    fn add_size(self, add: u64) -> Self {
600        use self::SavedData::File;
601
602        match self {
603            File(path, size) => File(path, size.saturating_add(add)),
604            other => other,
605        }
606    }
607}
608
609impl From<String> for SavedData {
610    fn from(s: String) -> Self {
611        SavedData::Text(s)
612    }
613}
614
615impl From<Vec<u8>> for SavedData {
616    fn from(b: Vec<u8>) -> Self {
617        SavedData::Bytes(b)
618    }
619}
620
621/// A `Read` (and `BufRead`) adapter for `SavedData`
622pub enum DataReader<'a> {
623    /// In-memory data source (`SavedData::Bytes | Text`)
624    Bytes(&'a [u8]),
625    /// On-disk data source (`SavedData::File`)
626    File(BufReader<File>),
627}
628
629impl<'a> Read for DataReader<'a> {
630    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
631        use self::DataReader::*;
632
633        match *self {
634            Bytes(ref mut bytes) => bytes.read(buf),
635            File(ref mut file) => file.read(buf),
636        }
637    }
638}
639
640impl<'a> BufRead for DataReader<'a> {
641    fn fill_buf(&mut self) -> io::Result<&[u8]> {
642        use self::DataReader::*;
643
644        match *self {
645            Bytes(ref mut bytes) => bytes.fill_buf(),
646            File(ref mut file) => file.fill_buf(),
647        }
648    }
649
650    fn consume(&mut self, amt: usize) {
651        use self::DataReader::*;
652
653        match *self {
654            Bytes(ref mut bytes) => bytes.consume(amt),
655            File(ref mut file) => file.consume(amt),
656        }
657    }
658}
659
660/// A result of `Multipart::save()`.
661#[derive(Debug)]
662pub struct Entries {
663    /// The fields of the multipart request, mapped by field name -> value.
664    ///
665    /// A field name may have multiple actual fields associated with it, but the most
666    /// common case is a single field.
667    ///
668    /// Each vector is guaranteed not to be empty unless externally modified.
669    // Even though individual fields might only have one entry, it's better to limit the
670    // size of a value type in `HashMap` to improve cache efficiency in lookups.
671    pub fields: HashMap<Arc<str>, Vec<SavedField>>,
672    /// The directory that the entries in `fields` were saved into.
673    pub save_dir: SaveDir,
674    fields_count: u32,
675}
676
677impl Entries {
678    /// Create a new `Entries` with the given `SaveDir`
679    pub fn new(save_dir: SaveDir) -> Self {
680        Entries {
681            fields: HashMap::new(),
682            save_dir,
683            fields_count: 0,
684        }
685    }
686
687    /// Returns `true` if `fields` is empty, `false` otherwise.
688    pub fn is_empty(&self) -> bool {
689        self.fields.is_empty()
690    }
691
692    /// The number of actual fields contained within this `Entries`.
693    ///
694    /// Effectively `self.fields.values().map(Vec::len).sum()` but maintained separately.
695    ///
696    /// ## Note
697    /// This will be incorrect if `fields` is modified externally. Call `recount_fields()`
698    /// to get the correct count.
699    pub fn fields_count(&self) -> u32 {
700        self.fields_count
701    }
702
703    /// Sum the number of fields in this `Entries` and then return the updated value.
704    pub fn recount_fields(&mut self) -> u32 {
705        let fields_count = self.fields.values().map(Vec::len).sum();
706        // saturating cast
707        self.fields_count = cmp::min(u32::MAX as usize, fields_count) as u32;
708        self.fields_count
709    }
710
711    fn push_field(&mut self, mut headers: FieldHeaders, data: SavedData) {
712        use std::collections::hash_map::Entry::*;
713
714        match self.fields.entry(headers.name.clone()) {
715            Vacant(vacant) => {
716                vacant.insert(vec![SavedField { headers, data }]);
717            }
718            Occupied(occupied) => {
719                // dedup the field name by reusing the key's `Arc`
720                headers.name = occupied.key().clone();
721                occupied.into_mut().push(SavedField { headers, data });
722            }
723        }
724
725        self.fields_count = self.fields_count.saturating_add(1);
726    }
727
728    /// Print all fields and their contents to stdout. Mostly for testing purposes.
729    pub fn print_debug(&self) -> io::Result<()> {
730        let stdout = io::stdout();
731        let stdout_lock = stdout.lock();
732        self.write_debug(stdout_lock)
733    }
734
735    /// Write all fields and their contents to the given output. Mostly for testing purposes.
736    pub fn write_debug<W: Write>(&self, mut writer: W) -> io::Result<()> {
737        for (name, entries) in &self.fields {
738            writeln!(writer, "Field {:?} has {} entries:", name, entries.len())?;
739
740            for (idx, field) in entries.iter().enumerate() {
741                let mut data = field.data.readable()?;
742                let headers = &field.headers;
743                writeln!(
744                    writer,
745                    "{}: {:?} ({:?}):",
746                    idx, headers.filename, headers.content_type
747                )?;
748                io::copy(&mut data, &mut writer)?;
749            }
750        }
751
752        Ok(())
753    }
754}
755
756/// The save directory for `Entries`. May be temporary (delete-on-drop) or permanent.
757#[derive(Debug)]
758pub enum SaveDir {
759    /// This directory is temporary and will be deleted, along with its contents, when this wrapper
760    /// is dropped.
761    Temp(TempDir),
762    /// This directory is permanent and will be left on the filesystem when this wrapper is dropped.
763    ///
764    /// **N.B.** If this directory is in the OS temporary directory then it may still be
765    /// deleted at any time.
766    Perm(PathBuf),
767}
768
769impl SaveDir {
770    /// Get the path of this directory, either temporary or permanent.
771    pub fn as_path(&self) -> &Path {
772        use self::SaveDir::*;
773        match *self {
774            Temp(ref tempdir) => tempdir.path(),
775            Perm(ref pathbuf) => &*pathbuf,
776        }
777    }
778
779    /// Returns `true` if this is a temporary directory which will be deleted on-drop.
780    pub fn is_temporary(&self) -> bool {
781        use self::SaveDir::*;
782        match *self {
783            Temp(_) => true,
784            Perm(_) => false,
785        }
786    }
787
788    /// Unwrap the `PathBuf` from `self`; if this is a temporary directory,
789    /// it will be converted to a permanent one.
790    pub fn into_path(self) -> PathBuf {
791        use self::SaveDir::*;
792
793        match self {
794            Temp(tempdir) => tempdir.into_path(),
795            Perm(pathbuf) => pathbuf,
796        }
797    }
798
799    /// If this `SaveDir` is temporary, convert it to permanent.
800    /// This is a no-op if it already is permanent.
801    ///
802    /// ### Warning: Potential Data Loss
803    /// Even though this will prevent deletion on-drop, the temporary folder on most OSes
804    /// (where this directory is created by default) can be automatically cleared by the OS at any
805    /// time, usually on reboot or when free space is low.
806    ///
807    /// It is recommended that you relocate the files from a request which you want to keep to a
808    /// permanent folder on the filesystem.
809    pub fn keep(&mut self) {
810        use self::SaveDir::*;
811        *self = match mem::replace(self, Perm(PathBuf::new())) {
812            Temp(tempdir) => Perm(tempdir.into_path()),
813            old_self => old_self,
814        };
815    }
816
817    /// Delete this directory and its contents, regardless of its permanence.
818    ///
819    /// ### Warning: Potential Data Loss
820    /// This is very likely irreversible, depending on the OS implementation.
821    ///
822    /// Files deleted programmatically are deleted directly from disk, as compared to most file
823    /// manager applications which use a staging area from which deleted files can be safely
824    /// recovered (i.e. Windows' Recycle Bin, OS X's Trash Can, etc.).
825    pub fn delete(self) -> io::Result<()> {
826        use self::SaveDir::*;
827        match self {
828            Temp(tempdir) => tempdir.close(),
829            Perm(pathbuf) => fs::remove_dir_all(&pathbuf),
830        }
831    }
832}
833
834impl AsRef<Path> for SaveDir {
835    fn as_ref(&self) -> &Path {
836        self.as_path()
837    }
838}
839
840/// The reason the save operation quit partway through.
841#[derive(Debug)]
842pub enum PartialReason {
843    /// The count limit for files in the request was hit.
844    ///
845    /// The associated file has not been saved to the filesystem.
846    CountLimit,
847    /// The size limit for an individual file was hit.
848    ///
849    /// The file was partially written to the filesystem.
850    SizeLimit,
851    /// An error occurred during the operation.
852    IoError(io::Error),
853    /// An error returned from validating a field as UTF-8 due to `SaveBuilder::force_text()`
854    Utf8Error(str::Utf8Error),
855}
856
857impl From<io::Error> for PartialReason {
858    fn from(e: io::Error) -> Self {
859        IoError(e)
860    }
861}
862
863impl From<str::Utf8Error> for PartialReason {
864    fn from(e: str::Utf8Error) -> Self {
865        Utf8Error(e)
866    }
867}
868
869impl PartialReason {
870    /// Return `io::Error` in the `IoError` case or panic otherwise.
871    pub fn unwrap_err(self) -> io::Error {
872        self.expect_err("`PartialReason` was not `IoError`")
873    }
874
875    /// Return `io::Error` in the `IoError` case or panic with the given
876    /// message otherwise.
877    pub fn expect_err(self, msg: &str) -> io::Error {
878        match self {
879            PartialReason::IoError(e) => e,
880            _ => panic!("{}: {:?}", msg, self),
881        }
882    }
883}
884
885/// The field that was being read when the save operation quit.
886///
887/// May be partially saved to the filesystem if `dest` is `Some`.
888#[derive(Debug)]
889pub struct PartialSavedField<M: ReadEntry> {
890    /// The field that was being read.
891    ///
892    /// May be partially read if `dest` is `Some`.
893    pub source: MultipartField<M>,
894    /// The data from the saving operation, if it got that far.
895    pub dest: Option<SavedData>,
896}
897
898/// The partial result type for `Multipart::save*()`.
899///
900/// Contains the successfully saved entries as well as the partially
901/// saved file that was in the process of being read when the error occurred,
902/// if applicable.
903#[derive(Debug)]
904pub struct PartialEntries<M: ReadEntry> {
905    /// The entries that were saved successfully.
906    pub entries: Entries,
907    /// The field that was in the process of being read. `None` if the error
908    /// occurred between entries.
909    pub partial: Option<PartialSavedField<M>>,
910}
911
912/// Discards `partial`
913impl<M: ReadEntry> Into<Entries> for PartialEntries<M> {
914    fn into(self) -> Entries {
915        self.entries
916    }
917}
918
919impl<M: ReadEntry> PartialEntries<M> {
920    /// If `partial` is present and contains a `SavedFile` then just
921    /// add it to the `Entries` instance and return it.
922    ///
923    /// Otherwise, returns `self.entries`
924    pub fn keep_partial(mut self) -> Entries {
925        if let Some(partial) = self.partial {
926            if let Some(saved) = partial.dest {
927                self.entries.push_field(partial.source.headers, saved);
928            }
929        }
930
931        self.entries
932    }
933}
934
935/// The ternary result type used for the `SaveBuilder<_>` API.
936#[derive(Debug)]
937pub enum SaveResult<Success, Partial> {
938    /// The operation was a total success. Contained is the complete result.
939    Full(Success),
940    /// The operation quit partway through. Included is the partial
941    /// result along with the reason.
942    Partial(Partial, PartialReason),
943    /// An error occurred at the start of the operation, before anything was done.
944    Error(io::Error),
945}
946
947/// Shorthand result for methods that return `Entries`
948pub type EntriesSaveResult<M> = SaveResult<Entries, PartialEntries<M>>;
949
950/// Shorthand result for methods that return `FieldData`s.
951///
952/// The `MultipartData` is not provided here because it is not necessary to return
953/// a borrow when the owned version is probably in the same scope. This hopefully
954/// saves some headache with the borrow-checker.
955pub type FieldSaveResult = SaveResult<SavedData, SavedData>;
956
957impl<M: ReadEntry> EntriesSaveResult<M> {
958    /// Take the `Entries` from `self`, if applicable, and discarding
959    /// the error, if any.
960    pub fn into_entries(self) -> Option<Entries> {
961        match self {
962            Full(entries) | Partial(PartialEntries { entries, .. }, _) => Some(entries),
963            Error(_) => None,
964        }
965    }
966}
967
968impl<S, P> SaveResult<S, P>
969where
970    P: Into<S>,
971{
972    /// Convert `self` to `Option<S>`; there may still have been an error.
973    pub fn okish(self) -> Option<S> {
974        self.into_opt_both().0
975    }
976
977    /// Map the `Full` or `Partial` values to a new type, retaining the reason
978    /// in the `Partial` case.
979    pub fn map<T, Map>(self, map: Map) -> SaveResult<T, T>
980    where
981        Map: FnOnce(S) -> T,
982    {
983        match self {
984            Full(full) => Full(map(full)),
985            Partial(partial, reason) => Partial(map(partial.into()), reason),
986            Error(e) => Error(e),
987        }
988    }
989
990    /// Decompose `self` to `(Option<S>, Option<io::Error>)`
991    pub fn into_opt_both(self) -> (Option<S>, Option<io::Error>) {
992        match self {
993            Full(full) => (Some(full), None),
994            Partial(partial, IoError(e)) => (Some(partial.into()), Some(e)),
995            Partial(partial, _) => (Some(partial.into()), None),
996            Error(error) => (None, Some(error)),
997        }
998    }
999
1000    /// Map `self` to an `io::Result`, discarding the error in the `Partial` case.
1001    pub fn into_result(self) -> io::Result<S> {
1002        match self {
1003            Full(entries) => Ok(entries),
1004            Partial(partial, _) => Ok(partial.into()),
1005            Error(error) => Err(error),
1006        }
1007    }
1008
1009    /// Pessimistic version of `into_result()` which will return an error even
1010    /// for the `Partial` case.
1011    ///
1012    /// ### Note: Possible Storage Leak
1013    /// It's generally not a good idea to ignore the `Partial` case, as there may still be a
1014    /// partially written file on-disk. If you're not using a temporary directory
1015    /// (OS-managed or via `TempDir`) then partially written files will remain on-disk until
1016    /// explicitly removed which could result in excessive disk usage if not monitored closely.
1017    pub fn into_result_strict(self) -> io::Result<S> {
1018        match self {
1019            Full(entries) => Ok(entries),
1020            Partial(_, PartialReason::IoError(e)) | Error(e) => Err(e),
1021            Partial(partial, _) => Ok(partial.into()),
1022        }
1023    }
1024}
1025
1026fn create_dir_all(path: &Path) -> io::Result<()> {
1027    if let Some(parent) = path.parent() {
1028        fs::create_dir_all(parent)
1029    } else {
1030        // RFC: return an error instead?
1031        warn!(
1032            "Attempting to save file in what looks like a root directory. File path: {:?}",
1033            path
1034        );
1035        Ok(())
1036    }
1037}
1038
1039fn try_copy_limited<R: BufRead, Wb: FnMut(&[u8]) -> SaveResult<usize, usize>>(
1040    src: R,
1041    mut with_buf: Wb,
1042    limit: u64,
1043) -> SaveResult<u64, u64> {
1044    let mut copied = 0u64;
1045    try_read_buf(src, |buf| {
1046        let new_copied = copied.saturating_add(buf.len() as u64);
1047        if new_copied > limit {
1048            return Partial(0, PartialReason::SizeLimit);
1049        }
1050        copied = new_copied;
1051
1052        with_buf(buf)
1053    })
1054}
1055
1056fn try_read_buf<R: BufRead, Wb: FnMut(&[u8]) -> SaveResult<usize, usize>>(
1057    mut src: R,
1058    mut with_buf: Wb,
1059) -> SaveResult<u64, u64> {
1060    let mut total_copied = 0u64;
1061
1062    macro_rules! try_here (
1063        ($try:expr) => (
1064            match $try {
1065                Ok(val) => val,
1066                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
1067                Err(e) => return if total_copied == 0 { Error(e) }
1068                                 else { Partial(total_copied, e.into()) },
1069            }
1070        )
1071    );
1072
1073    loop {
1074        let res = {
1075            let buf = try_here!(src.fill_buf());
1076            if buf.is_empty() {
1077                break;
1078            }
1079            with_buf(buf)
1080        };
1081
1082        match res {
1083            Full(copied) => {
1084                src.consume(copied);
1085                total_copied += copied as u64;
1086            }
1087            Partial(copied, reason) => {
1088                src.consume(copied);
1089                total_copied += copied as u64;
1090                return Partial(total_copied, reason);
1091            }
1092            Error(err) => {
1093                return Partial(total_copied, err.into());
1094            }
1095        }
1096    }
1097
1098    Full(total_copied)
1099}
1100
1101fn try_write_all<W: Write>(mut buf: &[u8], mut dest: W) -> SaveResult<usize, usize> {
1102    let mut total_copied = 0;
1103
1104    macro_rules! try_here (
1105        ($try:expr) => (
1106            match $try {
1107                Ok(val) => val,
1108                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
1109                Err(e) => return if total_copied == 0 { Error(e) }
1110                                 else { Partial(total_copied, e.into()) },
1111            }
1112        )
1113    );
1114
1115    while !buf.is_empty() {
1116        match try_here!(dest.write(buf)) {
1117            0 => try_here!(Err(io::Error::new(
1118                io::ErrorKind::WriteZero,
1119                "failed to write whole buffer"
1120            ))),
1121            copied => {
1122                buf = &buf[copied..];
1123                total_copied += copied;
1124            }
1125        }
1126    }
1127
1128    Full(total_copied)
1129}