fif/src/mimedb.rs

103 lines
3.5 KiB
Rust
Raw Normal View History

#[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))]
2021-02-18 09:48:38 +00:00
use std::str::FromStr;
use mime_guess::Mime;
pub trait MimeDb {
fn init() -> Self;
fn get_type(&self, data: &[u8]) -> Option<Mime>;
}
#[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))]
pub struct InferDb {
2021-02-18 09:48:38 +00:00
db: infer::Infer,
}
#[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))]
fn open_document_check(buf: &[u8], kind: &str) -> bool {
let mime = format!("application/vnd.oasis.opendocument.{}", kind);
let mime = mime.as_bytes();
buf.len() > 38 + mime.len() && buf.starts_with(b"PK\x03\x04") && buf[38..mime.len() + 38] == mime[..]
}
#[cfg(any(all(not(target_os = "linux"), not(feature = "xdg-mime-backend")), all(target_os = "linux", feature = "infer-backend")))]
impl MimeDb for InferDb {
fn init() -> Self {
let mut info = infer::Infer::new();
// jpeg2000 support because why the stinch not
2021-02-18 09:48:38 +00:00
info.add("image/jpeg2000", ".jp2", |buf| {
2021-02-21 14:15:09 +00:00
buf.len() > 23 && buf[..23] == b"\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A\x6A\x70\x32\x20"[..]
});
info.add("application/vnd.oasis.opendocument.text", "odt", |buf| {
open_document_check(buf, "text")
});
info.add("application/vnd.oasis.opendocument.spreadsheet", "ods", |buf| {
open_document_check(buf, "spreadsheet")
});
info.add("application/vnd.oasis.opendocument.presentation", "odp", |buf| {
open_document_check(buf, "presentation")
});
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"""""
2021-02-21 14:09:53 +00:00
for c in buf {
match c {
// whitespace (according to https://www.w3.org/TR/xml/#NT-S)
b'\t' | b'\r' | b'\n' | b'\x20' => continue,
b'<' => break,
2021-02-21 14:15:09 +00:00
_ => 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:
2021-02-21 14:15:09 +00:00
let identifiers: Vec<&[u8]> = vec![b"svg", b"SVG", b"!DOCTYPE svg", b"!DOCTYPE SVG"];
// - 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'<')
2021-02-21 14:15:09 +00:00
.any(|buf| identifiers.iter().any(|id| buf.starts_with(id)))
2021-02-18 09:48:38 +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())
}
}
#[cfg(any(all(target_os = "linux", not(feature = "infer-backend")), all(not(target_os = "linux"), not(feature = "xdg-mime-backend"))))]
pub struct XdgDb {
2021-02-18 09:48:38 +00:00
db: xdg_mime::SharedMimeInfo,
}
#[cfg(any(all(target_os = "linux", not(feature = "infer-backend")), all(not(target_os = "linux"), not(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(),
}
}
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
}