diff --git a/src/meta_plugin.rs b/src/meta_plugin.rs index 3e294cb..0457e02 100644 --- a/src/meta_plugin.rs +++ b/src/meta_plugin.rs @@ -11,6 +11,7 @@ pub mod system; use crate::meta_plugin::program::MetaPluginProgram; use crate::meta_plugin::digest::{DigestSha256MetaPlugin, ReadTimeMetaPlugin, ReadRateMetaPlugin}; use crate::meta_plugin::system::{CwdMetaPlugin, BinaryMetaPlugin, UidMetaPlugin, UserMetaPlugin, GidMetaPlugin, GroupMetaPlugin, ShellMetaPlugin, ShellPidMetaPlugin, KeepPidMetaPlugin, HostnameMetaPlugin, FullHostnameMetaPlugin}; +use crate::meta_plugin::magic::MagicMetaPlugin; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString)] #[strum(ascii_case_insensitive)] @@ -18,6 +19,9 @@ pub enum MetaPluginType { FileMagic, FileMime, FileEncoding, + MagicFileType, + MagicMimeType, + MagicMimeEncoding, LineCount, WordCount, Cwd, @@ -83,6 +87,9 @@ pub fn get_meta_plugin(meta_plugin_type: MetaPluginType) -> Box MetaPluginType::FileMagic => Box::new(MetaPluginProgram::new("file", vec!["-bE", "-"], "file_magic".to_string(), true)), MetaPluginType::FileMime => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-type", "-"], "file_mime".to_string(), true)), MetaPluginType::FileEncoding => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-encoding", "-"], "file_encoding".to_string(), true)), + MetaPluginType::MagicFileType => Box::new(MagicMetaPlugin::new_file_type()), + MetaPluginType::MagicMimeType => Box::new(MagicMetaPlugin::new_mime_type()), + MetaPluginType::MagicMimeEncoding => Box::new(MagicMetaPlugin::new_mime_encoding()), MetaPluginType::LineCount => Box::new(MetaPluginProgram::new("wc", vec!["-l"], "line_count".to_string(), true)), MetaPluginType::WordCount => Box::new(MetaPluginProgram::new("wc", vec!["-w"], "word_count".to_string(), true)), MetaPluginType::Cwd => Box::new(CwdMetaPlugin::new()), diff --git a/src/meta_plugin/magic.rs b/src/meta_plugin/magic.rs index e69de29..198ee27 100644 --- a/src/meta_plugin/magic.rs +++ b/src/meta_plugin/magic.rs @@ -0,0 +1,97 @@ +use anyhow::Result; +use magic::{Cookie, CookieFlags}; +use std::io; +use std::io::Write; + +use crate::meta_plugin::MetaPlugin; + +#[derive(Debug, Clone)] +pub struct MagicMetaPlugin { + buffer: Vec, + meta_name: String, + cookie_flags: CookieFlags, +} + +impl MagicMetaPlugin { + pub fn new_file_type() -> MagicMetaPlugin { + MagicMetaPlugin { + buffer: Vec::new(), + meta_name: "magic_file_type".to_string(), + cookie_flags: CookieFlags::NONE, + } + } + + pub fn new_mime_type() -> MagicMetaPlugin { + MagicMetaPlugin { + buffer: Vec::new(), + meta_name: "magic_mime_type".to_string(), + cookie_flags: CookieFlags::MIME_TYPE, + } + } + + pub fn new_mime_encoding() -> MagicMetaPlugin { + MagicMetaPlugin { + buffer: Vec::new(), + meta_name: "magic_mime_encoding".to_string(), + cookie_flags: CookieFlags::MIME_ENCODING, + } + } +} + +impl MetaPlugin for MagicMetaPlugin { + fn is_internal(&self) -> bool { + true + } + + fn create(&self) -> Result> { + // For meta plugins, we don't actually create a writer since we're buffering data internally + Ok(Box::new(DummyWriter)) + } + + fn finalize(&mut self) -> io::Result { + let cookie = Cookie::open(self.cookie_flags) + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to open magic cookie: {}", e)))?; + + cookie.load(&Default::default()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to load magic database: {}", e)))?; + + let result = cookie.buffer(&self.buffer) + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to analyze buffer: {}", e)))?; + + // Clean up the result - remove extra whitespace and take first part if needed + let trimmed = result.trim(); + + // For some magic results, we might want just the first part before semicolon or comma + let cleaned = if trimmed.contains(';') { + trimmed.split(';').next().unwrap_or(trimmed).trim() + } else if trimmed.contains(',') && self.meta_name.contains("mime") { + trimmed.split(',').next().unwrap_or(trimmed).trim() + } else { + trimmed + }; + + Ok(cleaned.to_string()) + } + + fn update(&mut self, data: &[u8]) { + self.buffer.extend_from_slice(data); + } + + fn meta_name(&mut self) -> String { + self.meta_name.clone() + } +} + +// Dummy writer that implements Write but doesn't do anything +// This is needed to satisfy the MetaPlugin trait requirements +struct DummyWriter; + +impl Write for DummyWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +}