2021-02-14 19:20:04 +00:00
|
|
|
#[cfg(feature = "infer-backend")]
|
2021-02-18 09:48:38 +00:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
use mime_guess::Mime;
|
2021-02-14 18:58:57 +00:00
|
|
|
|
|
|
|
pub trait MimeDb {
|
|
|
|
fn init() -> Self;
|
|
|
|
fn get_type(&self, data: &[u8]) -> Option<Mime>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "infer-backend")]
|
|
|
|
pub struct InferDb {
|
2021-02-18 09:48:38 +00:00
|
|
|
db: infer::Infer,
|
2021-02-14 18:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "infer-backend")]
|
|
|
|
impl MimeDb for InferDb {
|
|
|
|
fn init() -> Self {
|
|
|
|
let mut info = infer::Infer::new();
|
2021-02-21 11:30:58 +00:00
|
|
|
|
|
|
|
// jpeg2000 support because why the stinch not
|
2021-02-18 09:48:38 +00:00
|
|
|
info.add("image/jpeg2000", ".jp2", |buf| {
|
|
|
|
buf.len() > 23
|
2021-02-21 11:30:58 +00:00
|
|
|
&& buf[..23] == b"\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x6A\x70\x32\x20"[..]
|
|
|
|
});
|
|
|
|
|
|
|
|
info.add("image/svg+xml", "svg", |buf| {
|
|
|
|
// before doing the moderately expensive SVG check, we should make sure that the input is actually SGML-ish
|
|
|
|
// by "SGML-ish", i mean starts with anywhere from zero to ∞-1 whitespace characters, and then a less than sign,
|
|
|
|
// and then there's some other stuff we don't care about right now
|
|
|
|
|
|
|
|
// so, here comes our fancy pants """""SGML-ish validator"""""
|
|
|
|
for i in 0..buf.len() {
|
|
|
|
match buf[i] {
|
|
|
|
// whitespace (according to https://www.w3.org/TR/xml/#NT-S)
|
|
|
|
b'\t' | b'\r' | b'\n' | b'\x20' => continue,
|
|
|
|
b'<' => break,
|
|
|
|
_ => return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally, to check whether or not the file is an SVG:
|
|
|
|
// - split the buffer up into chunks separated by the less than sign
|
|
|
|
// - check to see if this chunk starts with any of these identifiers:
|
|
|
|
let identifiers: Vec<&[u8]> = vec![
|
|
|
|
"svg".as_bytes(),
|
|
|
|
"SVG".as_bytes(),
|
|
|
|
"!DOCTYPE svg".as_bytes(),
|
|
|
|
"!DOCTYPE SVG".as_bytes()
|
|
|
|
];
|
|
|
|
// - if it does, the nested `any` will short circuit and immediately return true, causing the parent `any` to do
|
|
|
|
// the same
|
|
|
|
// - and finally, if none of the chunks match, we'll return false
|
|
|
|
|
|
|
|
// TODO: this is kind of messy, i'd like to clean it up somehow :(
|
|
|
|
buf
|
|
|
|
.split(|c| *c == b'<')
|
|
|
|
.any(|buf| {
|
|
|
|
identifiers
|
|
|
|
.iter()
|
|
|
|
.any(|id| buf.starts_with(id))
|
|
|
|
})
|
2021-02-18 09:48:38 +00:00
|
|
|
});
|
2021-02-14 18:58:57 +00:00
|
|
|
|
|
|
|
// unmut
|
|
|
|
let info = info;
|
|
|
|
|
|
|
|
Self { db: info }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_type(&self, data: &[u8]) -> Option<Mime> {
|
2021-02-18 09:48:38 +00:00
|
|
|
self.db.get(data).map(|f| Mime::from_str(f.mime_type()).unwrap())
|
2021-02-14 18:58:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "xdg-mime-backend")]
|
|
|
|
pub struct XdgDb {
|
2021-02-18 09:48:38 +00:00
|
|
|
db: xdg_mime::SharedMimeInfo,
|
2021-02-14 18:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "xdg-mime-backend")]
|
|
|
|
impl MimeDb for XdgDb {
|
|
|
|
fn init() -> Self {
|
|
|
|
Self {
|
2021-02-18 09:48:38 +00:00
|
|
|
db: xdg_mime::SharedMimeInfo::new(),
|
2021-02-14 18:58:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_type(&self, data: &[u8]) -> Option<Mime> {
|
|
|
|
self.db.get_mime_type_for_data(&data).map(|m| m.0)
|
|
|
|
}
|
2021-02-18 09:48:38 +00:00
|
|
|
}
|