feat(paste): support overriding MIME types

This commit is contained in:
orhun 2021-08-09 22:28:33 +03:00
parent 0296bc0594
commit 03348cb38f
No known key found for this signature in database
GPG Key ID: F83424824B3E4B90
7 changed files with 119 additions and 0 deletions

13
Cargo.lock generated
View File

@ -1520,9 +1520,12 @@ dependencies = [
"futures-util",
"infer",
"log",
"mime",
"petname",
"rand 0.8.4",
"regex",
"serde",
"serde_regex",
"url",
]
@ -1602,6 +1605,16 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_regex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
dependencies = [
"regex",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.0"

View File

@ -24,6 +24,9 @@ petname = "1.1.0"
rand = "0.8.4"
dotenv = "0.15.0"
url = "2.2.2"
mime = "0.3.16"
regex = "1.5.4"
serde_regex = "1.1.0"
[dependencies.config]
version = "0.11.0"

View File

@ -8,3 +8,12 @@ upload_path="./upload"
random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
#random_url = { enabled = true, type = "alphanumeric", length = 8 }
default_extension = "txt"
mime_override = [
{ mime = "image/jpeg", regex = "^.*\\.jpg$" },
{ mime = "image/png", regex = "^.*\\.png$" },
{ mime = "image/svg+xml", regex = "^.*\\.svg$" },
{ mime = "video/webm", regex = "^.*\\.webm$" },
{ mime = "video/x-matroska", regex = "^.*\\.mkv$" },
{ mime = "application/octet-stream", regex = "^.*\\.bin$" },
{ mime = "text/plain", regex = "^.*\\.(log|txt|diff)$" },
]

View File

@ -1,3 +1,4 @@
use crate::mime::MimeMatcher;
use crate::random::RandomURLConfig;
use byte_unit::Byte;
use config::{self, ConfigError};
@ -32,6 +33,8 @@ pub struct PasteConfig {
pub random_url: RandomURLConfig,
/// Default file extension.
pub default_extension: String,
/// Media type override options.
pub mime_override: Vec<MimeMatcher>,
}
impl Config {

View File

@ -18,3 +18,6 @@ pub mod auth;
/// Storage handler.
pub mod paste;
/// Media type handler.
pub mod mime;

83
src/mime.rs Normal file
View File

@ -0,0 +1,83 @@
use actix_files::file_extension_to_mime;
use mime::{FromStrError, Mime};
use regex::Regex;
use std::path::PathBuf;
use std::str::FromStr;
/// Matcher for MIME types.
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct MimeMatcher {
/// MIME type to set for the matched file name.
pub mime: String,
/// Regex for matching the file name.
#[serde(with = "serde_regex")]
pub regex: Option<Regex>,
}
/// Returns the appropriate media type using an array of
/// [`MIME matcher`]s and the file name.
///
/// [`MIME matcher`]: MimeMatcher
pub fn get_mime_type(
mime_matchers: &[MimeMatcher],
file_name: String,
) -> Result<Mime, FromStrError> {
let path = PathBuf::from(&file_name);
let mut mime_type = file_extension_to_mime(
path.extension()
.map(|v| v.to_str())
.flatten()
.unwrap_or_default(),
);
for matcher in mime_matchers {
if matcher
.regex
.as_ref()
.map(|r| r.is_match(&file_name))
.unwrap_or(false)
{
mime_type = Mime::from_str(&matcher.mime)?;
break;
}
}
Ok(mime_type)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_mime_type() {
assert_eq!(
mime::TEXT_PLAIN,
get_mime_type(
&[MimeMatcher {
mime: String::from("text/plain"),
regex: Regex::new("^.*\\.test$").ok(),
}],
String::from("mime.test")
)
.unwrap()
);
assert_eq!(
mime::IMAGE_PNG,
get_mime_type(
&[MimeMatcher {
mime: String::from("image/png"),
regex: Regex::new("^.*\\.PNG$").ok(),
}],
String::from("image.PNG")
)
.unwrap()
);
assert_eq!(
mime::APPLICATION_PDF,
get_mime_type(&[], String::from("book.pdf")).unwrap()
);
assert_eq!(
mime::APPLICATION_OCTET_STREAM,
get_mime_type(&[], String::from("x.unknown")).unwrap()
);
}
}

View File

@ -1,6 +1,7 @@
use crate::auth;
use crate::config::Config;
use crate::header::ContentDisposition;
use crate::mime;
use crate::paste::{Paste, PasteType};
use actix_files::NamedFile;
use actix_multipart::Multipart;
@ -38,6 +39,10 @@ async fn serve(
match paste_type {
PasteType::File => Ok(NamedFile::open(&path)?
.disable_content_disposition()
.set_content_type(
mime::get_mime_type(&config.paste.mime_override, file.to_string())
.map_err(error::ErrorInternalServerError)?,
)
.prefer_utf8(true)
.into_response(&request)?),
PasteType::Url => Ok(HttpResponse::Found()