use duplicate::duplicate;
use std::convert::TryFrom;
use std::fmt;
use std::error::Error;
///
/// This struct implements the bodyfile format generated by TSK 3.x
///
#[derive(Debug)]
pub struct Bodyfile3Line {
md5: String,
name: String,
inode: String,
mode_as_string: String,
uid: u64,
gid: u64,
size: u64,
atime: i64,
mtime: i64,
ctime: i64,
crtime: i64,
}
impl Bodyfile3Line {
/// create a new empty bodyfile line
///
/// # Example
/// ```
/// use bodyfile::Bodyfile3Line;
///
/// let bf = Bodyfile3Line::new();
/// assert_eq!(bf.get_md5(), "0");
/// assert_eq!(bf.get_name(), "");
/// assert_eq!(bf.get_inode(), "0");
/// assert_eq!(bf.get_mode(), "");
/// assert_eq!(bf.get_uid(), 0);
/// assert_eq!(bf.get_gid(), 0);
/// assert_eq!(bf.get_size(), 0);
/// assert_eq!(bf.get_atime(), -1);
/// assert_eq!(bf.get_mtime(), -1);
/// assert_eq!(bf.get_ctime(), -1);
/// assert_eq!(bf.get_crtime(), -1);
/// ```
pub fn new() -> Self {
Self {
md5: "0".to_owned(),
name: "".to_owned(),
inode: "0".to_owned(),
mode_as_string: "".to_owned(),
uid: 0,
gid: 0,
size: 0,
atime: -1,
mtime: -1,
ctime: -1,
crtime: -1,
}
}
/// create a new bodyfile line with concrete values
///
/// # Example
/// ```
/// use bodyfile::Bodyfile3Line;
///
/// let bf = Bodyfile3Line::from_values(
/// "4bad420da66571dac7f1ace995cc55c6".to_owned(),
/// "sample.txt".to_owned(),
/// "87915-128-1".to_owned(),
/// "r/rrwxrwxrwx".to_owned(),
/// 1003,
/// 500,
/// 126378,
/// 12341,
/// 12342,
/// 12343,
/// 12344);
/// assert_eq!(bf.get_md5(), "4bad420da66571dac7f1ace995cc55c6");
/// assert_eq!(bf.get_name(), "sample.txt");
/// assert_eq!(bf.get_inode(), "87915-128-1");
/// assert_eq!(bf.get_mode(), "r/rrwxrwxrwx");
/// assert_eq!(bf.get_uid(), 1003);
/// assert_eq!(bf.get_gid(), 500);
/// assert_eq!(bf.get_size(), 126378);
/// assert_eq!(bf.get_atime(), 12341);
/// assert_eq!(bf.get_mtime(), 12342);
/// assert_eq!(bf.get_ctime(), 12343);
/// assert_eq!(bf.get_crtime(), 12344);
pub fn from_values(
md5: String,
name: String,
inode: String,
mode_as_string: String,
uid: u64,
gid: u64,
size: u64,
atime: i64,
mtime: i64,
ctime: i64,
crtime: i64,
) -> Self {
Self {
md5,
name,
inode,
mode_as_string,
uid,
gid,
size,
atime,
mtime,
ctime,
crtime,
}
}
#[duplicate(
method_name attribute_name;
[with_md5] [md5];
[with_name] [name];
[with_inode] [inode];
[with_mode] [mode_as_string];
)]
pub fn method_name(mut self, attribute_name: &str) -> Self {
self.attribute_name = attribute_name.to_owned();
self
}
#[duplicate(
method_name attribute_name attribute_type;
[with_owned_md5] [md5] [String];
[with_owned_name] [name] [String];
[with_owned_inode] [inode] [String];
[with_owned_mode] [mode_as_string] [String];
[with_uid] [uid] [u64];
[with_gid] [gid] [u64];
[with_size] [size] [u64];
[with_atime] [atime] [i64];
[with_mtime] [mtime] [i64];
[with_ctime] [ctime] [i64];
[with_crtime] [crtime] [i64];
)]
pub fn method_name(mut self, attribute_name: attribute_type) -> Self {
self.attribute_name = attribute_name;
self
}
#[duplicate(
method_name attribute_name;
[get_md5] [md5];
[get_name] [name];
[get_inode] [inode];
[get_mode] [mode_as_string];
)]
pub fn method_name(&self) -> &str {
&self.attribute_name
}
#[duplicate(
method_name attribute_name attribute_type;
[get_uid] [uid] [u64];
[get_gid] [gid] [u64];
[get_size] [size] [u64];
[get_atime] [atime] [i64];
[get_mtime] [mtime] [i64];
[get_ctime] [ctime] [i64];
[get_crtime] [crtime] [i64];
)]
pub fn method_name(&self) -> attribute_type {
self.attribute_name
}
}
impl fmt::Display for Bodyfile3Line {
/// exports the line to the format parsable by, eg. `mactime`
///
/// # Example
/// ```
/// use bodyfile::Bodyfile3Line;
///
/// let bf = Bodyfile3Line::new()
/// .with_md5("4bad420da66571dac7f1ace995cc55c6")
/// .with_name("sample.txt")
/// .with_inode("87915-128-1")
/// .with_mode("r/rrwxrwxrwx")
/// .with_uid(1003)
/// .with_gid(500)
/// .with_size(126378)
/// .with_atime(12341)
/// .with_mtime(12342)
/// .with_ctime(12343)
/// .with_crtime(12344);
/// let line = bf.to_string();
/// assert_eq!(line, "4bad420da66571dac7f1ace995cc55c6|sample.txt|87915-128-1|r/rrwxrwxrwx|1003|500|126378|12341|12342|12343|12344")
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,
"{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}",
self.md5,
self.name,
self.inode,
self.mode_as_string,
self.uid,
self.gid,
self.size,
self.atime,
self.mtime,
self.ctime,
self.crtime)
}
}
#[derive(Debug)]
pub enum Bodyfile3ParserError {
/// indicates that number of columns is not valid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from(""), Err(Bodyfile3ParserError::WrongNumberOfColumns));
/// assert_matches!(Bodyfile3Line::try_from("|||||||||"), Err(Bodyfile3ParserError::WrongNumberOfColumns));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-1|-1|-1"), Ok(_));
/// assert_matches!(Bodyfile3Line::try_from("0|{\"activity_id\":null,\"channel_name\":\"Microsoft-Windows-PowerShell/Operational\",\"custom_data\":{\"EventData\":{\"ContextInfo\":\" Severity = Warning\\r\\n Host Name = ConsoleHost\\r\\n Host Version = 4.0\\r\\n Host ID = 5635c559-63c7-4bdc-8bb6-e2aa0448e7b9\\r\\n Host Application = powershell get-VMNetworkAdapter -ManagementOS | fl | out-file -encoding ASCII VMNetworkAdapterInstances.txt\\r\\n Engine Version = 4.0\\r\\n Runspace ID = d315d83a-8923-4530-9553-e63551c33cbc\\r\\n Pipeline ID = 1\\r\\n Command Name = \\r\\n Command Type = Script\\r\\n Script Name = \\r\\n Command Path = \\r\\n Sequence Number = 15\\r\\n User = TEST\\\\SYSTEM\\r\\n Shell ID = Microsoft.PowerShell\\r\\n\",\"Payload\":\"Error Message = Could not load file or assembly 'System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The media is write protected. (Exception from HRESULT: 0x80070013)\\r\\nFully Qualified Error ID = System.IO.FileLoadException\\r\\n\",\"UserData\":\"\"}},\"event_id\":4100,\"event_record_id\":2424468,\"provider_name\":\"Microsoft-Windows-PowerShell\"}|0||0|0|0|-1|1645178371|-1|-1"), Ok(_));
/// ```
WrongNumberOfColumns,
/// indicates that the uid is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||X|0|0|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalUid));
/// assert_matches!(Bodyfile3Line::try_from("0||0||-1|0|0|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalUid));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|0|-1|-1|-1|-1").unwrap();
/// assert_eq!(valid_bf.get_uid(), 1);
/// ```
IllegalUid,
/// indicates that the gid is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|X|0|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalGid));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|-2|0|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalGid));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|2|0|-1|-1|-1|-1").unwrap();
/// assert_eq!(valid_bf.get_gid(), 2);
/// ```
IllegalGid,
/// indicates that the size is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|X|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalSize));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|-4|-1|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalSize));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|4|-1|-1|-1|-1").unwrap();
/// assert_eq!(valid_bf.get_size(), 4);
/// ```
IllegalSize,
/// indicates that the atime is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|X|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalATime));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-5|-1|-1|-1"), Err(Bodyfile3ParserError::IllegalATime));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|0|5|-1|-1|-1").unwrap();
/// assert_eq!(valid_bf.get_atime(), 5);
/// ```
IllegalATime,
/// indicates that the mtime is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|X|-1|-1"), Err(Bodyfile3ParserError::IllegalMTime));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-5|-1|-1"), Err(Bodyfile3ParserError::IllegalMTime));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|0|-1|5|-1|-1").unwrap();
/// assert_eq!(valid_bf.get_mtime(), 5);
/// ```
IllegalMTime,
/// indicates that the ctime is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-1|X|-1"), Err(Bodyfile3ParserError::IllegalCTime));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-1|-5|-1"), Err(Bodyfile3ParserError::IllegalCTime));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|0|-1|-1|5|-1").unwrap();
/// assert_eq!(valid_bf.get_ctime(), 5);
/// ```
IllegalCTime,
/// indicates that the crtime is syntactically invalid
///
/// # Examples
/// ```
/// extern crate matches;
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// use matches::assert_matches;
///
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-1|-1|X"), Err(Bodyfile3ParserError::IllegalCRTime));
/// assert_matches!(Bodyfile3Line::try_from("0||0||0|0|0|-1|-1|-1|-5"), Err(Bodyfile3ParserError::IllegalCRTime));
/// let valid_bf = Bodyfile3Line::try_from("0||0||1|0|0|-1|-1|-1|5").unwrap();
/// assert_eq!(valid_bf.get_crtime(), 5);
/// ```
IllegalCRTime,
}
/// implements `Display` for this enum
///
/// # Example
/// ```
/// use bodyfile::Bodyfile3ParserError;
///
/// let myerror = Bodyfile3ParserError::IllegalCRTime;
/// assert_eq!(myerror.to_string(), "IllegalCRTime")
/// ```
impl fmt::Display for Bodyfile3ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Error for Bodyfile3ParserError {}
impl TryFrom<&str> for Bodyfile3Line {
type Error = Bodyfile3ParserError;
/// parses a bodyfile line
///
/// # Example
/// ```
/// use bodyfile::{Bodyfile3Line, Bodyfile3ParserError};
/// use std::convert::TryFrom;
/// let bf_line = Bodyfile3Line::try_from("0|ls -l |wc|1|2|3|4|5|6|7|8|9").unwrap();
/// assert_eq!(bf_line.get_md5(), "0");
/// assert_eq!(bf_line.get_name(), "ls -l |wc");
/// assert_eq!(bf_line.get_inode(), "1");
/// assert_eq!(bf_line.get_mode(), "2");
/// assert_eq!(bf_line.get_uid(), 3);
/// assert_eq!(bf_line.get_gid(), 4);
/// assert_eq!(bf_line.get_size(), 5);
/// assert_eq!(bf_line.get_atime(), 6);
/// assert_eq!(bf_line.get_mtime(), 7);
/// assert_eq!(bf_line.get_ctime(), 8);
/// assert_eq!(bf_line.get_crtime(), 9);
/// ```
fn try_from(line: &str) -> Result<Self, Self::Error> {
let parts: Vec<&str> = line.split('|').collect();
if parts.len() < 11 {
return Err(Self::Error::WrongNumberOfColumns);
}
let name_chunks = parts.len() - 10;
let md5 = parts[0];
let name = parts[1..name_chunks+1].join("|");
let inode = parts[2 + name_chunks - 1];
let mode = parts[3 + name_chunks - 1];
let uid = str::parse::<u64>(parts[4 + name_chunks - 1]).or(Err(Self::Error::IllegalUid))?;
let gid = str::parse::<u64>(parts[5 + name_chunks - 1]).or(Err(Self::Error::IllegalGid))?;
let size = str::parse::<u64>(parts[6 + name_chunks - 1]).or(Err(Self::Error::IllegalSize))?;
let atime = str::parse::<i64>(parts[7 + name_chunks - 1]).or(Err(Self::Error::IllegalATime))?;
if atime < -1 { return Err(Self::Error::IllegalATime); }
let mtime = str::parse::<i64>(parts[8 + name_chunks - 1]).or(Err(Self::Error::IllegalMTime))?;
if mtime < -1 { return Err(Self::Error::IllegalMTime); }
let ctime = str::parse::<i64>(parts[9 + name_chunks - 1]).or(Err(Self::Error::IllegalCTime))?;
if ctime < -1 { return Err(Self::Error::IllegalCTime); }
let crtime = str::parse::<i64>(parts[10 + name_chunks - 1]).or(Err(Self::Error::IllegalCRTime))?;
if crtime < -1 { return Err(Self::Error::IllegalCRTime); }
Ok(Self::from_values(
md5.to_owned(),
name.to_owned(),
inode.to_owned(),
mode.to_owned(), uid, gid, size, atime, mtime, ctime, crtime))
}
}