From 5ab432ae464fa5d12f8c572c75359092c2abd1d5 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Wed, 5 Oct 2022 13:16:22 -0700 Subject: [PATCH] =?UTF-8?q?Magic=20bytes=20=F0=9F=AA=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Brendan Allan --- .cspell/project_words.txt | 1 + core/src/lib.rs | 2 + core/src/object/kind.rs | 328 +++++++++++++----- .../src/components/explorer/Inspector.tsx | 2 +- .../src/components/layout/Sidebar.tsx | 10 +- .../src/components/layout/TopBar.tsx | 2 +- .../src/components/primitive/Shortcut.tsx | 4 +- 7 files changed, 255 insertions(+), 94 deletions(-) diff --git a/.cspell/project_words.txt b/.cspell/project_words.txt index bc01cab56..365c3ba6f 100644 --- a/.cspell/project_words.txt +++ b/.cspell/project_words.txt @@ -10,6 +10,7 @@ davidmytton deel elon encryptor +Flac haden haoyuan haris diff --git a/core/src/lib.rs b/core/src/lib.rs index 14778b2ae..a8c3264f2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -51,6 +51,8 @@ impl Node { fs::create_dir_all(&data_dir).await?; + // dbg!(get_object_kind_from_extension("png")); + tracing_subscriber::registry() .with( EnvFilter::from_default_env() diff --git a/core/src/object/kind.rs b/core/src/object/kind.rs index dc0e7fe68..41e47588c 100644 --- a/core/src/object/kind.rs +++ b/core/src/object/kind.rs @@ -1,3 +1,8 @@ +/// Object Kind +/// +/// https://www.garykessler.net/library/file_sigs.html +/// https://github.com/bojand/infer/ +/// use std::{ fmt::{Display, Formatter}, str::FromStr, @@ -8,6 +13,8 @@ use rspc::Type; use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::prisma::file_path; + #[derive(Debug, Serialize, Deserialize, Clone, Copy, Type, IntEnum)] #[repr(u8)] pub enum ObjectKind { @@ -47,19 +54,39 @@ pub enum ObjectKind { Album = 16, // Its like a folder, but appears like a stack of files, designed for burst photos / associated groups of files Collection = 17, + // You know, text init + Font = 18, } +/// Construct the extensions enum macro_rules! extension_enum { ( Extension { $( $variant:ident($type:ident), )* } ) => { - #[derive(Debug, Serialize, Deserialize)] + // construct enum + #[derive(Debug, Serialize, Deserialize, PartialEq)] pub enum Extension { $( $variant($type), )* } - // convert extension to object kind + impl Extension { + pub fn from_str(s: &str) -> Option { + let mut exts = [$( + $type::from_str(s).ok().map(Self::$variant) + ),*] + .into_iter() + .filter_map(|s| s) + .collect::>(); + + match exts { + _ if exts.len() == 0 => None, + _ if exts.len() == 1 => Some(ExtensionPossibility::Known(exts.swap_remove(0))), + _ => Some(ExtensionPossibility::Conflicts(exts)) + } + } + } + // convert Extension to ObjectKind impl From for ObjectKind { fn from(ext: Extension) -> Self { match ext { @@ -67,6 +94,7 @@ macro_rules! extension_enum { } } } + // impl Display for Extension { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -77,9 +105,67 @@ macro_rules! extension_enum { }; } +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn extension_from_str() { + // single extension match + assert_eq!( + Extension::from_str("jpg"), + Some(ExtensionPossibility::Known(Extension::Image( + ImageExtension::Jpg + ))) + ); + // with conflicts + assert_eq!( + Extension::from_str("ts"), + Some(ExtensionPossibility::Conflicts(vec![ + Extension::Video(VideoExtension::Ts), + Extension::Text(TextExtension::Ts) + ])) + ); + // invalid case + assert_eq!(Extension::from_str("jeff"), None); + } +} + +impl file_path::Data { + // fn extension(&self, magic_bytes: Option<&[u8]>) -> OptionV { + // let ext = match self.extension { + // Some(ext) => ext.as_str(), + // None => return Ok(Extension::Unknown("".to_string())), + // }; + + // // if let Ok(ex) = VideoExtension::from_str(ext) { + // // // .ts files can be video or text + // // match ex { + // // VideoExtension::Ts => { + // // // double check if it is a video= + // // } + // // _ => Extension::Video(ex), + // // } + // // // + // // } else if let Ok(ex) = ImageExtension::from_str(ext) { + // // return Extension::Image(ex); + // // // + // // } else if let Ok(ex) = AudioExtension::from_str(ext) { + // // return Extension::Audio(ex); + // // // + // // } else { + // // return Extension::Unknown(ext); + // // } + // } + + // fn object_kind(&self, magic_bytes: Option<&[u8]>) -> ObjectKind { + // let extension = self.extension(magic_bytes); + // extension.into() + // } +} + extension_enum! { Extension { - Unknown(String), Video(VideoExtension), Image(ImageExtension), Audio(AudioExtension), @@ -88,19 +174,50 @@ extension_enum! { Text(TextExtension), Encrypted(EncryptedExtension), Key(KeyExtension), + Font(FontExtension), } } +#[derive(Debug, PartialEq)] +pub enum ExtensionPossibility { + Known(Extension), + Conflicts(Vec), +} + +pub trait MagicBytes: Sized { + fn from_magic_bytes_buf(buf: &[u8]) -> Option; + fn magic_bytes_len(&self) -> usize; + fn magic_bytes_offset(&self) -> usize; +} + +macro_rules! magic_byte_value { + (_) => { + 0 as u8 + }; + ($val:literal) => {{ + $val as u8 + }}; +} + +macro_rules! magic_byte_offset { + () => { + 0 + }; + ($val:literal) => { + $val + }; +} + /// Define a public enum with static array of all possible variants /// including implementations to convert to/from string -macro_rules! enum_with_variants { +macro_rules! extension_category_enum { ( $(#[$enum_attr:meta])* $enum_name:ident $static_array_name:ident { - $($(#[$variant_attr:meta])* $variant:ident, )* + $($(#[$variant_attr:meta])* $variant:ident $(= [$($magic_bytes:tt),*] $(+ $offset:literal)?)? ,)* } ) => { - #[derive(Debug, Serialize, Deserialize, Clone, Copy)] + #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)] #[serde(rename_all = "snake_case")] $(#[$enum_attr])* // construct enum @@ -111,6 +228,7 @@ macro_rules! enum_with_variants { pub static $static_array_name: &[$enum_name] = &[ $( $enum_name::$variant, )* ]; + extension_category_enum!(@magic_bytes; $enum_name ( $($(#[$variant_attr])* $variant $(= [$($magic_bytes),*] $(+ $offset)?)? ),* )); // convert from string impl FromStr for $enum_name { type Err = serde_json::Error; @@ -124,115 +242,141 @@ macro_rules! enum_with_variants { write!(f, "{}", serde_json::to_string(self).unwrap()) // SAFETY: This is safe } } - } + }; + (@magic_bytes; $enum_name:ident ($($(#[$variant_attr:meta])* $variant:ident = [$($magic_bytes:tt),*] $(+ $offset:literal)? ),*)) => { + impl MagicBytes for $enum_name { + fn from_magic_bytes_buf(buf: &[u8]) -> Option { + match buf { + $( &[$($magic_bytes),*] => Some($enum_name::$variant),)* + _ => None + } + } + fn magic_bytes_len(&self) -> usize { + match self { + $( $enum_name::$variant => (&[$(magic_byte_value!($magic_bytes)),*] as &[u8]).len() ),*, + } + } + + fn magic_bytes_offset(&self) -> usize { + match self { + $( $enum_name::$variant => magic_byte_offset!($($offset)?)),* + } + } + } + }; + (@magic_bytes; $enum_name:ident ($($(#[$variant_attr:meta])* $variant:ident),*)) => {}; } // video extensions -enum_with_variants! { +extension_category_enum! { VideoExtension ALL_VIDEO_EXTENSIONS { - Avi, - Asf, - Mpeg, - Mts, - Mpg, - Mpe, - Qt, - Mov, - Swf, - Mjpeg, - Ts, - Mxf, - M2v, - M2ts, - Flv, - Wm, + Avi = [0x52, 0x49, 0x46, 0x46, _, _, _, _, 0x41, 0x56, 0x49, 0x20], + Mpeg = [0x47], + Mts = [0x47, 0x41, 0x39, 0x34], + Mpg = [], + Mpe = [], + Qt = [0x71, 0x74, 0x20, 0x20], + Mov = [0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20] + 4, + Swf = [0x5A, 0x57, 0x53], + Mjpeg = [], + Ts = [0x47], + Mxf = [0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02], + M2v = [0x00, 0x00, 0x01, 0xBA], + M2ts = [], + Flv = [0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x56, 0x20] + 4, + Wm = [], #[serde(rename = "3gp")] - _3gp, - M4v, - Wmv, - Mp4, - Webm, + _3gp = [], + M4v = [0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32] + 4, + Wmv = [0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C], + Asf = [0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C], + Wma = [0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C], + Mp4 = [], + Webm = [0x1A, 0x45, 0xDF, 0xA3], + Mkv = [0x1A, 0x45, 0xDF, 0xA3], } } // image extensions -enum_with_variants! { +extension_category_enum! { ImageExtension _ALL_IMAGE_EXTENSIONS { - Jpg, - Jpeg, - Png, - Gif, - Bmp, - Tiff, - Webp, - Svg, - Ico, - Heic, + Jpg = [0xFF, 0xD8], + Jpeg = [0xFF, 0xD8], + Png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], + Gif = [0x47, 0x49, 0x46, 0x38, _, 0x61], + Bmp = [0x42, 0x4D], + Tiff = [0x49, 0x49, 0x2A, 0x00], + Webp = [0x52, 0x49, 0x46, 0x46, _, _, _, _, 0x57, 0x45, 0x42, 0x50], + Svg = [0x3C, 0x73, 0x76, 0x67], + Ico = [0x00, 0x00, 0x01, 0x00], + Heic = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63], } } // audio extensions -enum_with_variants! { +extension_category_enum! { AudioExtension _ALL_AUDIO_EXTENSIONS { - Mp3, - M4a, - Wav, - Aiff, - Aif, - Flac, - Ogg, - Opus, + Mp3 = [0x49, 0x44, 0x33], + M4a = [0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41, 0x20] + 4, + Wav = [0x52, 0x49, 0x46, 0x46, _, _, _, _, 0x57, 0x41, 0x56, 0x45], + Aiff = [0x46, 0x4F, 0x52, 0x4D, _, _, _, _, 0x41, 0x49, 0x46, 0x46], + Aif = [0x46, 0x4F, 0x52, 0x4D, _, _, _, _, 0x41, 0x49, 0x46, 0x46], + Flac = [0x66, 0x4C, 0x61, 0x43], + Ogg = [0x4F, 0x67, 0x67, 0x53], + Opus = [0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64], } } // archive extensions -enum_with_variants! { +extension_category_enum! { ArchiveExtension _ALL_ARCHIVE_EXTENSIONS { - Zip, - Rar, - Tar, - Gz, - Bz2, - SevenZip, + Zip = [0x50, 0x4B, 0x03, 0x04], + Rar = [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00], + Tar = [0x75, 0x73, 0x74, 0x61, 0x72], + Gz = [0x1F, 0x8B, 0x08], + Bz2 = [0x42, 0x5A, 0x68], + #[serde(rename = "7z")] + _7z = [0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C], + Xz = [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00], } } // executable extensions -enum_with_variants! { +extension_category_enum! { ExecutableExtension _ALL_EXECUTABLE_EXTENSIONS { - Exe, - App, - Apk, - Deb, - Dmg, - Pkg, - Rpm, - Msi, + Exe = [], + App = [], + Apk = [0x50, 0x4B, 0x03, 0x04], + Deb = [], + Dmg = [], + Pkg = [], + Rpm = [], + Msi = [], } } // document extensions -enum_with_variants! { +extension_category_enum! { DocumentExtension _ALL_DOCUMENT_EXTENSIONS { - Pdf, - Key, - Pages, - Numbers, - Doc, - Docx, - Xls, - Xlsx, - Ppt, - Pptx, - Odt, - Ods, - Odp, - Ics, + Pdf = [0x25, 0x50, 0x44, 0x46, 0x2D], + Key = [0x50, 0x4B, 0x03, 0x04], + Pages = [0x50, 0x4B, 0x03, 0x04], + Numbers = [0x50, 0x4B, 0x03, 0x04], + Doc = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1], + Docx = [0x50, 0x4B, 0x03, 0x04], + Xls = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1], + Xlsx = [0x50, 0x4B, 0x03, 0x04], + Ppt = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1], + Pptx = [0x50, 0x4B, 0x03, 0x04], + Odt = [0x50, 0x4B, 0x03, 0x04], + Ods = [0x50, 0x4B, 0x03, 0x04], + Odp = [0x50, 0x4B, 0x03, 0x04], + Ics = [0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A, 0x56, 0x43, 0x41, 0x52, 0x44], } } // text file extensions -enum_with_variants! { +extension_category_enum! { TextExtension _ALL_TEXT_EXTENSIONS { Txt, Rtf, @@ -243,19 +387,23 @@ enum_with_variants! { Yaml, Xml, Md, + Ts, } } // encrypted file extensions -enum_with_variants! { +extension_category_enum! { EncryptedExtension _ALL_ENCRYPTED_EXTENSIONS { - Bit, // Spacedrive encrypted file - Box, // Spacedrive container - Block,// Spacedrive block storage, + Bit, + Box, + Block, } } +// Spacedrive encrypted file +// Spacedrive container +// Spacedrive block storage, // key extensions -enum_with_variants! { +extension_category_enum! { KeyExtension _ALL_KEY_EXTENSIONS { Pgp, Pub, @@ -265,3 +413,11 @@ enum_with_variants! { Keychain, } } + +// font extensions +extension_category_enum! { + FontExtension _ALL_FONT_EXTENSIONS { + Ttf = [0x00, 0x01, 0x00, 0x00, 0x00], + Otf = [0x4F, 0x54, 0x54, 0x4F, 0x00], + } +} diff --git a/packages/interface/src/components/explorer/Inspector.tsx b/packages/interface/src/components/explorer/Inspector.tsx index 37a44eab4..8db5116ff 100644 --- a/packages/interface/src/components/explorer/Inspector.tsx +++ b/packages/interface/src/components/explorer/Inspector.tsx @@ -45,7 +45,7 @@ export const Inspector = (props: Props) => {
{!!props.data && ( <> -
+
- - + {/* */} +
{location.name} diff --git a/packages/interface/src/components/layout/TopBar.tsx b/packages/interface/src/components/layout/TopBar.tsx index f4f3fb03e..a07681bdd 100644 --- a/packages/interface/src/components/layout/TopBar.tsx +++ b/packages/interface/src/components/layout/TopBar.tsx @@ -102,7 +102,7 @@ const SearchBar = forwardRef((props, forwardedRe
diff --git a/packages/interface/src/components/primitive/Shortcut.tsx b/packages/interface/src/components/primitive/Shortcut.tsx index 31a402c75..4a3295d88 100644 --- a/packages/interface/src/components/primitive/Shortcut.tsx +++ b/packages/interface/src/components/primitive/Shortcut.tsx @@ -12,10 +12,10 @@ export const Shortcut: React.FC = (props) => { return (