mirror of https://github.com/rwf2/Rocket.git
Allow accessing raw file name from 'DataField'.
This commit is contained in:
parent
0654890e3d
commit
fa1b75ba74
|
@ -22,6 +22,7 @@ all-features = true
|
||||||
default = []
|
default = []
|
||||||
tls = ["rocket_http/tls"]
|
tls = ["rocket_http/tls"]
|
||||||
secrets = ["rocket_http/private-cookies"]
|
secrets = ["rocket_http/private-cookies"]
|
||||||
|
trusted_client = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.3.0"
|
futures = "0.3.0"
|
||||||
|
|
|
@ -461,7 +461,7 @@ impl<'v> FromFormField<'v> for Capped<TempFile<'v>> {
|
||||||
async fn from_data(
|
async fn from_data(
|
||||||
f: DataField<'v, '_>
|
f: DataField<'v, '_>
|
||||||
) -> Result<Self, Errors<'v>> {
|
) -> Result<Self, Errors<'v>> {
|
||||||
Ok(TempFile::from(f.request, f.data, f.file_name, Some(f.content_type)).await?)
|
Ok(TempFile::from(f.request, f.data, f.file_name.and_then(|f| f.name()), Some(f.content_type)).await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,62 @@ pub struct ValueField<'r> {
|
||||||
pub value: &'r str,
|
pub value: &'r str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A file name found in a multipart field
|
||||||
|
pub struct FileName<'r> {
|
||||||
|
raw_name: &'r str
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> FileName<'r> {
|
||||||
|
pub(crate) fn new(raw_name: &'r str) -> FileName<'r> {
|
||||||
|
FileName {
|
||||||
|
raw_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The form field's sanitized file name.
|
||||||
|
///
|
||||||
|
/// ## Santization
|
||||||
|
///
|
||||||
|
/// A "sanitized" file name is a non-empty string, stripped of its file
|
||||||
|
/// exntesion, which does not contain any path delimiters (`/` or `\`), is
|
||||||
|
/// not a hidden (`.`) or `*`-prefixed name, and does not contain special
|
||||||
|
/// characters (`:`, `>`, or `<`). If the submitted file name matches any of
|
||||||
|
/// these properties or none was submitted, the return value will be `None`.
|
||||||
|
pub fn name(&self) -> Option<&'r str> {
|
||||||
|
fn sanitize(file_name: &str) -> Option<&str> {
|
||||||
|
let file_name = std::path::Path::new(file_name)
|
||||||
|
.file_name()
|
||||||
|
.and_then(|n| n.to_str())
|
||||||
|
.map(|n| n.find('.').map(|i| n.split_at(i).0).unwrap_or(n))?;
|
||||||
|
|
||||||
|
if file_name.is_empty()
|
||||||
|
|| file_name.starts_with(|c| c == '.' || c == '*')
|
||||||
|
|| file_name.ends_with(|c| c == ':' || c == '>' || c == '<')
|
||||||
|
|| file_name.contains(|c| c == '/' || c == '\\')
|
||||||
|
{
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(file_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitize(self.raw_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw file name. The client can send arbitrary text here.
|
||||||
|
/// In almost every case, use name() instead, which returns a safe sanitized file name.
|
||||||
|
/// Only use this if the server is not publicly accessible and
|
||||||
|
/// you can trust the client to send correct data **and** you really need the orinal file name.
|
||||||
|
/// Don't rely on the file extension to figure out the type of data.
|
||||||
|
///
|
||||||
|
/// Note: This function requires the "trusted_client" feature to be enabled.
|
||||||
|
#[cfg(feature = "trusted_client")]
|
||||||
|
pub fn raw_name(&self) -> &'r str {
|
||||||
|
self.raw_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A multipart form field with an underlying data stream.
|
/// A multipart form field with an underlying data stream.
|
||||||
///
|
///
|
||||||
/// Rocket preprocesses all form fields into either [`ValueField`]s or
|
/// Rocket preprocesses all form fields into either [`ValueField`]s or
|
||||||
|
@ -25,16 +81,8 @@ pub struct ValueField<'r> {
|
||||||
pub struct DataField<'r, 'i> {
|
pub struct DataField<'r, 'i> {
|
||||||
/// The (decoded) name of the form field.
|
/// The (decoded) name of the form field.
|
||||||
pub name: NameView<'r>,
|
pub name: NameView<'r>,
|
||||||
/// The form field's sanitized file name.
|
/// The form fields's file name.
|
||||||
///
|
pub file_name: Option<FileName<'r>>,
|
||||||
/// ## Santization
|
|
||||||
///
|
|
||||||
/// A "sanitized" file name is a non-empty string, stripped of its file
|
|
||||||
/// exntesion, which does not contain any path delimiters (`/` or `\`), is
|
|
||||||
/// not a hidden (`.`) or `*`-prefixed name, and does not contain special
|
|
||||||
/// characters (`:`, `>`, or `<`). If the submitted file name matches any of
|
|
||||||
/// these properties or none was submitted, `file_name` will be `None`.
|
|
||||||
pub file_name: Option<&'r str>,
|
|
||||||
/// The form field's Content-Type, as submitted, which may or may not
|
/// The form field's Content-Type, as submitted, which may or may not
|
||||||
/// reflect on `data`.
|
/// reflect on `data`.
|
||||||
pub content_type: ContentType,
|
pub content_type: ContentType,
|
||||||
|
|
|
@ -182,7 +182,7 @@ impl<'r, 'i> MultipartParser<'r, 'i> {
|
||||||
content_type,
|
content_type,
|
||||||
request: self.request,
|
request: self.request,
|
||||||
name: NameView::new(name),
|
name: NameView::new(name),
|
||||||
file_name: file_name.and_then(sanitize),
|
file_name: file_name.and_then(|name| Some(FileName::new(name))),
|
||||||
data: Data::from(field),
|
data: Data::from(field),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -204,23 +204,6 @@ impl<'r, 'i> MultipartParser<'r, 'i> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize(file_name: &str) -> Option<&str> {
|
|
||||||
let file_name = std::path::Path::new(file_name)
|
|
||||||
.file_name()
|
|
||||||
.and_then(|n| n.to_str())
|
|
||||||
.map(|n| n.find('.').map(|i| n.split_at(i).0).unwrap_or(n))?;
|
|
||||||
|
|
||||||
if file_name.is_empty()
|
|
||||||
|| file_name.starts_with(|c| c == '.' || c == '*')
|
|
||||||
|| file_name.ends_with(|c| c == ':' || c == '>' || c == '<')
|
|
||||||
|| file_name.contains(|c| c == '/' || c == '\\')
|
|
||||||
{
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(file_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Buffer {
|
Buffer {
|
||||||
|
|
Loading…
Reference in New Issue